frog-LineExercises

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