sketch
/* Evan Stuhlfire
* estuhlfi@andrew.cmu.edu Section B
* Project-07: Composition with Curves
* Hypocycloid Spirograph */
var rAngle = 30; // rotation angle
var rotation = 0; // degrees of rotation
var maxShapes = 8;
var shape = []; // array of shapes
function setup() {
createCanvas(480, 480);
background(250);
// fill array of objects with default shapes
createShapes();
}
function draw() {
// redraw background
background(250);
stroke(200);
// map the mouseX position to a circle for rotation radians
var xRot = map(mouseX, 0, width, 0, TWO_PI);
// map the mouseY to half the height to size the shape
var my = map(mouseY, 0, height, 50, height/2);
// r is based on the y position and becomes the radius of the circle
var r = constrain(my, 100, height/2 - 75);
// add text to canvas
addText();
push(); // store settings
// translate origin to center
translate(width/2, height/2);
// draw the small circles and their curves
for(var s = 0; s < shape.length; s++) {
drawSmallShapes(s, height/2 - 25);
}
// rotate the canvas based on the mouseX position
rotate(xRot);
circle(0, 0, 2 * r);
// loop over array of shape objects
for(var s = 0; s < shape.length; s++) {
// drawSmallShapes(s, maxr);
// reset degrees of rotation for each shape
rotation = 0;
if(shape[s].on == true){
// draw the curve in spirograph, 4 times for curve rotation
for(var i = 0; i < 4; i++) {
// rotation canvas, start at 0
rotate(radians(rotation));
rotation = rAngle;
drawCurves(shape[s].verts, r, shape[s].color);
}
}
}
pop(); // restore settings
}
function mousePressed() {
// when a shape is clicked it is added or removed
// from the spirograph, toggles like a button
// map the mouse position to the translated canvas
var mx = map(mouseX, 0, width, -240, 240);
var my = map(mouseY, 0, height, -240, 240);
// loop through shapes to see if clicked
for(var i = 0; i < shape.length; i++) {
var d = dist(shape[i].px, shape[i].py, mx, my);
// check distance mouse click from center of a shape
if(d <= shape[i].radius){
// if on, set to false
if(shape[i].on == true) {
shape[i].on = false;
} else {
// if on = false, set true
shape[i].on = true;
}
}
}
}
function addText() {
// Add text to canvas
fill(shape[0].colorB);
strokeWeight(.1);
textAlign(CENTER, CENTER);
// title at top
textSize(20);
text("Hypocycloid", width/2, 8);
text("Spirograph", width/2, 30);
// directions at bottom
textSize(10);
text("Click the circles to toggle curves on or off. Reload for different pen colors.",
width/2, height - 5);
noFill();
strokeWeight(1);
}
function createShapes() {
var vCount = 3; // start shapes with 3 verticies
var sOn = true; // default first shape to show in spirograph
var angle = 30;
var shapeRad = 35;
// create array of shape objects
for(var i = 0; i < maxShapes; i++) {
shape[i] = new Object();
// set object values
shape[i].verts = vCount;
// generate random color Bright and Dull for each
var r = random(255);
var g = random(255);
var b = random(255);
shape[i].colorB = color(r, g, b, 255); // Bright color
shape[i].colorM = color(r, g, b, 80); // Muted faded color
shape[i].color = shape[i].colorM;
shape[i].angle = angle;
shape[i].px = 0;
shape[i].px = 0;
shape[i].radius = shapeRad;
shape[i].on = sOn;
// default shapes to not display in spirograph
sOn = false;
vCount++;
angle += 30;
if (angle == 90 || angle == 180 || angle == 270) {
angle += 30;
}
}
}
function drawSmallShapes(s, r) {
// calculate the parametric x and y
var px = r * cos(radians(shape[s].angle));
var py = r * sin(radians(shape[s].angle));
shape[s].px = px;
shape[s].py = py;
// map the mouse position to the translated canvas
var mx = map(mouseX, 0, width, -240, 240);
var my = map(mouseY, 0, height, -240, 240);
// check if mouse is hovering over small circle
var d = dist(shape[s].px, shape[s].py, mx, my);
if(d <= shape[s].radius) {
// hovering, make bigger, make brighter
var hover = 1.25;
shape[s].color = shape[s].colorB;
} else {
// not hovering, make normal size, mute color
var hover = 1;
shape[s].color = shape[s].colorM;
}
// check if shape is appearing in spriograph
if(shape[s].on == true) {
var c = shape[s].colorB; // bright
} else {
var c = shape[s].colorM; // muted
}
push();
// move origin to little circle center
translate(px, py);
stroke(c); // set color from object
strokeWeight(2);
circle(0, 0, shape[s].radius * 2 * hover); // draw little circle
// draw the curve in the little circle
drawCurves(shape[s].verts, shape[s].radius * hover, c)
pop();
}
function drawCurves(n, r, c) {
// n = number of shape verticies, r = radius, c = color
// number of vertices for drawing with beginShape
var nPoints = 50;
beginShape();
for(var i = 0; i < nPoints; i++) {
// map the number of points to a circle
var t = map(i, 0, nPoints, 0, TWO_PI); // t = theta
// fit the shape to the radius of the circle
var value = r/n;
// calculate hypocycloid at a different number of cusps.
var px = value * ((n - 1) * cos(t) - cos((n - 1) * t));
var py = value * ((n - 1) * sin(t) + sin((n - 1) * t));
vertex(px, py); // add a vertex to the shape
}
noFill();
stroke(c);
strokeWeight(1);
endShape(CLOSE); // close shape
}