Spoon-Scope

 

This design takes the bouncing rectangle design from my GIF and applies it to the format of a praxinoscope. The side ratios are slightly different, adjusted only so the rectangle would fill the frame well. The motion in the rectangle is fluid as it uses the same adjustable center cosine window function that is used in the GIF for the rectangle.

See my GIF post for sketches.

praxinoscope-output

 

 

// Template for KidzLabs/4M/Toysmith Animation Praxinoscope
// https://www.amazon.com/4M-3474-Animation-Praxinoscope/dp/B000P02HYC
// https://www.walmart.com/ip/Animation-Praxinoscope-Science-Kits-by-Toysmith-3474/45681503
// Developed for Processing 3.3.6 * http://processing.org
// 23 January 2018 * Golan Levin 
 
// See information about Processing PDF export at: 
// https://processing.org/reference/libraries/pdf/index.html
// PDF generated by Processing can be opened in Adobe Illustrator.
import processing.pdf.*;
boolean bRecordingPDF = false;
 
float inch = 72; 
float diamArtInner = inch * 1.50; 
float diamArtOuter = inch * 4.80; 
float diamCutInner = inch * 1.41; 
float diamCutOuter = inch * 4.875; 
float holeDy = inch * 0.23;
float holeDx = inch * 0.20;
float holeD = inch * 0.1;
 
final int nFrames = 10; 
int myFrameCount = 0;
int exportFrameCount = 0; 
boolean bAnimate = true; 
boolean bExportFrameImages = false;
 
//-------------------------------------------------------
void setup() {
  size(792, 612); // 11x8.5" at 72DPI
  frameRate(15);
  smooth();
} 
 
//-------------------------------------------------------
void draw() {
  background(240); 
  if (bRecordingPDF) {
    beginRecord(PDF, "praxinoscope-output.pdf");
  }
 
  // Do all the drawing. 
  pushMatrix(); 
  translate(width/2, height/2);
  drawCutLines(); 
  drawGuides(); 
  drawAllFrames();
  popMatrix();
 
  if (bExportFrameImages) {
    // If activated, export .PNG frames 
    if (exportFrameCount < nFrames) { String filename = "frame_" + nf((exportFrameCount%nFrames), 3) + ".png"; saveFrame("frames/" + filename); println("Saved: " + filename); exportFrameCount++; if (exportFrameCount >= nFrames) {
        bExportFrameImages = false;
        exportFrameCount = 0;
      }
    }
  }
 
  if (bRecordingPDF) {
    endRecord();
    bRecordingPDF = false;
  }
}
 
 
//-------------------------------------------------------
void keyPressed() {
  switch (key) {
  case ' ': 
    // Press spacebar to pause/unpause the animation. 
    bAnimate = !bAnimate;
    break;
 
  case 'p': 
  case 'P':
    // Press 'p' to export a PDF for the Praxinoscope.
    bRecordingPDF = true; 
    break;
 
  case 'f': 
  case 'F': 
    // Press 'f' to export .png Frames (to make an animated .GIF)
    myFrameCount = 0; 
    exportFrameCount = 0; 
    bExportFrameImages = true;
    bAnimate = true; 
    break;
  }
}
 
//-------------------------------------------------------
void drawCutLines() {
  fill(0); 
  textAlign(CENTER, BOTTOM); 
  text("Praxinoscope Template", 0, 0-diamCutOuter/2-6); 
 
  stroke(0); 
  strokeWeight(1.0);
 
  noFill(); 
  if (!bRecordingPDF) {
    fill(255); 
  }
  ellipse(0, 0, diamCutOuter, diamCutOuter);
 
  noFill(); 
  if (!bRecordingPDF) {
    fill(240); 
  }
  ellipse(0, 0, diamCutInner, diamCutInner);
 
  noFill(); 
  ellipse(diamCutOuter/2 - holeDx, 0-holeDy, holeD, holeD); 
 
  line (diamCutInner/2, 0, diamCutOuter/2, 0);
}
 
//-------------------------------------------------------
void drawGuides() {
  // This function draws the guidelines. 
  // Don't draw these when we're exporting the PDF. 
  if (!bRecordingPDF) {
 
    noFill(); 
    stroke(128); 
    strokeWeight(0.2); 
    ellipse(0, 0, diamArtInner, diamArtInner); 
    ellipse(0, 0, diamArtOuter, diamArtOuter);
 
    for (int i=0; i<nFrames; i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
      float pxi = diamArtInner/2 * cos(angle);
      float pyi = diamArtInner/2 * sin(angle);
      float pxo = diamArtOuter/2 * cos(angle);
      float pyo = diamArtOuter/2 * sin(angle);
      stroke(128); 
      strokeWeight(0.2);
      line (pxi, pyi, pxo, pyo);
    }
 
    // Draw the red wedge outline, highlighting the main view.
    int redWedge = 7; // assuming nFrames = 10
    for (int i=redWedge; i<=(redWedge+1); i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
      float pxi = diamArtInner/2 * cos(angle);
      float pyi = diamArtInner/2 * sin(angle);
      float pxo = diamArtOuter/2 * cos(angle);
      float pyo = diamArtOuter/2 * sin(angle);
      stroke(255, 0, 0); 
      strokeWeight(2.0);
      line (pxi, pyi, pxo, pyo);
    }
    noFill(); 
    stroke(255, 0, 0); 
    strokeWeight(2.0);
    float startAngle = redWedge*TWO_PI/nFrames;
    float endAngle = (redWedge+1)*TWO_PI/nFrames;
    arc(0, 0, diamArtInner, diamArtInner, startAngle, endAngle); 
    arc(0, 0, diamArtOuter, diamArtOuter, startAngle, endAngle); 
 
 
    for (int i=0; i<nFrames; i++) {
      float angle = map(i, 0, nFrames, 0, TWO_PI); 
 
      pushMatrix();
      rotate(angle); 
      float originY = ((diamArtOuter + diamArtInner)/2)/2;
      translate(0, 0-originY); 
 
      noFill(); 
      stroke(128); 
      strokeWeight(0.2);
      line (-inch/2, 0, inch/2, 0); 
      line (0, -inch/2, 0, inch/2); 
 
      popMatrix();
    }
  }
}
 
//-------------------------------------------------------
void drawAllFrames() {
  for (int i=0; i<nFrames; i++) {
    float angle = map(i, 0, nFrames, 0, TWO_PI); 
    float originY = ((diamArtOuter + diamArtInner)/2)/2;
 
    pushMatrix();
    rotate(angle); 
    translate(0, 0-originY); 
    scale(0.8, 0.8); // feel free to ditch this 
 
    int whichFrame = i; 
    if (bAnimate) {
      whichFrame = (i+myFrameCount)%nFrames;
    }
    drawArtFrame (whichFrame); 
    // drawArtFrameAlternate (whichFrame); 
 
    popMatrix();
  }
  myFrameCount++;
}
 
 
//-------------------------------------------------------
void drawArtFrame (int whichFrame) { 
  // Draw the artwork for a generic frame of the Praxinoscope, 
  // given the framenumber (whichFrame) out of nFrames.
  // NOTE #1: The "origin" for the frame is in the center of the wedge.
  // NOTE #2: Remember that everything will appear upside-down!
 
  fill(0); 
  noStroke(); 
 
  float multiplier2 = function_AdjustableCenterCosineWindow(
                                              map(whichFrame, 
                                              0, nFrames, 0, 1), 
                                              0.3);
  drawCenteredRectangleFromBottom(0, 30, 
                                  map(multiplier2, 0, 1, 35, 65),
                                  map(multiplier2, 0, 1, 100, 50));
  noFill();
  stroke(255);
  strokeWeight(5);
}
 
//-------------------------------------------------------
 
void drawCenteredRectangleFromBottom(float x, float y, 
                            float width, float height) {
  float upperLeftX = x - (width / 2);
  float upperLeftY = height - y;
  rect(upperLeftX, upperLeftY, width, -height);
}
 
float function_AdjustableCenterCosineWindow (float x, float a) {
  // functionName = "Adjustable Center Cosine Window";
 
  float ah = a/2.0; 
  float omah = 1.0 - ah;
 
  float y = 1.0;
  if (x <= a) {
    y = 0.5 * (1.0 + cos(PI* ((x/a) - 1.0)));
  } 
  else {
    y = 0.5 * (1.0 + cos(PI* (((x-a)/(1.0-a))  )));
  } 
  return y;
}

paukparl-AnimatedLoop

thoughts:
I wanted to make a series of simple animations with circles as a main theme. But ended up only making two. I wanted to try the blur effect too, but spent too much time figuring out how to export frames from p5 without randomly skipping any frames. But I just gave up. I think slowing down the frame rate might be a solution, although I haven't tried it. Apart from that, I had fun.

gif:

 

 

...
void renderMyDesign (float percent) {
  float x1 = cos(percent*6*PI);
  float x2 = -cos(percent*6*PI);
  float z1 = sin(percent*6*PI);
  float z2 = -sin(percent*6*PI);
  noStroke();
  background(0, 0, 200);
 
 
  translate(width/2, height/2);
  rotate(2*PI*percent);
  if (percent%(1/3f) < 1/6f) {
    fill(255);
    ellipse(
      map(x1, -1, 1, -160, 160), 
      0, 
      200+ map(z1, -1, 1, -80, 80), 
      200+ map(z1, -1, 1, -80, 80));
    fill(0);
    ellipse(
      map(x2, -1, 1, -160, 160), 
      0, 
      200+ map(z2, -1, 1, -80, 80), 
      200+ map(z2, -1, 1, -80, 80));
  } else {
    fill(0);
    ellipse(
      map(x2, -1, 1, -160, 160), 
      0, 
      200+ map(z2, -1, 1, -80, 80), 
      200+ map(z2, -1, 1, -80, 80));
    fill(255);
    ellipse(
      map(x1, -1, 1, -160, 160), 
      0, 
      200+ map(z1, -1, 1, -80, 80), 
      200+ map(z1, -1, 1, -80, 80));
  }
}
...
var frame = 0;
var phase1 = 30;
var phase2 = 50;
var phase3 = 150; // start falling
var phase4 = 160;
var phase5 = 190;
 
var SIZE = 120;
 
var r = 255;
var g = 255;
var b = 255;
 
var cnv;
 
function setup() {
  cnv = createCanvas(640/2, 640/2);
  noStroke();
}
 
function draw() {
  background(225, 0, 100);
 
 
  if (frame <= phase2) { translate(0, map(pennerEaseOutCubic(frame/phase2), 0, 1, -60/2, 520/2)); } // horizontal oscillation if (frame > phase1 && frame < phase4) { var osc = map(sin((frame-phase2)/PI*2), -1, 1, -50/2, 50/2); var oscScale = reverseSineEase(map(frame, phase1, phase4, 0, 2*PI)); translate(osc*oscScale, 0); // ellipse(width/2, 0, reverseSineEase(map(frame, phase1, phase4, SIZE, 400))); } if (frame > phase2 && frame <= phase3) { translate(0, map(pennerEaseInOutQuad((frame-phase2)/(phase3-phase2)), 0, 1, 520/2, 200/2)); SIZE += 1.5/2; } if (frame > phase3) {
    translate(0, map(pennerEaseInCubic((frame-phase3)/(phase5-phase3)), 0, 1, 200/2, 840/2)); 
  }
 
  if (frame > phase2 + 20 && frame < phase4) { r -= 3.5; g -= 3.5; b -= 3.5; } fill(r, g, b); ellipse(width/2, 0, SIZE); frame++; if (frame>phase5) {
    frame=0;
    SIZE = 120;
    r=255;
    g=255;
    b=255;
  }
}
 
function pennerEaseOutCubic(t) {
  t = t-1.0;
  return (t*t*t + 1);
}
 
function pennerEaseInCubic(t) {
  return t*t*t;
}
 
function reverseSineEase(t) {
  return ((1-cos(t))/2)*((1-cos(t))/2);
}
 
function pennerEaseInOutQuad (t) {
  if ((t/=1/2) < 1) return 1/2*t*t;
  return -1/2 * ((--t)*(t-2) - 1);
}

rigatoni-Scope

At first I was pretty daunted by the thought of figuring out what the entire zoetrope template was all about. The comments were quite helpful though and I figured out how to put together my "skeptical emoji" using a few ellipses and a curve. Reading through the template I also found a much better way to handle "per-frame" actions than I had been doing up until this point.

nixel-AnimatedLoop

I had a lot of fun with this project!

My process:

I started with some messy brainstorms from when we first got the prompt:

I thought that loops of characters bouncing down stairs, or being transported by a never ending escalator would be fun. I also considered doing skyscapes that looped between sunrise and sunset and a swinging character. In the end, I went with rain because it was the simplest in terms of shape and color design.

I thought that I would do some sort of walk cycle and plotted some leg rotations.

I went online and found a shape-heavy walk cycle gif and spliced some frames out to break it down.

However, when I began to code and actually look into the easing functions, I realized that it would be simpler to pair them with a bouncing animation instead of a walking one. It was sad to leave all the walking animation work I did, but I think I made a better choice in terms of time and simplicity. I think I will go back one day and make all of the ideas I had initially.

After a lot of experimentation (and suffering) I chose to use primarily the Flat Top Window easing function since it was bouncy, playful, and worked well for a looping animation since it was symmetrical.

Here is a thumbnail I made half way through coding in order to hash out the locations of some body parts.

I struggled with the rain since I thought it added to the narrative of the gif, but didn't necessarily loop. I also struggled with the umbrella since it's an interesting visual element but doesn't make much sense since the character isn't holding on to it. I did at one point code an umbrella handle and hands, but it made the image too complex so I got rid of them.

Critiquing myself, I think that I did well in keeping the design simple and appealing. I could have been more ambitious with connecting the umbrella to the character and been more efficient with my code (I organized it in groups of body parts but ended up repeating a lot of simple functions like noStroke() or fill() when I could have just organized the code more efficiently).

 

 
// This is a template for creating a looping animation in Processing/Java. 
// When you press the 'F' key, this program will export a series of images
// into a "frames" directory located in its sketch folder. 
// These can then be combined into an animated gif. 
// Known to work with Processing 3.3.6
// Prof. Golan Levin, January 2018
 
 
//===================================================
// Global variables. 
String  myNickname = "nixel"; 
int     nFramesInLoop = 60;
int     nElapsedFrames;
boolean bRecording; 
Drop[] drops = new Drop[50];
 
//===================================================
void setup() {
  size (640, 640); 
  bRecording = false;
  nElapsedFrames = 0;  
  for (int i = 0; i < drops.length; i++) {
   drops[i] = new Drop(); 
  }
}
//===================================================
void keyPressed() {
  if ((key == 'f') || (key == 'F')) {
    bRecording = true;
    nElapsedFrames = 0;
  }
}
//===================================================
void draw() {
  // Compute a percentage (0...1) representing where we are in the loop.
  float percentCompleteFraction = 0; 
  if (bRecording) {
    percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop;
  } else {
    percentCompleteFraction = (float) (frameCount % nFramesInLoop) / (float)nFramesInLoop;
  }
 
  // Render the design, based on that percentage. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  if (bRecording) {
    saveFrame("frames/" + myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
//===================================================
void renderMyDesign (float percent) {
  int bgColor = 255;
  background (bgColor);
  stroke (0, 0, 0); 
  strokeWeight (2); 
  float pennerEase = FlatTopWindow(percent);
  noStroke();
 
  //umbrella
  float umbrellashift = (map(pennerEase, 0, 1, -50, -20)); 
  fill(255, 0, 0);
  ellipse(width/2, 240 + umbrellashift, 360, 360);
 
  //cutout umbrella
  fill(bgColor);
  int r = 250;
  for (int i = 0; i < 360; i += 30){
  float dy = sin(radians(i)) * r;
  float dx = cos(radians(i)) * r;
  ellipse(width/2 + dx, 240 + umbrellashift + dy, 180, 180);
  }
 
  rectMode(CENTER);
 
  //ground
  fill(10, 95, 223);
  rect(width/2, 600, width, 200);
 
  //puddle
  noFill();
  stroke(255);
  float jumpWeight = map(percent, 0, 1, 15, 0);
  float puddleEase = map(PennerEaseOutExpo(percent-0.7), 0, 1, 100, 1000);
  float puddleEaseY = map(PennerEaseOutExpo(percent), 0, 1, 0, 50); 
  strokeWeight(jumpWeight);
 
  if (percent >= 0.7){
    ellipse(355, 530, puddleEase, puddleEaseY);
    ellipse(200, 560, puddleEase, puddleEaseY);
    ellipse(300, 600, puddleEase, puddleEaseY);
    ellipse(50, 650, puddleEase, puddleEaseY);
    ellipse(550, 610, puddleEase, puddleEaseY);
  }
 
  //hair
  noStroke();
  float hairshift = (map(pennerEase, 0, 1, 60, -40)); 
  fill(0);
  arc(width/2, 240 + hairshift, 180, 300, PI, TWO_PI);
 
 
  //legs 
  float rX = map(pennerEase, 0, 1, 350, 340);
  float Y = map(pennerEase, 0, 1, 560, 510);
  float squishX = map(pennerEase, 0, 1, 10, 0);
  float lX = map(pennerEase, 0, 1, width - 360, width - 350);
  fill(247,227,40);
 
  //right leg
  triangle(rX, Y, rX - 5 - squishX, Y - 150, rX + 10 + squishX, Y - 150);
 
  //left leg
  triangle(lX, Y, lX - 5 - squishX, Y - 150, lX + 10 + squishX, Y - 150);
 
  //body
	float bsquish = map(pennerEase, 0, 1, 10, -10);
  float bodyshift = (map(pennerEase, 0, 1, 50, -20)); 
  fill(247, 227, 40);
  triangle(width/2, 170 + bodyshift, width/2 - 100 - bsquish, 380 + bodyshift, width/2 + 100 + bsquish, 380 + bodyshift);
 
  //mirrored legs 
  float mrX = map(pennerEase, 0, 1, 350, 340);
  float mrY = map(pennerEase, 0, 1, 510, 560);
  float msquishX = map(pennerEase, 0, 1, 10, 0);
  float mlX = map(pennerEase, 0, 1, width - 360, width - 350);
  fill(247,227,40);
 
  //right leg
  triangle(mrX, mrY + 50, mrX - 5 - msquishX, mrY + 175, mrX + 10 + msquishX, mrY + 175);
 
  //left leg
  triangle(mlX, mrY + 50, mlX - 5 - msquishX, mrY + 175, mlX + 10 + msquishX, mrY + 175);
  //head
  float headshift = (map(pennerEase, 0, 1, 55, -30)); 
  fill(255);
  ellipse(width/2, 170 + headshift, 100, 100);
 
  //mouth
  float mouthshift = (map(pennerEase, 0, 1, 30, -70)); 
  float mouthY = (map(pennerEase, 0, 1, -5, 10)); 
  fill(255, 0, 0);
  ellipse(width/2, 225 + mouthshift, 20, 15 + mouthY);
 
  //bangs  
  fill(0);
  arc(width/2, 150 + hairshift, 100, 100, PI, TWO_PI);
 
  //eyes
  float eyeY = (map(pennerEase, 0, 1, 1, 5)); 
  float eyeX = (map(pennerEase, 0, 1, 1, 5)); 
  ellipse(width/2 - 30, 200 + mouthshift, 10 + eyeX, 15 + eyeY);
  ellipse(width/2 + 30, 200 + mouthshift, 10 + eyeX, 15 + eyeY);
 
  //arms
  fill(247,227,40);
 
  float Ya = map(pennerEase, 0, 1, 10, 80);
  //right arm
  triangle(345, 230 + bodyshift, 345, 260 + bodyshift, 450, 250 + Ya);
 
  //left arm
  triangle(width - 345, 230 + bodyshift, width - 345, 260 + bodyshift, width - 450, 250 + Ya);
 
  //rain
  for (int i = 0; i < drops.length; i++){
  drops[i].fall();
  drops[i].show();
  }
 
}
 
//===================================================
//rain code refenced from Dan Shiffman's Purple Rain Coding Challenge
class Drop {
 float x = random(width);
 float y = random(0, height);
 float yspeed = 11;
 float len = 15;
 
 void fall(){
   y += yspeed;
 
  if (y > height) {
   y = 0;
   yspeed = 11;
  }
 
   }
 
 void show(){
  stroke(10, 95, 223);
  strokeWeight(2);
  line(x, y, x, y+len);
 }
}
 
//===================================================
float FlatTopWindow (float x) {
  //easing functions from Pattern Master https://github.com/golanlevin/Pattern_Master
  // http://en.wikipedia.org/wiki/Window_function 
 
  final float a0 = 1.000;
  final float a1 = 1.930;
  final float a2 = 1.290;
  final float a3 = 0.388;
  final float a4 = 0.032;
 
  float pix = PI*x;
  float y = a0 - a1*cos(2*pix) + a2*cos(4*pix) - a3*cos(6*pix) + a4*cos(8*pix);
  y /= (a0 + a1 + a2 + a3 + a4); 
 
  return y;
}
 
float PennerEaseOutExpo(float t) {
  return (t==1) ? 1 : (-pow(2, -10 * t) + 1);
}

harsh – AnimatedLoop

I started off with inspiration from D'arcy Thompson's "Theory of Transformations" - where he uses grid transformations to morph between different species of animals. I found his idea of being able to use simple mathematical equations to create transformations quite fascinating. So, I began to implement some of these systems, which included shear, scale, polar co-ordinate and exponential-scaling transformations. First, I tested this theory out on something simple - like a circle, before moving on to more complex geometries. I decided to keep my objects constructed as point clouds - as this kept my process of iteration quick, and the system scalable. By a sheer stroke of luck, I tried a polar coordinate transform before transforming it into a cartesian system - this created a really cool, point dissipation effect. I then began playing with these transformations, eventually closing in on using Mattise's Dancers - I thought the notion of an emergent composition from a seemingly random point cloud was quite interesting. It also connotated notions of the Gestalt and the border between familiarity and familiarity of a representation. I also thought that it made an interesting take on composition, referencing Mattise's methods in a digital medium. This was probably one the most iterations I have gone through in a week-long project, tweaking transformations, easing functions and point clouds numerous times before arriving at this result. I used the exponentialInOut and quinticInOut easing functions - both primarily to increase the time spent at the actual image and to speed up the transformation process. The process of crafting these transformations, both with morphing and easing functions was quite a novel one for me, having never really thought about computational craft before. Overall, I'm happy with the effect, and I think the easing and transformation functions work well. If I had more time on this, I would increase the complexity of the animation to emphasize this idea of composition - trying to recreate each object like pieces of paper cut by Mattise.

For this project, I did not really keep "sketches" - instead relying on Thompson's illustrations and descriptions as a means of generation:

 
 
 
//===================================================
// User-modifiable global variables.
var myNickname = "Harsh";
var nFramesInLoop = 120;
var bEnableExport = true;
 
 
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 
 
var circVals = [];
for(var i=0; i<pointsVals.length; i++){
  var curPointVals = pointsVals[i];
  circVals.push([curPointVals[0],curPointVals[1]*-1]);
}
 
 
var polarVals = []
for(var i=0; i<circVals.length; i++){
  var polarRadius = 30;
  var curPointVals = circVals[i];
  var cosX = polarRadius*Math.cos(curPointVals[0]);
  var sinY = polarRadius*Math.sin(curPointVals[1])
  polarVals.push([cosX,sinY]);
}
 
 
var shearVals = [];
for(var i=0; i<circVals.length; i++){
  var curPointVals = polarVals[i];
  var newX = curPointVals[0]-200;
  var newY = curPointVals[1]+200;
  shearVals.push([newX,newY]);
}
 
var shearDifferencesCartesian = []
for(var i=0; i<pointsVals.length; i++){
  var curCartValx = circVals[i][0];
  var curCartValy = circVals[i][1];
  var curShearValx = shearVals[i][0];
  var curShearValy = shearVals[i][1];
  var curTotalDifferenceX = curShearValx-curCartValx;
  var curTotalDifferenceY = curShearValy-curCartValy;
  shearDifferencesCartesian.push([curTotalDifferenceX,curTotalDifferenceY]);
}
 
var shearDifferencesPolar = []
for(var i=0; i<pointsVals.length; i++){
  var curPolarValx = polarVals[i][0];
  var curPolarValy = polarVals[i][1];
  var curShearValx = shearVals[i][0];
  var curShearValy = shearVals[i][1];
  var curTotalDifferenceX = curPolarValx-curShearValx;
  var curTotalDifferenceY = curPolarValy-curShearValy;
  shearDifferencesPolar.push([curTotalDifferenceX,curTotalDifferenceY]);
}
 
var cartesianDifferences = []
for(var i=0; i<pointsVals.length; i++){
  var curCartValx = circVals[i][0];
  var curCartValy = circVals[i][1];
  var curPolarValx = polarVals[i][0];
  var curPolarValy = polarVals[i][1];
  var curTotalDifferenceX = curPolarValx-curCartValx;
  var curTotalDifferenceY = curPolarValy-curCartValy;
  cartesianDifferences.push([curTotalDifferenceX,curTotalDifferenceY]);
}
 
var repVals = [];
for(var i=0; i<pointsVals.length; i++){
  var curPointVals = pointsVals[i];
  repVals.push([curPointVals[0],curPointVals[1]]);
}
 
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 
var circValsHead = [];
for(var i=0; i<headVals.length; i++){
  var curPointVals = headVals[i];
  circValsHead.push([curPointVals[0],curPointVals[1]*-1]);
}
 
 
var polarValsHead = []
for(var i=0; i<circValsHead.length; i++){
  var polarRadius = 30;
  var curPointVals = circValsHead[i];
  var cosX = polarRadius*Math.cos(curPointVals[0]);
  var sinY = polarRadius*Math.sin(curPointVals[1])
  polarValsHead.push([cosX,sinY]);
}
 
 
var shearValsHead = [];
for(var i=0; i<circValsHead.length; i++){
  var curPointVals = polarValsHead[i];
  var newX = curPointVals[0]+200;
  var newY = curPointVals[1]+200;
  shearValsHead.push([newX,newY]);
}
 
var shearDifferencesCartesianHead = []
for(var i=0; i<circValsHead.length; i++){
  var curCartValx = circValsHead[i][0];
  var curCartValy = circValsHead[i][1];
  var curShearValx = shearValsHead[i][0];
  var curShearValy = shearValsHead[i][1];
  var curTotalDifferenceX = curShearValx-curCartValx;
  var curTotalDifferenceY = curShearValy-curCartValy;
  shearDifferencesCartesianHead.push([curTotalDifferenceX,curTotalDifferenceY]);
}
 
var shearDifferencesPolarHead = []
for(var i=0; i<circValsHead.length; i++){
  var curPolarValx = polarValsHead[i][0];
  var curPolarValy = polarValsHead[i][1];
  var curShearValx = shearValsHead[i][0];
  var curShearValy = shearValsHead[i][1];
  var curTotalDifferenceX = curPolarValx-curShearValx;
  var curTotalDifferenceY = curPolarValy-curShearValy;
  shearDifferencesPolarHead.push([curTotalDifferenceX,curTotalDifferenceY]);
}
 
var cartesianDifferencesHead = []
for(var i=0; i<circValsHead.length; i++){
  var curCartValx = circValsHead[i][0];
  var curCartValy = circValsHead[i][1];
  var curPolarValx = polarValsHead[i][0];
  var curPolarValy = polarValsHead[i][1];
  var curTotalDifferenceX = curPolarValx-curCartValx;
  var curTotalDifferenceY = curPolarValy-curCartValy;
  cartesianDifferencesHead.push([curTotalDifferenceX,curTotalDifferenceY]);
}
 
var repValsHead = [];
for(var i=0; i<circValsHead.length; i++){
  var curPointVals = headVals[i];
  repValsHead.push([curPointVals[0],curPointVals[1]]);
}
 
 
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
 
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
//===================================================
function setup() {
  theCanvas = createCanvas(900, 900);
  bRecording = false;
  nElapsedFrames = 0;
}
//===================================================
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
    }
  }
}
//===================================================
function draw() {
  // Compute a percentage (0...1) representing where we are in the loop.
  var percentCompleteFraction = 0;
  if (bRecording) {
    percentCompleteFraction = float(nElapsedFrames) / float(nFramesInLoop);
  } else {
    percentCompleteFraction = float(frameCount % nFramesInLoop) / float(nFramesInLoop);
  }
  // Render the design, based on that percentage.
  // This function renderMyDesign() is the one for you to change.
  renderMyDesign (percentCompleteFraction);
  // If we're recording the output, save the frame to a file.
  // Note that the output images may be 2x large if you have a Retina mac.
  // You can compile these frames into an animated GIF using a tool like:
  if (bRecording && bEnableExport) {
    var frameOutputFilename = myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png";
    print("Saving output image: " + frameOutputFilename);
    saveCanvas(theCanvas, frameOutputFilename, 'png');
    nElapsedFrames++;
 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
//===================================================
function renderMyDesign (percent) {
 
  background(13, 60, 85);
  smooth();
  stroke(0, 0, 0);
  strokeWeight(2);
 
  //----------------------
  var e = new p5.Ease(); // easing function object
 
  // percentSig = e.quadraticInOut(percent);
  // console.log(percentSig);
 
  percentSig = percent;
  if(0<=percentSig && percentSig<0.1){
 
    for(var i=0; i<circVals.length; i++){
      var curVal = polarVals[i];
      var curX = curVal[0];
      var curY = curVal[1];
      var curDifferenceX = shearDifferencesPolar[i][0];
      var curDifferenceY = shearDifferencesPolar[i][1];
      var newPerc = map(percent,0,0.1,0,1);
      var e = new p5.Ease(); // easing function object
      newPerc = e.exponentialInOut(newPerc);
      var newX = -1*((curDifferenceX*newPerc)-curX)
      var newY = -1*((curDifferenceY*newPerc)-curY)
      repVals[i] = [newX,newY];
    }
 
 
    for(var i=0; i<circValsHead.length; i++){
      var curVal = polarValsHead[i];
      var curX = curVal[0];
      var curY = curVal[1];
      var curDifferenceX = shearDifferencesPolarHead[i][0];
      var curDifferenceY = shearDifferencesPolarHead[i][1];
      var newPerc = map(percent,0,0.1,0,1);
      var e = new p5.Ease(); // easing function object
      newPerc = e.exponentialInOut(newPerc);
      var newX = -1*((curDifferenceX*newPerc)-curX)
      var newY = -1*((curDifferenceY*newPerc)-curY)
      repValsHead[i] = [newX,newY];
    }
 
  }
 
  else if(.1<=percentSig&& percentSig<0.66){
 
    for(var i=0; i<circVals.length; i++){
      var curVal = shearVals[i];
      var curX = curVal[0];
      var curY = curVal[1];
      var curDifferenceX = shearDifferencesCartesian[i][0];
      var curDifferenceY = shearDifferencesCartesian[i][1];
      var newPerc = map(percent,0.1,0.66,0,1)
      var e = new p5.Ease(); // easing function object
      newPerc = e.quinticInOut(newPerc);
      var newX = -1*((curDifferenceX*newPerc) - curX)
      var newY =  -1*((curDifferenceY*newPerc) - curY)
      repVals[i] = [newX,newY];
    }
 
 
    for(var i=0; i<circValsHead.length; i++){
      var curVal = shearValsHead[i];
      var curX = curVal[0];
      var curY = curVal[1];
      var curDifferenceX = shearDifferencesCartesianHead[i][0];
      var curDifferenceY = shearDifferencesCartesianHead[i][1];
      var newPerc = map(percent,0.1,0.66,0,1)
      var e = new p5.Ease(); // easing function object
      newPerc = e.quinticInOut(newPerc);
      var newX = -1*((curDifferenceX*newPerc) - curX)
      var newY =  -1*((curDifferenceY*newPerc) - curY)
      repValsHead[i] = [newX,newY];
    }
  }
 
  else if(.66<=percentSig&& percentSig<1){
 
    for(var i=0; i<circVals.length; i++){
      var curVal = circVals[i];
      var curX = curVal[0];
      var curY = curVal[1];
      var curDifferenceX = cartesianDifferences[i][0];
      var curDifferenceY = cartesianDifferences[i][1];
      var newPerc = map(percent,0.66,1,0,1)
      var e = new p5.Ease(); // easing function object
      newPerc = e.quadraticInOut(newPerc);
      var newX = (curDifferenceX*newPerc) + curX
      var newY =  (curDifferenceY*newPerc) + curY
      repVals[i] = [newX,newY];
    }
 
    for(var i=0; i<circValsHead.length; i++){
      var curVal = circValsHead[i];
      var curX = curVal[0];
      var curY = curVal[1];
      var curDifferenceX = cartesianDifferencesHead[i][0];
      var curDifferenceY = cartesianDifferencesHead[i][1];
      var newPerc = map(percent,0.66,1,0,1)
      var e = new p5.Ease(); // easing function object
      newPerc = e.quadraticInOut(newPerc);
      var newX = (curDifferenceX*newPerc) + curX
      var newY =  (curDifferenceY*newPerc) + curY
      repValsHead[i] = [newX,newY];
    }
  }
 
 
  for(var i=0; i<repVals.length; i++){
    //console.log((oldVals[i][0]+50),(oldVals[i][1]+50))
    push();
    fill(19, 149, 186);
    stroke(19, 149, 186);
    ellipse((repVals[i][0]+450),(repVals[i][1]+450),2,2);
    pop();
  }
 
 
  for(var i=0; i<repValsHead.length; i++){
    //console.log((oldVals[i][0]+50),(oldVals[i][1]+50))
    push();
    fill(241, 108, 32);
    stroke(241, 108, 32);
    ellipse((repValsHead[i][0]+450),(repValsHead[i][1]+450),5,5);
    pop();
  }
 
  //----------------------
  // Include some visual feedback.
  // fill(255, 0, 0);
  // noStroke();
  // textAlign(CENTER);
  // var percentDisplayString = "" + nf(percent, 1, 3);
  // text(percentDisplayString, 100, 200 - 15);
}

chromsan-Scope

PDF: praxinoscope-output

This design is essentially four triangles whose top point moves towards the center. I made it as a practice for my main GIF, which uses this same effect. I experimented with coloring the triangles, but in the end settled for a black and white combination, which sort of gives the illusion of a 3D space. The triangles move with an easing function so they close faster than they open, which gives a more interesting movement than just a constant open and close.

The relevant code, inserted into the praxinoscope template:

void drawArtFrame (int whichFrame) { 
 
  int height = 40;
  int width = 40;
  translate(-20, -20);
 
  if (whichFrame &lt;= nFrames/2){
    // close quickly 
    float eased = function_DoubleExponentialSigmoid(map(whichFrame, 0, nFrames/2, 0, 1), 0.7);
    fill(50);
    triangle(0, height, width, height, width/2, map(eased, 0, 1, height, height/2 ));
    fill(100);
    triangle(0, 0, 0, height, width/2 * map(eased, 0, 1, 0, 1), height/2);
    fill(150);
    triangle(0, 0, width, 0, width/2, height/2 * map(eased, 0, 1, 0, 1));
    fill(200);
    triangle(width, 0, width, height, map(eased, 0, 1, width, width/2), height/2); 
 
  } else {
    // open slowly
    float eased = function_DoubleExponentialSigmoid(map(whichFrame, nFrames/2, nFrames, 0, 1), 0.05);
    fill(50);
    triangle(0, height, width, height, width/2, map(eased, 0, 1, height/2, height ));
    fill(100);
    triangle(0, 0, 0, height, width/2 * map(eased, 0, 1, 1, 0), height/2);
    fill(150);
    triangle(0, 0, width, 0, width/2, height/2 * map(eased, 0, 1, 1, 0));
    fill(200);
    triangle(width, 0, width, height, map(eased, 0, 1, width/2, width), height/2); 
  }
}
 
// Taken from https://github.com/golanlevin/Pattern_Master
float function_DoubleExponentialSigmoid (float x, float a) {
  // functionName = "Double-Exponential Sigmoid";
 
  float min_param_a = 0.0 + EPSILON;
  float max_param_a = 1.0 - EPSILON;
  a = constrain(a, min_param_a, max_param_a); 
  a = 1-a;
 
  float y = 0;
  if (x&lt;=0.5) {
    y = (pow(2.0*x, 1.0/a))/2.0;
  } else {
    y = 1.0 - (pow(2.0*(1.0-x), 1.0/a))/2.0;
  }
  return y;
}

 

 

02 – Reading

Here's a major major throwback: Craig Reynolds' Boids

I believe that this project is one of the earliest examples of effective complexity in digital and generative art - where simple rules create a generative behaviour that ends up becoming a good mixture between order and disorder. In the spectrum between total order and total randomness, I believe that this sits somewhere in the middle, which is what makes it so effective. Though this slide can be adjusted depending on the number of boids in the system, and the complexity of the rules being followed.

The Problem of Postmodernity

This problem hits closest to home for me, as someone who is an acute observer/believer in the postmodern phenomenon. The shift away from modernist top-down approaches (where the whole must resemble the parts, or the theory of composition) to the postmodern beliefs of bottom-up generation, complex systems and chaos theory hold more truth in the contemporary world, especially one where technology plays such a decisive role. The world unfortunately just is not that coherent anymore.

casher-Scope

Since I am still new to Javascript/p5.js, I didn't totally understand what the code in the template meant when I began this assignment. As a result, most of my design is from experimentation. I started out by writing some groups of statements similar to the template's, and then I just started changing values of variables and parameters, testing the limits of the application. I spent a long time perfecting everything so it looked aesthetic.

There's not much else to explain, but I love the way it turned out! And I am pleased with the effort I put in because now I definitely better understand how the code works.

/*
// Template for KidzLabs/4M/Toysmith Animation Praxinoscope
// https://www.amazon.com/4M-3474-Animation-Praxinoscope/dp/B000P02HYC
// https://www.walmart.com/ip/Animation-Praxinoscope-Science-Kits-by-Toysmith-3474/45681503
// Developed for p5.js, September 2018 * Golan Levin 
 
*/
 
var bRecordingPNG = false;
 
var inch = 72.0; 
var diamArtInner = inch * 1.50; 
var diamArtOuter = inch * 4.80; 
var diamCutInner = inch * 1.41; 
var diamCutOuter = inch * 4.875; 
var holeDy = inch * 0.23;
var holeDx = inch * 0.20;
var holeD = inch * 0.1;
 
var nFrames = 10; 
var myFrameCount = 0;
var exportFrameCount = 0; 
var bAnimate = true; 
var bExportFrameImages = false;
 
//-------------------------------------------------------
function setup() {
  createCanvas(792, 612); // 11x8.5" at 72DPI
  frameRate(20);
  smooth();
} 
 
 
//-------------------------------------------------------
function draw() {
  background(255); 
 
  // Do all the drawing. 
  push(); 
  translate(width/2, height/2);
  drawCutLines(); 
  //drawGuides(); 
  drawAllFrames();
  pop();
 
 
  if (bRecordingPNG) {
    saveCanvas('myPraxinoscope.png', 'png');
    bRecordingPNG = false;
  }
}
 
 
//-------------------------------------------------------
function keyPressed() {
  switch (key) {
  case ' ': 
    // Press spacebar to pause/unpause the animation. 
    bAnimate = !bAnimate;
    break;
 
  case 'p': 
  case 'P':
    // Press 'p' to export a PNG for the Praxinoscope.
    bRecordingPNG = true; 
    break;
 
  case 'f':
    case 'F':
      // Press 'f' to export multiple frames 
      // (in order to make an animated .GIF)
      // such as with http://gifmaker.me/
      myFrameCount = 0;
      exportFrameCount = 0;
      bExportFrameImages = true;
      bAnimate = true;
 
      if (bExportFrameImages) {
        var recordFramerate = 1.0;
        var recordDuration = nFrames * 1.01; 
        frameRate(recordFramerate); // 1 FPS baby
        saveFrames('Praxinoscope_', 'png', recordDuration, recordFramerate); 
        bExportFrameImages = false;
      }
 
      break;
  }
}
 
//-------------------------------------------------------
function drawCutLines() {
  fill(0); 
  textAlign(CENTER, BOTTOM); 
  text("Praxinoscope Template", 0, 0-diamCutOuter/2-6); 
 
  stroke(0); 
  strokeWeight(1.0);
 
  noFill(); 
  if (!bRecordingPNG) {
    fill(255); 
  }
  ellipse(0, 0, diamCutOuter, diamCutOuter);
 
  noFill(); 
  if (!bRecordingPNG) {
    fill(240); 
  }
  ellipse(0, 0, diamCutInner, diamCutInner);
 
  noFill(); 
  ellipse(diamCutOuter/2 - holeDx, 0-holeDy, holeD, holeD); 
 
  line (diamCutInner/2, 0, diamCutOuter/2, 0);
}
 
//-------------------------------------------------------
function drawGuides() {
  // This function draws the guidelines. 
  // Don't draw these when we're exporting the PNG. 
  if (!bRecordingPNG) {
 
    noFill(); 
    stroke(128); 
    strokeWeight(0.2); 
    ellipse(0, 0, diamArtInner, diamArtInner); 
    ellipse(0, 0, diamArtOuter, diamArtOuter);
 
    for (var i=0; i&lt;nFrames; i++) {
      var angle = map(i, 0, nFrames, 0, TWO_PI); 
      var pxi = diamArtInner/2 * cos(angle);
      var pyi = diamArtInner/2 * sin(angle);
      var pxo = diamArtOuter/2 * cos(angle);
      var pyo = diamArtOuter/2 * sin(angle);
      stroke(128); 
      strokeWeight(0.2);
      line (pxi, pyi, pxo, pyo);
    }
 
    // Draw the red wedge outline, highlighting the main view.
    var redWedge = 7; // assuming nFrames = 10
    for (var i=redWedge; i&lt;=(redWedge+1); i++) {
      var angle = map(i, 0, nFrames, 0, TWO_PI); 
      var pxi = diamArtInner/2 * cos(angle);
      var pyi = diamArtInner/2 * sin(angle);
      var pxo = diamArtOuter/2 * cos(angle);
      var pyo = diamArtOuter/2 * sin(angle);
      stroke(255, 0, 0); 
      strokeWeight(2.0);
      line (pxi, pyi, pxo, pyo);
    }
    noFill(); 
    stroke(255, 0, 0); 
    strokeWeight(2.0);
    var startAngle = redWedge*TWO_PI/nFrames;
    var endAngle = (redWedge+1)*TWO_PI/nFrames;
    arc(0, 0, diamArtInner, diamArtInner, startAngle, endAngle); 
    arc(0, 0, diamArtOuter, diamArtOuter, startAngle, endAngle); 
 
 
    for (var i=0; i&lt;nFrames; i++) {
      var angle = map(i, 0, nFrames, 0, TWO_PI); 
 
      push();
      rotate(angle); 
      var originY = ((diamArtOuter + diamArtInner)/2)/2;
      translate(0, 0-originY); 
 
      noFill(); 
      stroke(128); 
      strokeWeight(0.2);
      line (-inch/2, 0, inch/2, 0); 
      line (0, -inch/2, 0, inch/2); 
 
      pop();
    }
  }
}
 
//-------------------------------------------------------
function drawAllFrames() {
  for (var i=0; i&lt;nFrames; i++) {
    var angle = map(i, 0, nFrames, 0, TWO_PI); 
    var originY = ((diamArtOuter + diamArtInner)/2)/2;
 
    push();
    rotate(angle); 
    translate(0, 0-originY); 
    scale(0.8, 0.8); // feel free to ditch this 
 
    var whichFrame = i; 
    if (bAnimate) {
      whichFrame = (i+myFrameCount)%nFrames;
    }
    drawArtFrame (whichFrame); 
    // drawArtFrameAlternate (whichFrame); 
 
    pop();
  }
  myFrameCount++;
}
 
 
//-------------------------------------------------------
function drawArtFrame ( whichFrame ) { 
 
  //ellipses
  fill(random(0, 255), random(150,200), random(200,230));
  stroke(255);
  strokeWeight(2);
  for (var i = 0; i &lt; nFrames; i++)
  {
    var t = map(whichFrame, 1, nFrames, 1, i*.38);
  	var diam = map(cos(t*TWO_PI*.22), -1, 1, -i*8, i*8);
  	ellipse(0,i*7-10,diam*.7,diam*0.4);
  }
 
  //lines
  p1 = -45;
  p2 = 70;
  p3 = -0;
  p4 = 54;
  stroke(100);
  strokeWeight(1.2);
  line2 = map(whichFrame, -1, 1, -50, 50);
  for (var i = 0; i &lt; nFrames; i++) {
    line(p1+8*whichFrame,p2,p3,p4);
  }
 
  //triangles
  var p1x = 50;
  var p1y = -25;
  var p2x = 60;
  var p2y = -60;
  var p3x = -40;
  var p3y = -10;
  strokeWeight(2);
  stroke(255);
  for (var i = 0; i &lt; nFrames; i++) {
    fill(25*whichFrame);
    triangle(p1x/(whichFrame*-1)-10, p1y-40, p2x/(whichFrame**2)+20, p2y+20, p3x+whichFrame*10, p3y);
    fill(30*whichFrame);
    triangle(p1x/(whichFrame*2)-3, p1y-30, p2x/(whichFrame)+20, p2y+20, p3x+whichFrame*-2+15, p3y);
  }
 
  //circles
  var p1 = -50;
  var p2 = 0;
  noFill();
  stroke(50);
  strokeWeight(1);
  for (var i = 0; i &lt; nFrames-4; i+=2) {
    stroke(2**whichFrame);
    ellipse(p1-whichFrame*5, p2+20, 10, 10);
    p1 = 0;
    p2 = 0;
  }
}

rigatoni – Intersections01

via GIPHY

let redraw = true
function setup() {
  createCanvas(400, 400);
}
 
function draw() {
  if(redraw) {
  	background(180);
  	var lineArray = new LineArray(20)
  	lineArray.draw()
    redraw = false
  }
}
 
function mouseClicked() {
	redraw = true
}
 
// CLASS CONSTRUCTORS //
 
function LineArray(lineCount) {
	this.lines = []
  for(i=0; i<lineCount; i++) {
		this.lines[i] = new Line(random(width), random(width), random(width), random(width))
  }
 
  this.draw = function(displayIntersections=false) {
		for(i=0; i<lineCount; i++) {
			this.lines[i].draw()
      for(j=0; j<i; j++) {
				var intersection = getIntersection(this.lines[j], 
                                           this.lines[i])
        fill(0,0,0)
        if(isWithinSegment(intersection, this.lines[i]) &&
           isWithinSegment(intersection, this.lines[j])) {
        	ellipse(intersection[0], intersection[1], 5, 5)  
        }
      }
    }
  }
}
 
function Line(x1, y1, x2, y2) {
	this.x1=x1
  this.y1=y1
  this.x2=x2
  this.y2=y2
 
  this.draw = function() {
    stroke(5)
		line(this.x1, this.y1, this.x2, this.y2) 
  }
}
 
//HELPER METHODS //
 
// All the calculations here were informed by Paul Bourke's work
// http://paulbourke.net/geometry/pointlineplane/
function getIntersection(line1, line2) {
	var coef = (line2.x2-line2.x1)*(line1.y1-line2.y1)
  coef -= (line2.y2-line2.y1)*(line1.x1-line2.x1)
  var denom = (line2.y2-line2.y1)*(line1.x2-line1.x1)
  denom -= (line2.x2-line2.x1)*(line1.y2-line1.y1)
	coef = coef/denom
 
  var x = line1.x1 + coef*(line1.x2-line1.x1)
  var y = line1.y1 + coef*(line1.y2-line1.y1)
  var intersection = [x,y]
  return intersection
}
 
// I learnt how to do cross & dot products from here: 
// https://stackoverflow.com/questions/328107
// heavily based on the pseudocode written by user Cyrille Ka
function isWithinSegment(p, seg) {
	let x1 = min(seg.x1, seg.x2)
  let y1 = min(seg.y1, seg.y2)
  let x2 = max(seg.x1, seg.x2)
  let y2 = max(seg.y1, seg.y2)
 
  if((p[0]>=x1 && p[0]<=x2 && p[1]>=y1 && p[1]<=y2)==false) {
    return false
  }
  return true 
}

sapeck-IterationExercise

/* Sapeck    9/6/2017
"sapeck-IterationExercise"
60-212                        Carnegie Mellon University
Copyright (C) 2018-present  Sapeck
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*/
 
// Based on starter Code for "Embedded Iteration + Randomness"
var boolDoRefresh;
 
function setup() {
  createCanvas(400, 400)
  boolDoRefresh = true
}
 
function draw() {
  if (boolDoRefresh) {
    background(255);
 
		var rows = width/50
		var cols = width/50
 
		for (var i=0;i<rows;i++) {
			for (var j=0;j<cols;j++) {
				if (random(50) < 5) {
					fill(color(255,0,0))
					ellipse((i*50) + (45/2) + 2.5, (j*50) + (45/2) + 2.5, 45, 45)
				} else {
					fill(color(0,255,0))
					rect(i*50 + 2.5, j*50 + 2.5, 45, 45)
				}
			}
		}
 
    boolDoRefresh = false
  }
}
 
function mousePressed() {
  boolDoRefresh = true
}