svitoora – 07 – Squircle

sketch

// 
// Supawat Vitoorapakorn
// Svitoora@andrew.cmu.edu
// Section E
// 
// Squircle
// Equation from:
// https://en.wikipedia.org/wiki/Superellipse

w = 480;
h = 480;

//Signum function
function sgn(x) {
	if (x > 0) {
		return 1
	}
	if (x == 0) {
		return 0
	}
	if (x < 1) {
		return -1
	}
}

//------------------------

var SQUIRCLE = []; // Array for nodes
var theta;
var a = w * .33 // Squircle width
var b = h * .33 // Squircle height
var n = 3.5 // roundness

// Create nodes for squircle
function create_squircle(x, y) {
	this.x = x;
	this.y = y;
}

// Calculate Squircle's X position from theta
function squircle_x() {
	angleMode(DEGREES);
	return abs(cos(theta)) ** (2 / n) * a * sgn(cos(theta))
}

// Calculate Squircle's Y position rom theta
function squircle_y() {
	angleMode(DEGREES);
	return abs(sin(theta)) ** (2 / n) * b * sgn(sin(theta))
}

// Create Squircle based on an interval of theta
// and push its node into an array
function make_squircle() {
	angleMode(DEGREES);
	//Plot every interval degree of squircle
	interval = 1;
	for (theta = 0; theta < 360; theta += interval) {
		x = squircle_x(theta);
		y = squircle_y(theta);
		SQUIRCLE.push(new create_squircle(x, y))
	}
}

var gon = 60;
var min_gon = 45;
var max_gon = 160;
var SWITCH = 1;
// Create a bounce loop animation from drawing lines
function gon_loop() {
	print(SQUIRCLE.length)
	if (gon == max_gon) {
		SWITCH = -1;
		print("inversing");
	};
	if (gon == min_gon) {
		SWITCH = 1;
	}
	gon += SWITCH;
}

// Draws Squircle from array
function draw_squircle() {
	//Draw Shape

	if (inverse == 1) {
		fill(255);
	} else {
		fill(0);
	}
	beginShape();

	strokeWeight(1);
	for (i in SQUIRCLE) {
		x = SQUIRCLE[i].x
		y = SQUIRCLE[i].y
		curveVertex(x, y);
	}
	//Force Close Shape
	curveVertex(SQUIRCLE[5].x, SQUIRCLE[5].y);
	endShape(CLOSE);
	connect_lines();
}

// Connect a point in squircle to every point
function connect_lines() {
	//Add lines
	if (inverse == 1) {
		stroke(0, 0, 0, 255 * .1);
	} else {
		stroke(255, 255, 255, 255 * .1)
	}
	for (i in SQUIRCLE) {
		x_0 = SQUIRCLE[i].x
		y_0 = SQUIRCLE[i].y
		for (i in SQUIRCLE) {
			if (i % (gon) == 0) {
				// %gon used to skip certains nodes
				// to not overload the computer
				x_1 = SQUIRCLE[i].x
				y_1 = SQUIRCLE[i].y
				line(x_0, y_0, x_1, y_1);
			}
		}
	}
}

//------------------------

function setup() {
	createCanvas(w, h);
	background(255 * .75);
	make_squircle();
	print(SQUIRCLE.length / 2, SQUIRCLE.length / 6)
}

//------------------------

// Use mouseX to control the roundeness of squircle
function control_roundness() {
	mousePos = (abs(mouseX - (w / 2)) / (w / 2));
	mousePos = constrain(mousePos, 0, 1);
	roundness = map(mousePos, 0, 1, .2, 10);
	n = roundness;
	SQUIRCLE.length = 0;
	make_squircle();
}

//Use mouseY to control size of squircle
function control_size() {
	mousePos = (abs(mouseY - (h / 2)) / (h / 2));
	mousePos = constrain(mousePos, 0, 1);
	size = map(mousePos, 0, 1.2, .175, .35);
	a = w * size;
	b = h * size;
	SQUIRCLE.length = 0;
	make_squircle();
}

function mouseClicked() {
	inverse = inverse * -1;
}

//------------------------

var inverse = 1; // incerts color
function draw() {
	if (inverse == 1) {
		background(255 * .8);
	} else {
		background(255 * .1)
	}
	control_roundness();
	control_size();
	gon_loop(); //bounce animation for connecting line

	//Draw Closed Shape
	push()
	translate((w / 2), (h / 2)) // center drawing
	draw_squircle();
	pop()
}

Squircle

For my equation, I picked the superellipse equation which is capable of creating a squircle. I stumbled upon this equation on MathWorld:

\left|{\frac {x}{a}}\right|^{n}\!+\left|{\frac {y}{b}}\right|^{n}\!=1,

Since this equation is not particularly helpful, I went on Wikipedia and found the parametric equation for the superellipse:

{\displaystyle {\begin{aligned}x\left(t\right)&={|\cos t|}^{\frac {2}{m}}\cdot a\operatorname {sgn}(\cos t)\\y\left(t\right)&={|\sin t|}^{\frac {2}{n}}\cdot b\operatorname {sgn}(\sin t)\end{aligned}}}

At first, I was a bit confused about what the signum sgn() function is, but after some googling, I understood it and was able to replicate the function easily in javascript. Initially, I thought about  doing a spirograph whereby a pen would rotate around the moving point and generate a drawing with variable orbital roundness:

Initial Idea sketched on Desmos.

Variable Density

After re-building my equation in javascript, I found that the curve was denser in some area than others. This I believe is caused by the way that I parametrically constructed this curve via iterating from 0 to 360˚ and pushing the nodes into an array:

Notice how the nodes are denser near the corners.

Had this been constructed through Calculus, these variable densities wouldn’t occur. But since these variable densities existed, I decided to take advantage of it use it as nodes to do a string art. These are the results:

Leave a Reply