/* 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
}
Month: October 2022
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.
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.
// 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
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.
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…
// 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 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!
// 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)
}