{"id":74350,"date":"2022-10-14T18:05:29","date_gmt":"2022-10-14T22:05:29","guid":{"rendered":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/?p=74350"},"modified":"2022-10-14T18:05:29","modified_gmt":"2022-10-14T22:05:29","slug":"project-07-hypocycloid-spirograph","status":"publish","type":"post","link":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/2022\/10\/14\/project-07-hypocycloid-spirograph\/","title":{"rendered":"Project-07: Hypocycloid Spirograph"},"content":{"rendered":"<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<html><body><figure class=\"wp-block-image size-full\"><img loading=\"lazy\" width=\"728\" height=\"799\" src=\"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-content\/uploads\/2022\/10\/7.jpg\" alt=\"\" class=\"wp-image-74352\" srcset=\"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-content\/uploads\/2022\/10\/7.jpg 728w, https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-content\/uploads\/2022\/10\/7-273x300.jpg 273w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px\"><figcaption>Sketch<\/figcaption><\/figure><div><a class=\"p5_sketch_link\" href=\"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-content\/uploads\/2022\/10\/sketch-113.js\" data-width=\"480\" data-height=\"480\">sketch<\/a><iframe src=\"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-content\/plugins\/p5-embedder\/p5_iframe.html\" class=\"p5_exampleFrame\" width=\"480\" height=\"480\"><\/iframe><pre class=\"language-javascript\"><code class=\"p5_editor language-javascript\">\/* Evan Stuhlfire\n * estuhlfi@andrew.cmu.edu Section B\n * Project-07: Composition with Curves \n * Hypocycloid Spirograph *\/\n\nvar rAngle = 30; \/\/ rotation angle\nvar rotation = 0; \/\/ degrees of rotation\nvar maxShapes = 8;\nvar shape = []; \/\/ array of shapes\n\nfunction setup() {\n    createCanvas(480, 480);\n    background(250);\n    \/\/ fill array of objects with default shapes\n    createShapes();\n}\n\nfunction draw() {\n     \/\/ redraw background\n    background(250);\n    stroke(200);\n\n    \/\/ map the mouseX position to a circle for rotation radians\n    var xRot = map(mouseX, 0, width, 0, TWO_PI);\n    \/\/ map the mouseY to half the height to size the shape \n    var my = map(mouseY, 0, height, 50, height\/2);\n    \/\/ r is based on the y position and becomes the radius of the circle\n    var r = constrain(my, 100, height\/2 - 75);\n\n    \/\/ add text to canvas\n    addText();\n\n    push(); \/\/ store settings\n    \/\/ translate origin to center\n    translate(width\/2, height\/2);\n\n    \/\/ draw the small circles and their curves\n    for(var s = 0; s &lt; shape.length; s++) {\n        drawSmallShapes(s, height\/2 - 25);\n    }\n\n\n    \/\/ rotate the canvas based on the mouseX position\n    rotate(xRot);\n    circle(0, 0, 2 * r);\n    \/\/ loop over array of shape objects\n    for(var s = 0; s &lt; shape.length; s++) {\n        \/\/ drawSmallShapes(s, maxr);\n        \/\/ reset degrees of rotation for each shape\n        rotation = 0; \n        if(shape[s].on == true){\n            \/\/ draw the curve in spirograph, 4 times for curve rotation\n            for(var i = 0; i &lt; 4; i++) {\n                \/\/ rotation canvas, start at 0\n                rotate(radians(rotation));\n                rotation = rAngle;\n                drawCurves(shape[s].verts, r, shape[s].color);\n            }            \n        }\n    }\n    pop(); \/\/ restore settings\n}\n\nfunction mousePressed() {\n    \/\/ when a shape is clicked it is added or removed\n    \/\/ from the spirograph, toggles like a button\n\n    \/\/ map the mouse position to the translated canvas\n    var mx = map(mouseX, 0, width, -240, 240);\n    var my = map(mouseY, 0, height, -240, 240);\n\n    \/\/ loop through shapes to see if clicked\n    for(var i = 0; i &lt; shape.length; i++) {\n        var d = dist(shape[i].px, shape[i].py, mx, my);\n\n        \/\/ check distance mouse click from center of a shape\n        if(d &lt;= shape[i].radius){\n            \/\/ if on, set to false\n            if(shape[i].on == true) {\n                shape[i].on = false;  \n            } else {\n                \/\/ if on = false, set true\n                shape[i].on = true;\n            }\n        }\n    }\n}\n\nfunction addText() {\n    \/\/ Add text to canvas\n    fill(shape[0].colorB);\n    strokeWeight(.1);\n    textAlign(CENTER, CENTER);\n\n    \/\/ title at top\n    textSize(20);\n    text(\"Hypocycloid\", width\/2, 8);\n    text(\"Spirograph\", width\/2, 30);\n    \/\/ directions at bottom\n    textSize(10);\n    text(\"Click the circles to toggle curves on or off. Reload for different pen colors.\", \n        width\/2, height - 5);\n\n    noFill();\n    strokeWeight(1);\n}\n\nfunction createShapes() {\n    var vCount = 3; \/\/ start shapes with 3 verticies\n    var sOn = true; \/\/ default first shape to show in spirograph\n    var angle = 30;\n    var shapeRad = 35;\n\n    \/\/ create array of shape objects\n    for(var i = 0; i &lt; maxShapes; i++) {\n        shape[i] = new Object();\n\n        \/\/ set object values\n        shape[i].verts = vCount;\n        \/\/ generate random color Bright and Dull for each\n        var r = random(255);\n        var g = random(255);\n        var b = random(255);\n\n        shape[i].colorB = color(r, g, b, 255); \/\/ Bright color\n        shape[i].colorM = color(r, g, b, 80); \/\/ Muted faded color\n        shape[i].color = shape[i].colorM;\n        shape[i].angle = angle;\n        shape[i].px = 0;\n        shape[i].px = 0;\n        shape[i].radius = shapeRad;\n        shape[i].on = sOn;\n\n        \/\/ default shapes to not display in spirograph\n        sOn = false;      \n        vCount++;\n        angle += 30;\n        if (angle == 90 || angle == 180 || angle == 270) {\n            angle += 30;\n        }\n    }\n}\n\nfunction drawSmallShapes(s, r) {\n    \/\/ calculate the parametric x and y\n    var px = r * cos(radians(shape[s].angle));\n    var py = r * sin(radians(shape[s].angle));\n    shape[s].px = px;\n    shape[s].py = py;\n\n    \/\/ map the mouse position to the translated canvas\n    var mx = map(mouseX, 0, width, -240, 240);\n    var my = map(mouseY, 0, height, -240, 240);\n\n    \/\/ check if mouse is hovering over small circle\n    var d = dist(shape[s].px, shape[s].py, mx, my);\n    if(d &lt;= shape[s].radius) {\n        \/\/ hovering, make bigger, make brighter\n        var hover = 1.25;\n        shape[s].color = shape[s].colorB;\n    } else {\n        \/\/ not hovering, make normal size, mute color\n        var hover = 1;\n        shape[s].color = shape[s].colorM;\n    }\n\n    \/\/ check if shape is appearing in spriograph\n    if(shape[s].on == true) {\n        var c = shape[s].colorB; \/\/ bright\n    } else {\n        var c = shape[s].colorM; \/\/ muted\n    }\n\n    push();\n    \/\/ move origin to little circle center\n    translate(px, py);\n    stroke(c); \/\/ set color from object\n    strokeWeight(2);\n\n    circle(0, 0, shape[s].radius * 2 * hover); \/\/ draw little circle\n    \/\/ draw the curve in the little circle\n    drawCurves(shape[s].verts, shape[s].radius * hover, c)\n    pop();\n}\n\nfunction drawCurves(n, r, c) {\n    \/\/ n = number of shape verticies, r = radius, c = color\n    \/\/ number of vertices for drawing with beginShape\n    var nPoints = 50;\n\n    beginShape();\n    for(var i = 0; i &lt; nPoints; i++) {\n        \/\/ map the number of points to a circle\n        var t = map(i, 0, nPoints, 0, TWO_PI); \/\/ t = theta\n        \/\/ fit the shape to the radius of the circle\n        var value = r\/n;\n        \/\/ calculate hypocycloid at a different number of cusps.\n        var px = value * ((n - 1) * cos(t) - cos((n - 1) * t));\n        var py = value * ((n - 1) * sin(t) + sin((n - 1) * t)); \n    \n        vertex(px, py); \/\/ add a vertex to the shape\n    }\n    noFill();\n    stroke(c);\n    strokeWeight(1);\n    endShape(CLOSE); \/\/ close shape\n}\n<\/code><\/pre><\/div><\/body><\/html>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":760,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[108,56,1],"tags":[],"_links":{"self":[{"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/posts\/74350"}],"collection":[{"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/users\/760"}],"replies":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/comments?post=74350"}],"version-history":[{"count":1,"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/posts\/74350\/revisions"}],"predecessor-version":[{"id":74354,"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/posts\/74350\/revisions\/74354"}],"wp:attachment":[{"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/media?parent=74350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/categories?post=74350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/15-104\/f2022\/wp-json\/wp\/v2\/tags?post=74350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}