The following source code is one technique of drawing a basic Harriss spiral using ninety degree Processing arcs. Each successive arc is the previous arc’s width/height divided by 1.325 going from outer to inner. The colored circles show the center point of its respective arc. There are other methods of drawing an arc, none better than Logo’s Turtle that I have been able to find so far. This demo builds the spiral manually without the aid of a loop or recursion.
void setup() {
size(600, 600);
// ======== 1 ========= //
pushMatrix();
translate(312, 370);
noFill();
strokeWeight(16);
arc(0, 0, 243, 243, radians(-45), radians(45));
strokeWeight(1);
circle(0, 0, 10);
fill(0);
text("1", -2, 4);
popMatrix();
// ======== 2 ======== //
pushMatrix();
translate(334, 350);
noFill();
strokeWeight(14);
stroke(11,123,54);
arc(0, 0, 183, 183, radians(225), radians(315));
strokeWeight(1);
circle(0, 0, 10);
fill(0);
text("2", -2, 4);
popMatrix();
// ======== 3 ======= //
pushMatrix();
translate(319, 333);
noFill();
strokeWeight(12);
stroke(0,0,254);
arc(0, 0, 138, 138, radians(135), radians(225));
strokeWeight(1);
circle(0, 0, 10);
fill(0);
text("3", -2, 4);
popMatrix();
// ======== 4 ======= //
pushMatrix();
translate(308, 345);
noFill();
strokeWeight(10);
stroke(234,193,54);
arc(0, 0, 104, 104, radians(45), radians(135));
strokeWeight(1);
circle(0, 0, 10);
fill(0);
text("4", -2, 4);
popMatrix();
// ======== 5 ======= //
pushMatrix();
translate(316, 355);
noFill();
strokeWeight(8);
stroke(179,23,254);
arc(0, 0, 79, 79, radians(-45), radians(45));
strokeWeight(1);
circle(0, 0, 10);
fill(0);
text("5", -2, 4);
popMatrix();
// ======= 6 ======== //
pushMatrix();
translate(322, 349);
noFill();
strokeWeight(6);
stroke(221,223,54);
arc(0, 0, 60, 60, radians(225), radians(315));
strokeWeight(1);
circle(0, 0, 10);
fill(0);
text("6", -2, 4);
popMatrix();
// ======== 7 ======= //
pushMatrix();
translate(317, 342);
noFill();
strokeWeight(4);
stroke(255,0,54);
arc(0, 0, 45, 45, radians(135), radians(225));
strokeWeight(1);
circle(0, 0, 10);
fill(0);
text("7", -2, 4);
popMatrix();
}
Output:
Technique 2:
The following technique uses recursion and an ArrayList of PVectors with a custom arc drawing method to create a Harriss spiral.
ArrayList<PVector> vectors;
int _wndW = 800;
int _wndH = 800;
float x1 = 0;
float y1 = 0;
void drawArc(float x, float y, float radius, int rotation, float penSize, int iteration) {
float angle = 0.0;
rotation += 90;
if (rotation > 315) {
rotation = 45;
}
radius = radius*1.325;
if (iteration > 0) {
iteration -= 1;
PVector v = vectors.get(iteration);
println("[" + iteration + "]" + v.x +" : " + v.y);
pushMatrix();
translate(v.x, v.y);
rotate(radians(rotation));
println("radius = " + radius);
println("rotation = ", rotation);
println("====================");
for ( int i = 0; i < 90; i++ ) {
angle = radians( i );
x = cos( angle ) * radius;
y = sin( angle ) * radius;
stroke(random(255),random(255),random(255));
strokeWeight(penSize);
if ((x>0)&&(y>0)) {
line(x, y, x1, y1);
}
x1 = x;
y1 = y;
}
popMatrix();
drawArc(x, y, radius, rotation, penSize, iteration);
}
}
void setup() {
size(_wndW, _wndH);
background(209);
vectors = new ArrayList<PVector>();
vectors.add(new PVector(290, 430)); // 0 Outside
vectors.add(new PVector(334, 390)); // 1
vectors.add(new PVector(305, 355)); // 2
vectors.add(new PVector(278, 376)); // 3
vectors.add(new PVector(293, 398)); // 4
vectors.add(new PVector(309, 387)); // 5
vectors.add(new PVector(301, 375)); // 6 Inside
for (int i = 0; i < vectors.size(); i++) {
PVector v = vectors.get(i);
println("[" + i + "]" + v.x +" : " + v.y);
}
println(" ================== ");
noLoop(); // uses recursion
}
void draw() {
//draws from inside out
drawArc(291, 430, 34, 45, 15, 7); // initial x,y values irrelevant; vectors start with 6
}
Output:
Technique 3:
// Harriss rectangle is subdivided into squares.
// Each square's dimension is equal to the preceding dimension divided by 1.325
// Center point of each arc is calculated using Pythagorean Theorem
class Square {
float x, y, w;
Square(float xpos, float ypos, float dimension) {
x = xpos;
y = ypos;
w = dimension;
}
void display() {
noFill();
stroke(0);
rect(x, y, w, w);
}
}
Square s;
ArrayList<Square> squares;
int _wndH = 600;
float w = _wndH*1.325; // Harriss rectangle to start
int _wndW = (int)w;
int startDegrees = 0;
int stopDegrees = 0;
float cntrX = 0;
float cntrY = 0;
void drawArc(int id) {
s = squares.get(id);
float radius = 0.7*s.w;
println("radius =", radius);
float d = sqrt((radius*radius)-(s.w/2*s.w/2));
println("d =", d);
noFill();
stroke(0);
strokeWeight(10);
switch(id) {
case 0:
startDegrees = -45;
stopDegrees = 45;
cntrX = s.x - d;
cntrY = s.y + s.w/2;
break;
case 1:
startDegrees = 225;
stopDegrees = 315;
cntrX = s.x + s.w/2;
cntrY = s.y + s.w + d;
break;
case 2:
startDegrees = 135;
stopDegrees = 225;
cntrX = s.x + s.w + d;
cntrY = s.y + s.w/2;
break;
case 3:
startDegrees = 45;
stopDegrees = 135;
cntrX = s.x + s.w/2;
cntrY = s.y - d;
break;
case 4:
startDegrees = -45;
stopDegrees = 45;
cntrX = s.x - d;
cntrY = s.y + s.w/2;;
break;
case 5:
startDegrees = 225;
stopDegrees = 315;
cntrX = s.x + s.w/2;
cntrY = s.y + s.w + d;
break;
case 6:
startDegrees = 135;
stopDegrees = 225;
cntrX = s.x + s.w + d;
cntrY = s.y + s.w/2;
break;
}
arc(cntrX, cntrY, 2*radius, 2*radius, radians(startDegrees), radians(stopDegrees));
strokeWeight(1);
circle(cntrX, cntrY, 13);
textSize(12);
text(str(id),cntrX-3, cntrY+4);
}
void setup() {
size(_wndW, _wndH);
background(209);
squares = new ArrayList<Square>();
squares.add(new Square(452.83017, 258.24136, 342.16983));
squares.add(new Square(194.6649, 0.0, 258.16528));
squares.add(new Square(0.0, 258.16528, 194.6649));
squares.add(new Square(194.6649, 452.8302, 146.9169));
squares.add(new Square(341.5818, 341.94952, 111.0141));
squares.add(new Square(257.82227, 258.16528, 83.75952));
squares.add(new Square(194.6649, 341.9248, 63.157375));
noLoop();
}
void draw() {
for (int i = 0; i < squares.size(); i++) {
Square s = squares.get(i);
s.display();
textSize(16);
fill(0);
text(str(squares.indexOf(s)), s.x + 32, s.y + 16);
drawArc(i);
}
}
Output:
Technique 4:
This is the most automated of all the techniques shown so far. All that the user has to provide is the window height and an initial arc rotation and the source code will do the rest.
/*
Uses an ArrayList of Square class squares
Arcs are derived from squares, each decreased by a factor of 1.325 (outer -> inner)
Ninety degree arcs are drawn clockwise using recursion (inner -> outer)
Arc centers are calculated with Pythagorean Theorem
*/
int h = 600; // Scalable (may be any value)
float wndW = h*1.325; // Start with Harriss rectangle based on window height
int _wndW = (int)wndW;
int _wndH = h;
float l = _wndH/1.325;
float w = l/1.325;
float t = w/1.325;
float x = 0;
float y = 0;
int rotation = 45; // Initial rotation
float angle = 0.0;
float cntrX = 0;
float cntrY = 0;
class Square {
float x, y, w;
Square(float xpos, float ypos, float dimension) {
x = xpos;
y = ypos;
w = dimension;
}
void display() {
noFill();
stroke(0);
strokeWeight(1);
rect(x, y, w, w);
}
}
Square s;
ArrayList<Square> squares;
void drawMyArc(int iteration) {
if (iteration > 0) {
iteration -= 1;
println("id =", iteration);
s = squares.get(iteration);
float radius = 0.7*s.w;
println("radius =", radius);
float d = sqrt((radius*radius)-(s.w/2*s.w/2));
println("d =", d);
rotation += 90;
if (rotation > 315) {
rotation = 45;
}
println("radius = " + radius);
println("rotation = ", rotation);
switch(iteration) {
case 0:
cntrX = s.x - d;
cntrY = s.y + s.w/2;
break;
case 1:
cntrX = s.x + s.w/2;
cntrY = s.y + s.w + d;
break;
case 2:
cntrX = s.x + s.w + d;
cntrY = s.y + s.w/2;
break;
case 3:
cntrX = s.x + s.w/2;
cntrY = s.y - d;
break;
case 4:
cntrX = s.x - d;
cntrY = s.y + s.w/2;
break;
case 5:
cntrX = s.x + s.w/2;
cntrY = s.y + s.w + d;
break;
case 6:
cntrX = s.x + s.w + d;
cntrY = s.y + s.w/2;
break;
}
println("cntrX = " + cntrX + " : cntrY = " + cntrY);
println("====================");
pushMatrix();
translate(cntrX, cntrY);
rotate(radians(rotation));
for ( int i = 0; i < 90; i++ ) {
angle = radians( i );
x = cos( angle ) * radius;
y = sin( angle ) * radius;
stroke(0, 0, 255);
strokeWeight(15);
point(x, y);
}
popMatrix();
drawMyArc(iteration); // recursion
}
}
void setup() {
size(_wndW, _wndH);
background(255);
squares = new ArrayList<Square>();
squares.add(new Square(l, t, w));
w = w/1.325;
squares.add(new Square(l-w, t-w, w));
w = w/1.325;
squares.add(new Square(0.0, t, w));
w = w/1.325;
squares.add(new Square(w*1.325, t+w*1.325, w));
w = w/1.325;
squares.add(new Square((w*1.325)+1.325*(w*1.325), t+w*1.325*1.325-w, w));
w = w/1.325;
squares.add(new Square((w*1.325)+1.325*(w*1.325), t, w));
w = w/1.325;
squares.add(new Square((w*1.325)+1.325*(w*1.325), t+w*1.325, w));
noLoop();
}
void draw() {
drawMyArc(squares.size());
/*
for(int i = 0; i < squares.size(); i++) {
s = squares.get(i);
s.display();
}
*/
}
Output:
Technique 5:
The following demo uses Harriss and Line classes to create a design composed of five Harriss spirals. The Harriss class is scalable to create larger or smaller spiral patterns. It uses eight line segments stored in an Array List to provide the scaffolding for ninety degree arcs which are then aligned to form the Harriss spiral. Each line is divided by a factor of 1.325 to give successively shorter arcs. Configuration variability is achieved by skipping arcs on either end of the base spiral in a ‘for’ loop. Increasing the minimum value of the loop will decrease the number of outer spiral arc segments (drawn first) and decreasing the maximum value of the loop will decrease the number of inner spiral segments (drawn last). [See image below.] Spirals are drawn in a counter clockwise direction. Translate() is then used to align the composite spirals to create a design pattern. Line thickness and arc color variables may be added to each arc for added variation.
int _wndW = 720;
int _wndH = 650;
Harriss hsp;
void setup() {
size(_wndW, _wndH);
background(209);
// **** main spiral **** //
hsp = new Harriss(600);
for (int i = 1; i < 8; i++) {
hsp.drawMyArc(i);
}
// **** right upper spiral **** //
hsp = new Harriss(300);
pushMatrix();
translate(390,-50);
for (int i = 0; i < 6; i++) {
hsp.drawMyArc(i);
}
popMatrix();
// **** left upper spiral **** //
pushMatrix();
translate(0,-68);
for (int i = 1; i < 6; i++) {
hsp.drawMyArc(i);
}
popMatrix();
// **** left lower spiral **** //
pushMatrix();
translate(-20,335);
for (int i = 2; i < 6; i++) {
hsp.drawMyArc(i);
}
popMatrix();
// **** mid lower spiral **** //
pushMatrix();
translate(240,330);
for (int i = 3; i < 6; i++) {
hsp.drawMyArc(i);
}
popMatrix();
}
void draw() {
}
Harriss Class (separate Tab):
class Harriss {
float x,y;
float cntrX = 0;
float cntrY = 0;
float radius = 0;
float d = 0;
int rotation;
float angle = 0.0;
int iteration = 0;
ArrayList<Line> lines;
// Scalable by changing the height parameter
Harriss(int ht) {
float h = ht;
float x = h/1.325;
float w = x/1.325;
float y = w/1.325;
lines = new ArrayList<Line>();
lines.add( new Line(x - w, y + w, x, y+ w, w)); // 0 Outer Spiral
lines.add( new Line(x, y + w, x, y, w)); // 1
w = w/1.325;
Line ln1 = lines.get(1);
lines.add(new Line(ln1.x2, ln1.y2, ln1.x1 - w, ln1.y2, w)); // 2
w = w/1.325;
Line ln2 = lines.get(2);
lines.add(new Line(ln2.x2, ln2.y2, ln2.x2, ln2.w + w, w)); // 3
w = w/1.325;
Line ln3 = lines.get(3);
lines.add(new Line(ln3.x2, ln3.y2, ln3.x2 + w, ln3.y2, w)); // 4
w = w/1.325;
Line ln4 = lines.get(4);
lines.add(new Line(ln4.x2, ln4.y2, ln4.x2, ln4.y2 - w, w)); // 5
w = w/1.325;
Line ln5 = lines.get(5);
lines.add(new Line(ln5.x2, ln5.y2, ln5.x2 - w, ln5.x2, w)); // 6
w = w/1.325;
Line ln6 = lines.get(6);
lines.add(new Line(ln6.x2, ln6.y2, ln6.x2, ln6.y2 + w, w)); // 7 Inner Spiral
}
void drawMyArc(int id) {
ln = lines.get(id);
radius = 0.7*ln.w;
d = 0.7*radius;
switch(id) {
case 0:
cntrX = ln.x1 + ln.w/2;
cntrY = ln.y2 - d;
rotation = 45;
break;
case 1:
cntrX = ln.x1 - d;
cntrY = ln.y1 - ln.w/2;
rotation = 315;
break;
case 2:
cntrX = ln.x1 - ln.w/2;
cntrY = ln.y1 + d;
rotation = 225;
break;
case 3:
cntrX = ln.x1 + d;
cntrY = ln.y1 + ln.w/2;
rotation = 135;
break;
case 4:
cntrX = ln.x1 + ln.w/2;
cntrY = ln.y2 - d;
rotation = 45;
break;
case 5:
cntrX = ln.x2 - d;
cntrY = ln.y1 - ln.w/2;
rotation = 315;
break;
case 6:
cntrX = ln.x2 + ln.w/2;
cntrY = ln.y1 + d;
rotation = 225;
break;
case 7:
cntrX = ln.x2 + ln.w/2;
cntrY = ln.y1 + d;
rotation = 135;
break;
}
println("id =", id);
println("x1 = " + ln.x1 + " : y1 = " + ln.y1 +" : x2 = " + ln.x2 + " : y2 = " + ln.y2);
println("w = ", ln.w);
println("radius =", radius);
println("d =", d);
println("rotation =", rotation);
println("cntrX = " + cntrX +" : cntrY = " + cntrY);
println("================");
pushMatrix();
translate(cntrX, cntrY);
rotate(radians(rotation));
for ( int i = 0; i < 90; i++ ) {
angle = radians( i );
x = cos( angle ) * radius;
y = sin( angle ) * radius;
stroke(0, 0, 255);
strokeWeight(12);
point(x, y);
}
popMatrix();
rotation -= 90; // Draws arcs counter clockwise - Outer => Inner
}
void displayLines() {
for (int i = 0; i < lines.size(); i++) {
Line ln = lines.get(i);
ln.display();
}
}
}
Line Class (separate Tab):
class Line {
float x1, y1, x2, y2, w;
// Constructor
Line(float x1Pos, float y1Pos, float x2Pos, float y2Pos, float len) {
x1 = x1Pos;
y1 = y1Pos;
x2 = x2Pos;
y2 = y2Pos;
w = len;
}
void display() {
stroke(0);
strokeWeight(1);
line(x1, y1, x2, y2);
}
}
Line ln;
Changing Spiral Configuration example:
Output: