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