Project-07: Hypocycloid Spirograph

Sketch
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
}

Moritz Stefaner’s Impfdashbord

Looking Outwards 07: Information Visualization

Alexia Forsyth

Moritz Stefaner is an independent designer and consultant that specializes in data visualization. She is dedicated to helping organizations develop research and data into something beautiful. I really admire the practicality of Stefaner’s work. This type of visualization seems like a useful skill to have in any career path. Her Impfdashbord describes Germany’s Covid-19 vaccine information. She prioritizes a mobile-first design approach, avoiding the common chart formats. As part of her design she even added a “vaccination clock” to help viewers visualize the  pace of vaccination. Her dashboard is really creative and seems to genuinely make an impact in how Germany, and world viewers see the success of covid vaccines. She uses social media preview images and utilizes favicon and puppeteer to generate a cohesive set of images that accurately represent the data

link: https://truth-and-beauty.net/projects/impfdashboard

Looking Outward 07

Rachel Binx, CA Coastline Ring, 2016

For this week’s Looking Outward, I looked at Rachel Binx’s CA Coastline Ring. In order to create the work, she appropriated the outline of the California coast, wrapped it into a circle, and used that pattern to create a ring. I find this so interesting because I have an intense curiosity about the interaction between the natural world and artistic practice as a whole: whether this be in medium, subject, or otherwise. I found it clever how Binx decided to cast the ring in gold, considering the history of the gold rush in California. I think that this is a great example of the way in which content can dictate form, and vice versa. In terms of its value as a visualization tool, I’m not sure if it would be recognizable as the coast of California to the average person. That being said, as a personal memento, or even as an object to someone with prior knowledge, I can see the ways in which this ring would more than successfully serve its owner. This unconventional application of the natural world without a doubt peaks my interest. 

Gold CA Coast Ring

Project 7 Curves

I connected mouseX and mouseY to the mapping of t to visualize the continuous movement of the curves and the larger shapes they create. It is interesting as you can watch it wind itself, giving a Spirograph effect. I then connected mouseX to a of the ranunculoid which affects the scaling from left to right. It is interesting how both x and y on the canvas determines the complexity of the shapes created. I also had fun experimenting with the different ways the shapes could be shown and decided to have the hypocycloid made of small squares.

project7
// Rachel Legg / rlegg / Section C

//# of point of each shape
var nPoints = 150;

function setup() {
    createCanvas(480, 480);
    frameRate(10);
}

function draw() {
    //draw the ranunculoid & astroid curve
    background(0, 38, 38);    //dark green-blue
    fill(255);
    push();
    translate(width/2, height/2);
    fill(121, 33, 250);        //purple
    drawRanunculoid();
    noFill();
    drawHypocycloid();
    scale(1/12);
    fill(105, 20, 235);        //darker purple
    drawRanunculoid();
    pop();

}

function drawRanunculoid() {
    //Ranunculoid : https://mathworld.wolfram.com/Ranunculoid.html

    var x;
    var y;

    var a = 30;        

    stroke(149, 198, 35);      //green
    strokeWeight(1);
    beginShape();
    for(var i = 0; i < nPoints; i++) {
        //map points to mouseX & mouseY for continuous flow
        var t = map(i, 0, nPoints, mouseX, mouseY); 
        a += mouseX / 500;                    //scale when mouseX changes
        
        //adjust to 7 to increase petals to 6
        x = (a * (7 * cos(t) - cos(7 * t)));
        y = (a * (7 * sin(t) - sin(7 * t)));
        vertex(x, y);                           //vertex gets spinograph effect
        //line(x - 5, y - 5, 10, 10);           //visually different. shows lines to center

    }
    endShape(CLOSE);
}

function drawHypocycloid() {
    //https://mathworld.wolfram.com/Hypocycloid.html

    var x;
    var y;
    

    var a = 110;        
    var b = 30;

    noFill();
    stroke(212, 223, 158);     //light green
    strokeWeight(1);
    for(var i = 0; i < nPoints; i++){
        //map points to mouseX & mouseY for continuous change
        var t = map(i, 0, nPoints, mouseX, mouseY);  

        x = ((a - b) * cos(t)) - (b * cos(t * ((a - b) / b)));
        y = ((a - b) * sin(t)) - (b * sin(t * ((a - b) / b)));
        //rectangle shapes to show patterns
        rect(x - 5, y - 5, 10, 10);
    }
}





cabspotting SF

animation of realtime GPS data from taxis in San Francisco

Cabspotting SF was created by Stamen Design for NYMoMA, originally as part of a research project called Invisible Dynamics (sponsored by the SF Exploratorium). This project was one of the first uses of realtime GPS data in data visualization. The movement of taxis is represented frame by frame as an array of semi-translucent yellow dots, which we can see intersect and densify in higher traffic areas. The concept is relatively simple, but the visualization is quite encapsulating (especially given that this project was done in 2008). The route of a single taxi can be singled out or compiled with the movement of all the other taxis at a given time and overlaid on a map that seems to glow. Stamen Design does many projects with a local focus, specializing in data visualization and cartography. They value aesthetics as well as thorough research, which is quite apparent in Cabspotting SF.

static map of taxi routes in San Francisco (2008)
compiled taxi movement

rose curve + augmented hypocycloid

mouseX rotates and changes the size of the hypocycloid (thicker lines, filled shape). mouseY corresponds with n, which essentially determines the complexity of the curves. it took me a while to finish this because I was having too much fun spacing out while playing with it…

sketch
// jaden luscher
// jluscher@andrew.cmu.edu
// section a
// project 07: composition with curves
// HYPOCYCLOID PEDAL CURVE + ROSE CURVE

// INITIALIZING VARIABLES
var nPoints = 250;
var n;
var a;
var q;

function setup() {
  createCanvas(400, 400);
  background(200);

  frameRate(30);

}

function draw() {
  background("orange");
  noFill();

  q = constrain((mouseX / width), 0.1, 1.0);
  p = constrain((mouseY / height), 0.1, 1.0);
  n = int(p * 100);   // n corresponds to the curves' complexity
  a = int(width * p * 10);    // "radius" of rose curve increades with mouseY
  var c = 155 + (q * 100);  // fill color for hypocycloid

  translate (width/2, height/2);
  strokeWeight(q *20);    // stroke of hypocycloid corresponds to mouseX
  stroke("white");
  fill(c, 0, c, 5);
  for (i = 0; i < nPoints/10; i++) {
    drawHypocycloid();
    a = a *sqrt(q) - width*q;   // sqaure root causes many curve sizes to "cross"
  }
  a = int(width * p * 10);    // "radius" of rose curve increades with mouseY
  stroke("white");
  strokeWeight(0.5);
  rotate(PI * q);   // rose curve spins with mouseX
  drawRoseCurve();
}


function drawHypocycloid() {
  // hypocycloid pedal curve:
  // https://mathworld.wolfram.com/HypocycloidPedalCurve.html
  push();

  var x;
  var y;
  var r;

  beginShape();
  for (var i = 0; i < nPoints; i++) {
    var t = map(i, 0, nPoints, 0, TWO_PI);// sweep theta from 0 to two pi

    // hypocycloid:
    r = q * (n-2) * ((sin * (n / (n-2))) * (t + PI/2));

    x = a * (((n-1) * cos(t) + cos((n-1) * t)) / n);
    y = a * (((n-1) * sin(t) + sin((n-1) * t)) / n);
    vertex(x, y);
  }
  endShape(CLOSE);
  pop();
}


function drawRoseCurve() {
  // rose curve
  // https://mathworld.wolfram.com/RoseCurve.html

  push();
  var x;
  var y;
  var r;

  beginShape();
  for (var i = 0; i < nPoints; i++) {
    var t = map(i, 0, nPoints, 0, TWO_PI);// sweep theta from 0 to two pi
    r = a * p  * cos(n * t);

// pasted from hypercycloid
    x = r * cos(t);
    y = r * sin(t);
    vertex(x, y);
  }
  endShape(CLOSE);
  pop();

}

Looking Outward – 07

Project Ukko visualizes wind patterns and helps predict future patterns, creating a tool for clean wind energy industry and the fight against climate change.

Project Ukko visualizes and helps predict future wind patterns which can be helpful in clean energy endeavors in the wind farm industry. Moritz Stefaner, a designer and researcher who focuses on data visualization, encoded prediction skills into visuals in Project Ukko to make this data accessible to others. Opacity represents precision accuracy, wind strength connects to line thickness, and trends of wind speed change with line tilt and color. The color, visual language, and design is really well done and very user-centered, making it easy for anyone to begin understanding and deciphering patterns. I appreciate that this gives experts a practical tool, along with sharing new techniques into how to share data. This browser allows people to interact and find out how the wind will change over the next few months. I really admire this project because it makes information more accessible to people, along with considering how code can be used in the fight against climate change.

Title: Project Ukko
Artist: Moritz Stefaner
Link: https://www.project-ukko.net
https://truth-and-beauty.net/projects/ukko

Looking Outwards 07: Information Visualization

Wind Map is a live visualization of the current direction and wind speeds across the United States. The wind vectors combine to appear almost as smoke churning at different speeds across the country. This project appeals to me visually because it captures the idea of constant change. It can at times be a peaceful flow and at other times it appears agitated and angry. It can appear both ways on the same map depending on where attention is focused.

This project is not limited to weather. It is aesthetically pleasing and useful. It is beautiful and intriguing enough to be on display at the MoMA. At the same time, diverse groups have found its data useful. Birdwatchers have used it to track migration patterns, and cyclists have used it to plan trips. Conspiracy theorists have even used it to track “chemtrails”. This visualization allows this vast, changing dataset to be useful in ways that are impossible with the raw data.

The underlying data for the Wind Map is from the National Digital Forecast Database which provides near term forecasts that are updated once per hour. The visualization was created by Fernanda Viégas and Martin Wattenberg, engineers from the visualization research lab at Google. The entire project is written in html and javascript. A live link to the Wind Map can be found here.

Looking Outwards 07

This week I looked at CMU alum Chris Harrison’s project Search Clock. He created an algorithm that mapped what people use the internet for the most during every hour of the day. This stood out to me because we just made clock looking devices with code, this is just way cooler. I also have just spent the semester in an intense mapping class and am impressed by his visual simplification and efficiency in descriving multiple changing factors so consicely. He somehow found hourly data on average time spent on certain websites and generalized them into catagories, and converted the scale to years. I think it’s super visually stunning and am impressed with how innovative this representation of data is.

https://www.chrisharrison.net/index.php/Visualizations/SearchClock

Project 07: Hallucinogenic Roses!

sketchDownload
// Ilia Urgen
// Section B
// iurgen@andrew.cmu.edu
// Project-07

function setup() {
  createCanvas(480, 480);
}
 
function draw() {

    color_1 = color (189,17,89);
    color_2 = color (253,163,26);

    // ombre 
    for (var y = 0; y < height; y++ ) {
        n = map (y,0, height, 0, 1);
        var color_3 = lerpColor (color_1, color_2, n);
        stroke (color_3);
        line (0, y, width, y);
    }

    noFill();
    stroke (0);
    strokeWeight (0.25);

    // canvas border lines
    line (1,1,1,width - 1);
    line (1,1,width - 1,1);
    line (1,width - 1,width - 1,width - 1);
    line (width - 1,1,width - 1,width - 1);
  
    // smallest possible x-value of drawing
    var x = min (mouseX, width);
  
    // link the curve scale to mouseX
    var a0 = map (x, 0, width, 0, 50);
    var b0 = map (mouseX, 0, width, 0, 100);

    var a1 = map (x, 0, width, 0, 200);
    var b1 = map (mouseX, 0, width, 0, 300);

    var a2 = map (x, 0, width, 0, 400);
    var b2 = map (mouseX, 0, width, 0, 800);

    var a3 = map (x, 0, width, 0, 900);
    var b3 = map (mouseX, 0, width, 0, 1800);

    var a4 = map (x, 0, width, 0, 1900);
    var b4 = map (mouseX, 0, width, 0, 3800);

    var a5 = map (x, 0, width, 0, 3900);
    var b5 = map (mouseX, 0, width, 0, 7800);

    var a6 = map (x, 0, width, 0, 7900);
    var b6 = map (mouseX, 0, width, 0, 15800);

    var a7 = map (x, 0, width, 0, 15900);
    var b7 = map (mouseX, 0, width, 0, 31800);
  
    // link the curve rotation to mouseY
    var degree = map (mouseY, 0, height, 0, 360);
  
    push();
    translate (width/2, height/2);
  
    for (var j = 0; j < degree; j += 3600 / degree) {
        
        rotate (720 / degree);
        beginShape();
        curveVertex (0, 0);
        
        // draw the complete curves 
        for (var i = -10; i < 10; i += 0.3) {
            var x0 = a0 * cos (i);
            var y0 = b0 * sin (i);
            curveVertex (x0, y0);
        }

        for (var i = -10; i < 10; i += 0.3) {
            var x1 = a1 * cos (i);
            var y1 = b1 * sin (i);
            curveVertex (x1, y1);
        }

        for (var i = -10; i < 10; i += 0.3) {
            var x2 = a2 * cos (i);
            var y2 = b2 * sin (i);
            curveVertex (x2, y2);
        }

        for (var i = -10; i < 10; i += 0.3) {
            var x3 = a3 * cos (i);
            var y3 = b3 * sin (i);
            curveVertex (x3, y3);
        }

        for (var i = -10; i < 10; i += 0.3) {
            var x4 = a4 * cos (i);
            var y4 = b4 * sin (i);
            curveVertex (x4, y4);
        } 

        for (var i = -10; i < 10; i += 0.3) {
            var x5 = a5 * cos (i);
            var y5 = b5 * sin (i);
            curveVertex (x5, y5);
        }

        for (var i = -10; i < 10; i += 0.3) {
            var x6 = a6 * cos (i);
            var y6 = b6 * sin (i);
            curveVertex (x6, y6);
        }

        for (var i = -10; i < 10; i += 0.3) {
            var x7 = a7 * cos (i);
            var y7 = b7 * sin (i);
            curveVertex (x7, y7);
        }
        
        curveVertex (0, 0);
        endShape();

    }

    pop();
    
    // consistent background lines
    line (0, 0, width, height);
    line (0, width, height, 0);

    line (width/2, 0, width/2, height);
    line (0, height/2, width, height/2)

}