/* Zeno random walks */ class StartStopButton extends Button { StartStopButton(int xp, int yp, int wd, int hg, color b, color o, color p) { super(xp, yp, wd, hg, b, o, p); } void display() { // add labels that depend on state of 'running' super.display(); fill(0); textAlign(CENTER); if (running == true) { text("STOP", x + (wide / 2), y + 15); } else { text("START", x + (wide / 2), y + 15); } } void release() { // toggle 'running' after each click on this button super.release(); if (over == true) { running = !running; } } } class ClearButton extends Button { ClearButton(int xp, int yp, int wd, int hg, color b, color o, color p) { super(xp, yp, wd, hg, b, o, p); } void display() { // add a label super.display(); fill(0); textAlign(CENTER); text("CLEAR", x + (wide / 2), y + 15); } void release() { super.release(); if (over == true) { setupGrid(); } } } // globals int leftMargin = 25; // margins define the box in which paths are drawn int rightMargin = 25; int topMargin = 25; int bottomMargin = 65; int widgetHeight = 20; // for sliders and buttons int buttonWidth = 75; boolean running = false; // widget instances StartStopButton startStopButton; ClearButton clearButton; Scrollbar stepsScrollbar; Scrollbar fadeScrollbar; void setup() { size(550, 600); PFont gillsans; gillsans = loadFont("GillSans-14.vlw"); textFont(gillsans); setupGrid(); startStopButton = new StartStopButton( width - rightMargin - buttonWidth, height - 30 - widgetHeight - 5, buttonWidth, widgetHeight, color(155,146,196), // a grayish blue color(105,74,245), // deeper blue on rollover color(105,74,245)); // or when clicked clearButton = new ClearButton( width - rightMargin - buttonWidth, height-30, buttonWidth, widgetHeight, color(155,146,196), color(105,74,245), color(105,74,245)); stepsScrollbar = new Scrollbar( leftMargin + 40, height - 30 - widgetHeight - 5, width - leftMargin - 40 - rightMargin - buttonWidth -10, widgetHeight, 2.0, // min number of steps to plot 150.0, // max number of steps (actually 145 because of tw offset 50.0); // initial value fadeScrollbar = new Scrollbar( leftMargin + 40, height - 30, width - leftMargin - 40 - rightMargin - buttonWidth -10, widgetHeight, 0.0, // no fade at all 42.0, // max fade rate (actually 40) 7.5); // initial fade rate (actually 7) } void setupGrid() { background(211, 207, 201); redrawGrid(); fill(0); textAlign(CENTER); int x; int y = topMargin - 5; x = scaleX(0.0); // placing labels at the top of the grid lines text("0", x, y); x = scaleX(0.25); text("1/4", x, y); x = scaleX(0.5); text("1/2", x, y); x = scaleX(0.75); text("3/4", x, y); x = scaleX(1.0); text("1", x, y); textAlign(LEFT); text("steps", leftMargin, height - 25 - widgetHeight - 5 + 15); // label sliders text("fade", leftMargin, height - 25 + 15); } void redrawGrid() { // called not only in initial setup int lef = scaleX(0.0); // but also in every frame, so that the int rht = scaleX(1.0); // white grid lines will not fade int top = scaleY(0.0); int bot = scaleY(1.0); stroke(255); for (int i = 0; i <= 8; i++) { // note the hard-coded 8 (tsk-tsk) float z = i / 8.0; int x = scaleX(z); line(x, top, x, bot); } line(lef, top, rht, top); line(lef, bot, rht, bot); } // In 'draw' the main business the program -- creating the Zeno paths -- is // actually pretty easy and straightforward. The less-obvious part is making // older paths fade away, so that we don't get a screen plugged up with red // ink. One's first idea -- redrawing each path in a lighter color on each // cycle -- is a mess. For starters, you'd have to store all the paths, whereas // the program as written simply draws 'em and forgets 'em. The solution adopted // here is the draw a partly transparent box over the whole area on each frame. // The alpha value of the box determines the frame rate. // For those new to Processing, 'draw' is the procedure that gets called // on every frame. void draw() { startStopButton.update(); startStopButton.display(); clearButton.update(); clearButton.display(); stepsScrollbar.update(mouseX, mouseY); stepsScrollbar.display(); fadeScrollbar.update(mouseX, mouseY); fadeScrollbar.display(); if (running == true) { drawPath(); noStroke(); int fadeRate = int(fadeScrollbar.getPos()); fill(211, 207, 201, fadeRate); rect(leftMargin, topMargin, width - leftMargin - rightMargin, height - topMargin - bottomMargin); redrawGrid(); } } // Now we finally get to calculate and draw a Zeno trajectory. The // number of steps in the path is picked off the scrollbar, and this // determines the vertical increment, so that the path fits in the // panel. Every path begins at z = 1/2, then we use the zStep routine // to calculate the next position. The z value is converted into picture // coordinates by the scaleX procedure, and likewise y values come // from scaleY. void drawPath() { stroke(247, 77, 30); int steps = int(stepsScrollbar.getPos()); float startZ = 0.5; int startX = scaleX(startZ); int startY = scaleY(0.0); for (int i=1; i<=steps; i++) { float endZ = zStep(startZ); int endX = scaleX(endZ); int endY = scaleY(i / float(steps)); line(startX, startY, endX, endY); startZ = endZ; startX = endX; startY = endY; } } void mousePressed() { // pass events to widgets on mouse activity startStopButton.press(); clearButton.press(); stepsScrollbar.press(mouseX, mouseY); fadeScrollbar.press(mouseX, mouseY); } void mouseReleased() { // pass events to widgets on mouse activity startStopButton.release(); clearButton.release(); stepsScrollbar.release(); fadeScrollbar.release(); } // The Zeno process takes place on the segment [0,1]; here we // scale to the width of the picture panel. The y coordinate can // be handled in the same way. int scaleX(float x) { return int(leftMargin + (x * (width - (leftMargin + rightMargin)))); } int scaleY(float y) { return int(topMargin + (y * (height - (topMargin + bottomMargin)))); } // Here's the actual Zeno process. With equal probability the walker // steps either or right, moving a distance equal to half the distance // to the nearer boundary. float zStep(float z) { float distance = 0.5 * min(z, 1 - z); if (randomBoolean() == true) { return z + distance; } else { return z - distance; } } boolean randomBoolean() { float z = random(1.0); if (z < 0.5) { return true; } else { return false; } }