randomfox: (Default)
[personal profile] randomfox
This is an example of a strange attractor, a formula that generates each subsequent point from the previous one and produces intriguing and surprising patterns when plotted on screen. Different values of P, Q, and R will produce different patterns.


screenshot

// hopalong - Produces graphics using the hopalong attractor.
//
// Author: Po Shan Cheah
// Last updated: August 22, 2003
// 
// Compile with: csc /doc:hopalong.xml hopalong.cs

namespace HopAlong {

using System;
using System.Threading;
using System.Drawing;
using System.Windows.Forms;

/// <summary>Contains code that runs the generator function. Also manages
/// the thread and timer for the generator and the display
/// refresh.</summary>
class HopAlongGenerator {

    /// <summary>Change brush color every this number of
    /// iterations.</summary>
    const int ColorChangeIterations = 500;

    /// <summary>Number of milliseconds between display updates.</summary>
    const int RefreshInterval = 50;

    double p;
    double q;
    double r;

    int canvasWidth;
    int canvasHeight;

    // The offscreen buffer.
    Bitmap image;

    public HopAlongGenerator(double p, double q, double r,
			     int canvasWidth, int canvasHeight) {
	this.p = p;
	this.q = q;
	this.r = r;

	// The width and height of the drawing area is saved just once,
	// when the Go button is clicked on. Any resizing after that won't
	// change the drawing area until the generator is restarted.
	this.canvasWidth = canvasWidth;
	this.canvasHeight = canvasHeight;
    }    

    public void Run() {
	double multiplier = 10 / Math.Sqrt(2);

	// Offscreen buffer.
	image = new Bitmap(canvasWidth, canvasHeight);
	Graphics graphics = Graphics.FromImage(image);

	int xmid = canvasWidth / 2;
	int ymid = canvasHeight / 2;

	graphics.Clear(Color.Black);

	int count = 0;
	double x = 0;
	double y = 0;

	SolidBrush brush = new SolidBrush(Color.White);
	Random rand = new Random();

	while (true) {
	    // Change the brush color every ColorChangeIterations iterations.
	    if (count % ColorChangeIterations == 0) {
		brush.Dispose();
		brush = new SolidBrush(Color.FromArgb(rand.Next(128, 256),
						      rand.Next(128, 256),
						      rand.Next(128, 256)));
	    }

	    int sign = x < 0 ? -1 : 1;
	    double x1 = y - sign * Math.Sqrt(Math.Abs(q * x - r));
	    y = p - x;
	    x = x1;

	    // Plot a point.
	    graphics.FillRectangle(brush,
				   (int) (Math.Round((x + y) * 
						     multiplier) + xmid), 
				   (int) (Math.Round((y - x) * 
						     multiplier) + ymid), 
				   1, 1);

	    ++count;
	}
    } // Run

    /// <value>The generated image.</value>
    public Bitmap Image {
	get { return image; }
    }
    
    Thread runThread;
    System.Windows.Forms.Timer refreshTimer;

    /// <summary>Start the generator thread and display refresh
    /// timer.</summary>
    /// <param name="refreshFunc">Function that will be called every
    /// RefreshInterval milliseconds.</param>
    public void Start(EventHandler refreshFunc) {
	runThread = new Thread(new ThreadStart(Run));
	runThread.Start();
	runThread.IsBackground = true;

	if (refreshTimer == null) {
	    refreshTimer = new System.Windows.Forms.Timer();
	    refreshTimer.Tick += refreshFunc;
	    refreshTimer.Interval = RefreshInterval;
	    refreshTimer.Start();
	}
    } // Start

    /// <summary>Stop the generator thread and display refresh
    /// timer.</summary>
    public void Stop() {
	if (runThread != null) {
	    runThread.Abort();
	    runThread = null;
	}

	if (refreshTimer != null) {
	    refreshTimer.Stop();
	    refreshTimer.Dispose();
	    refreshTimer = null;
	}
    } // Stop
} // class HopAlongGenerator

class HopAlong : Form {

    const int TopMargin = 30;

    TextBox pfield;
    TextBox qfield;
    TextBox rfield;

    SolidBrush blackBrush = new SolidBrush(Color.Black);

    HopAlongGenerator hopalong;

    /// <summary>Paint event handler.</summary>
    protected override void OnPaint(PaintEventArgs e) {
	e.Graphics.FillRectangle(blackBrush, 0, TopMargin, 
				 ClientRectangle.Width, 
				 ClientRectangle.Height - TopMargin);
	if (hopalong != null && hopalong.Image != null)
	    e.Graphics.DrawImage(hopalong.Image, 0, TopMargin);
    }

    /// <summary>Timer event handler. Will be called periodically to redraw
    /// the image.</summary>
    void Tick(Object o, EventArgs e) {
	Invalidate();
	// Invalidate() by itself does not trigger a repaint.
	Update();
    }

    /// <summary>Event handler for the Go button.</summary>
    void go_Click(object o, EventArgs e) {
	double p;
	double q;
	double r;

	try {
	    p = double.Parse(pfield.Text);
	    q = double.Parse(qfield.Text);
	    r = double.Parse(rfield.Text);
	}
	catch (FormatException) {
	    MessageBox.Show("Invalid numeric value entered.");
	    return;
	}

	// If there is a generator running, we have to stop it before
	// starting another one.
	if (hopalong != null)
	    hopalong.Stop();

	hopalong = new HopAlongGenerator(p, q, r,
					 ClientRectangle.Width,
					 ClientRectangle.Height - TopMargin);
	hopalong.Start(new EventHandler(Tick));
    } // go_Click

    /// <summary>Event handler for the Stop button.</summary>
    void stop_Click(object o, EventArgs e) {
	hopalong.Stop();
    } // stop_Click

    /// <summary>Event handler for the Random button.</summary>
    void random_Click(object o, EventArgs e) {
	Random rand = new Random();
	pfield.Text = (rand.NextDouble() + 0.3).ToString("#.####");
	qfield.Text = (rand.NextDouble() + 0.3).ToString("#.####");
	rfield.Text = (rand.NextDouble() + 0.3).ToString("#.####");
    } // random_Click
    
    HopAlong() {
	Text = "HopAlong";
	Name = "HopAlong";

	// Activate double-buffering.
	SetStyle(ControlStyles.UserPaint, true);
	SetStyle(ControlStyles.AllPaintingInWmPaint, true);
	SetStyle(ControlStyles.DoubleBuffer, true);

	ClientSize = new Size(700, 600);

	Label l1 = new Label();
	l1.Text = "Try values between 0 and 2. P:";
	l1.TextAlign = ContentAlignment.MiddleRight;
	l1.Location = new Point(0, 0);
	l1.Size = new Size(160, 25);

	Controls.Add(l1);

	pfield = new TextBox();
	pfield.Text = "0.5";
	pfield.MaxLength = 8;
	pfield.Location = new Point(160, 3);
	pfield.Size = new Size(75, 25);

	Controls.Add(pfield);

	Label l2 = new Label();
	l2.Text = "Q:";
	l2.TextAlign = ContentAlignment.MiddleRight;
	l2.Location = new Point(235, 0);
	l2.Size = new Size(30, 25);

	Controls.Add(l2);

	qfield = new TextBox();
	qfield.Text = "0.5";
	qfield.MaxLength = 8;
	qfield.Location = new Point(265, 3);
	qfield.Size = new Size(75, 25);

	Controls.Add(qfield);

	Label l3 = new Label();
	l3.Text = "R:";
	l3.TextAlign = ContentAlignment.MiddleRight;
	l3.Location = new Point(340, 0);
	l3.Size = new Size(30, 25);

	Controls.Add(l3);

	rfield = new TextBox();
	rfield.Text = "0.5";
	rfield.MaxLength = 8;
	rfield.Location = new Point(370, 3);
	rfield.Size = new Size(75, 25);

	Controls.Add(rfield);

	Button goButton = new Button();
	goButton.Text = "Go";
	goButton.Location = new Point(455, 3);
	goButton.Size = new Size(50, 20);
	goButton.Click += new EventHandler(go_Click);

	Controls.Add(goButton);
	AcceptButton = goButton;

	Button randomButton = new Button();
	randomButton.Text = "Random";
	randomButton.Location = new Point(515, 3);
	randomButton.Size = new Size(70, 20);
	randomButton.Click += new EventHandler(random_Click);

	Controls.Add(randomButton);
	
	Button stopButton = new Button();
	stopButton.Text = "Stop";
	stopButton.Location = new Point(595, 3);
	stopButton.Size = new Size(50, 20);
	stopButton.Click += new EventHandler(stop_Click);

	Controls.Add(stopButton);
    }

    static int Main() {
	Application.Run(new HopAlong());
	return 0;
    }
} // class HopAlong

} // namespace HopAlong

// The End

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

randomfox: (Default)
randomfox

November 2012

S M T W T F S
    123
45678910
11121314151617
18192021222324
25262728 2930 

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 14th, 2026 01:43 am
Powered by Dreamwidth Studios