Screengrabs
A

B

C

D

E

F

G

H

SVGs
H
G
Code
import processing.svg.*;
// Settings
int mode = 7;
// Data
int max_points = 100;
int xposes[] = new int[max_points];
int yposes[] = new int[max_points];
int poses[][] = new int[max_points][2];
Point points[] = new Point[max_points];
boolean recording = false;
void setup(){
size(800,800);
for (int i = 0; i < max_points; i++) { xposes[i] = yposes[i] = poses[i][0] = poses[i][1] = -1; points[i] = null; } } void mousePressed() { if (mode > 6) {
recording = true;
beginRecord(SVG,"O3_" + (mode == 7 ? "G" : "H") + ".svg");
}
}
void keyPressed(){
// Change mode on numerical key press and then loop
if (Character.isDigit(key)) {
int keyNum = Integer.parseInt(Character.toString(key));
if (1 <= keyNum && keyNum <= 8) {
mode = keyNum;
loop();
}
}
}
void draw(){
background(255);
strokeWeight(1);
switch(mode){
case 1:
solution_dashed();
break;
case 2:
solution_line_live();
break;
case 3:
solution_line_spicy();
break;
case 4:
solution_circle_two_ways();
break;
case 5:
solution_spiral();
break;
case 6:
solution_offset();
break;
case 7:
solution_weighted();
break;
case 8:
solution_calli();
break;
default:
break;
}
}
public class Point {
public int x;
public int y;
public Point(int x0, int y0) {
x = x0;
y = y0;
}
public Point clone() {
return new Point(x, y);
}
public double distance(Point p) {
return dist(x, y, p.x, p.y);
}
}
// Draws a dashed line between the indicated points
void dashed_line(int x_start, int y_start, int x_end, int y_end){
int dash_size = 10;
// Find length of line, then divide by 2*dash len to get intervals
float diffx = x_start-x_end;
float diffy = y_start-y_end;
float len =(float) Math.sqrt((diffx*diffx)+(diffy*diffy));
int intervals = (int) (len/dash_size);
if (intervals == 0) return; // Prevent 0 division error
float dx = -diffx/intervals;
float dy = -diffy/intervals;
boolean draw = true;
for (int i = 0; i < intervals; i++) {
float x0 = x_start+(dx*i);
float y0 = y_start+(dy*i);
float x1 = x0+dx;
float y1 = y0+dy;
if (draw) {
line(x0, y0, x1, y1);
}
draw = !draw; // Alternate drawing line to create dash
}
}
void solution_dashed(){
// Compute line endpoints
int x_start = width/2;
int y_start = height/2;
int x_end = mouseX;
int y_end = mouseY;
// Draw dashed line
dashed_line(x_start, y_start, x_end, y_end);
}
/*~~~~~~~~~~~~*/
/* SOLUTION 2 */
/*~~~~~~~~~~~~*/
// Used to move points
void shift_array(int A[]) {
for (int i = A.length-2; 0 <= i; i--) {
A[i+1] = A[i];
}
}
void shift_array2D(int A[][]) {
for (int i = A.length-2; 0 <= i; i--) {
A[i+1][0] = A[i][0];
A[i+1][1] = A[i][1];
}
}
void shift_array_points(Point A[]) {
for (int i = A.length-2; 0 <= i; i--) {
if (A[i] != null) A[i+1] = A[i].clone();
}
}
void live1() {
if (xposes[0] != -1 && yposes[0] != -1) {
if (xposes[0] != mouseX || yposes[0] != mouseY) {
shift_array(xposes);
shift_array(yposes);
xposes[0] = mouseX;
yposes[0] = mouseY;
}
} else {
xposes[0] = mouseX;
yposes[0] = mouseY;
}
// Draw curve
for (int i = 0; i < max_points-1; i++){
if (xposes[i+1] == -1) return;
line(xposes[i], yposes[i], xposes[i+1], yposes[i+1]);
}
}
void live2() {
if (poses[0][0] != -1 && poses[0][1] != -1) {
if (poses[0][0] != mouseX || poses[0][1] != mouseY) {
shift_array2D(poses);
poses[0][0] = mouseX;
poses[0][1] = mouseY;
}
} else {
poses[0][0] = mouseX;
poses[0][1] = mouseY;
}
// Draw curve
for (int i = 0; i < max_points-1; i++){
if (poses[i+1][0] == -1) return;
line(poses[i][0], poses[i][1], poses[i+1][0], poses[i+1][1]);
}
}
void live3() {
if (points[0] != null) {
if (points[0].x != mouseX || points[0].y != mouseY) {
shift_array_points(points);
points[0] = new Point(mouseX, mouseY);
}
} else {
points[0] = new Point(mouseX, mouseY);
}
// Draw curve
for (int i = 0; i < max_points-1; i++){
if (points[i+1] == null) return;
line(points[i].x, points[i].y, points[i+1].x, points[i+1].y);
}
}
void solution_line_live() {
// live1();
// live2();
live3();
}
/*~~~~~~~~~~~~*/
/* SOLUTION 3 */
/*~~~~~~~~~~~~*/
void add_spice(int spice_level) {
for (int i = 0; i < max_points; i++) {
Point p = points[i];
if (p == null) return;
p.x += spice_level*random(-1,1);// If the lower bound equals upper, cig effect
p.y += spice_level*random(-1,1);
}
}
void solution_line_spicy() {
live3();
add_spice(1);
}
/*~~~~~~~~~~~~*/
/* SOLUTION 4 */
/*~~~~~~~~~~~~*/
void solution_circle_two_ways() {
noLoop();
int cxA = width/4;
int cyA = height/2;
int rA = min(width,height)/4;
circle_A(cxA, cyA, rA);
int cxB = (3*width)/4;
int cyB = cyA;
int rB = min(width,height)/4;
circle_B(cxB, cyB, rB);
}
void circle_A(int cx, int cy, int r){
int n = 30;
for (int i = 0; i < n; i++){
float dtheta = 2*PI/n;
int start_x = (int)(Math.cos(i*dtheta)*r)+cx;
int start_y = (int)(Math.sin(i*dtheta)*r)+cy;
int next_x = (int)(Math.cos((i+1)*dtheta)*r)+cx;
int next_y = (int)(Math.sin((i+1)*dtheta)*r)+cy;
print(i);
line(start_x, start_y, next_x, next_y);
}
}
// Sampling points within square of side length r
// for points that are near the circle boundary
void circle_B(int cx, int cy, int r) {
// Settings
float epsilon = .5;
int divs = 400;
// Geometry variables
float x_start = cx-r;
float y_start = cy-r;
float dx =(2*r)/divs;
float dy = dx;
for (int i = 0; i <= divs; i++) {
for (int j = 0; j <= divs; j++) {
float x_sample = x_start + i*dx;
float y_sample = y_start + j*dy;
float dist_to_circle = (dist(cx, cy, x_sample, y_sample))-r;
if ((-epsilon < dist_to_circle) && (dist_to_circle < epsilon)){ point(x_sample, y_sample); } } } } /* SOLUTION 5 */ // Could generalize to have r = r(i), where r(i) is any contiuous function on R->R
void draw_spiral(float cx, float cy, float radius, int rad_divs, int theta_divs) {
float dtheta = 2*PI/theta_divs;
float dr = radius/rad_divs;
for (int i = 0; i < rad_divs; i++) {
float x0 = cx+dr*i*cos(dtheta*i);
float y0 = cy+dr*i*sin(dtheta*i);
float x1 = cx+dr*(i+1)*cos(dtheta*(i+1));
float y1 = cy+dr*(i+1)*sin(dtheta*(i+1));
line(x0, y0, x1, y1);
}
}
void solution_spiral() {
int cx = width/2;
int cy = height/2;
int radius = width/2;
draw_spiral(cx, cy, radius, 3000, 300);
}
/*
SOLUTION 6
*/
void solution_offset() {
live3();
draw_offset_curve(50);
}
Point offset_point(Point p0, Point p1, float offset) {
float diffx = p1.x-p0.x;
float diffy = p1.y-p0.y;
float theta;
int x_off, y_off;
theta = atan2(diffy, diffx);
float theta_off = theta + PI/2;
x_off = (int)(p1.x+offset*cos(theta_off));
y_off = (int)(p1.y+offset*sin(theta_off));
return new Point(x_off, y_off);
}
// Implicitly uses points array
void draw_offset_curve(int offset) {
if (points[0] != null && points[1] != null) {
if (points[0].x != mouseX || points[0].y != mouseY) {
points[0] = new Point(mouseX, mouseY);
}
} else {
points[0] = new Point(mouseX, mouseY);
}
// Draw curve
if (points[0] != null
&& (points[1] != null)) {
Point p_off_curr;
Point p_off_prev = offset_point(points[0], points[1], offset);
for (int i = 1; i < max_points-1; i++){
if (points[i+1] == null) return;
p_off_curr = offset_point(points[i], points[i+1], offset);
line(p_off_prev.x, p_off_prev.y, p_off_curr.x, p_off_curr.y);
p_off_prev = p_off_curr;
}
}
}
/*
SOLUTION 7
*/
void draw_weighted_line(int x0, int y0, int x1, int y1, int hatches, float dw) {
float diffx = x1 - x0;
float diffy = y1 - y0;
float theta;
theta = atan2(diffy, diffx);
float theta_perp = theta + PI/2;
// Get starting distance along perpendicular
float w0 = (dw * hatches)/2;
for (int i = 0; i < hatches; i++) {
float x_offset = (dw*i-w0)*sin(theta);
float y_offset = (w0-dw*i)*cos(theta);
print(x_offset, y_offset, "\n");
float nx0 = x0 + x_offset;
float ny0 = y0 + y_offset;
float nx1 = x1 + x_offset;
float ny1 = y1 + y_offset;
line(nx0, ny0, nx1, ny1);
}
}
void solution_weighted() {
int now = millis();
int hatches = 20;
float dw = 1;
int x0 = width/2;
int y0 = height/2;
int x1 = mouseX;
int y1 = mouseY;
draw_weighted_line(x0, y0, x1, y1, hatches, dw);
if (recording) {
endRecord();
recording = false;
}
}
/*
SOLUTION 8
*/
void solution_calli() {
draw_calligraphic(100);
}
// Needs some kind of smoothing or alternative draw method
void draw_calligraphic(float max_width) {
float k = 5.0;
float e = 0.47;
if (points[0] != null) {
if (points[0].x != mouseX || points[0].y != mouseY) {
shift_array_points(points);
points[0] = new Point(mouseX, mouseY);
}
} else {
points[0] = new Point(mouseX, mouseY);
}
// Draw curve
for (int i = 0; i < max_points-1; i++){
if (points[i+1] == null) return;
float d = dist(points[i].x, points[i].y, points[i+1].x, points[i+1].y);
strokeWeight((float)Math.pow(max_width/(d+1), e)*k);
line(points[i].x, points[i].y, points[i+1].x, points[i+1].y);
}
if (recording) {
endRecord();
recording = false;
}
}