{"id":920,"date":"2021-09-15T13:32:30","date_gmt":"2021-09-15T17:32:30","guid":{"rendered":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/?p=920"},"modified":"2021-09-15T13:32:30","modified_gmt":"2021-09-15T17:32:30","slug":"frog-lineexercises","status":"publish","type":"post","link":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/frog\/09\/15\/frog-lineexercises\/","title":{"rendered":"frog-LineExercises"},"content":{"rendered":"<h1>Screengrabs<\/h1>\n<p>A<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-909\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/A.png\" alt=\"\" width=\"353\" height=\"280\" \/><\/p>\n<p>B<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-910\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/B-427x480.png\" alt=\"\" width=\"427\" height=\"480\" srcset=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/B-427x480.png 427w, https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/B.png 618w\" sizes=\"(max-width: 427px) 85vw, 427px\" \/><\/p>\n<p>C<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-911\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/C.png\" alt=\"\" width=\"348\" height=\"304\" \/><\/p>\n<p>D<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-912\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/D-640x417.png\" alt=\"\" width=\"640\" height=\"417\" srcset=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/D-640x417.png 640w, https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/D-768x500.png 768w, https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/D.png 794w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px\" \/><\/p>\n<p>E<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-913\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/E-475x480.png\" alt=\"\" width=\"475\" height=\"480\" srcset=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/E-475x480.png 475w, https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/E-768x776.png 768w, https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/E.png 784w\" sizes=\"(max-width: 475px) 85vw, 475px\" \/><\/p>\n<p>F<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-914\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/F-497x480.png\" alt=\"\" width=\"497\" height=\"480\" srcset=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/F-497x480.png 497w, https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/F.png 649w\" sizes=\"(max-width: 497px) 85vw, 497px\" \/><\/p>\n<p>G<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-915\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/G.png\" alt=\"\" width=\"380\" height=\"318\" \/><\/p>\n<p>H<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-916\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/H-381x480.png\" alt=\"\" width=\"381\" height=\"480\" srcset=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/H-381x480.png 381w, https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/H.png 615w\" sizes=\"(max-width: 381px) 85vw, 381px\" \/><\/p>\n<h1>SVGs<\/h1>\n<p>H<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-917\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/O3_G.svg\" alt=\"\" width=\"640\" height=\"480\" \/><\/p>\n<p>G<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-918\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/09\/O3_H.svg\" alt=\"\" width=\"640\" height=\"480\" \/><\/p>\n<h1><\/h1>\n<p><!--more--><\/p>\n<h1>Code<\/h1>\n<pre>import processing.svg.*;\r\n\r\n\/\/ Settings\r\nint mode = 7;\r\n\r\n\/\/ Data\r\nint max_points = 100;\r\nint xposes[] = new int[max_points];\r\nint yposes[] = new int[max_points];\r\nint poses[][] = new int[max_points][2];\r\nPoint points[] = new Point[max_points];\r\nboolean recording = false;\r\n\r\nvoid setup(){\r\n  size(800,800);\r\n  for (int i = 0; i &lt; max_points; i++) { xposes[i] = yposes[i] = poses[i][0] = poses[i][1] = -1; points[i] = null; } } void mousePressed() { if (mode &gt; 6) {\r\n    recording = true;\r\n    beginRecord(SVG,\"O3_\" + (mode == 7 ? \"G\" : \"H\") + \".svg\");\r\n  }\r\n}\r\n\r\nvoid keyPressed(){\r\n  \r\n  \/\/ Change mode on numerical key press and then loop\r\n  if (Character.isDigit(key)) {\r\n    int keyNum = Integer.parseInt(Character.toString(key));\r\n    if (1 &lt;= keyNum &amp;&amp; keyNum &lt;= 8) {\r\n      mode = keyNum;\r\n      loop();\r\n    }\r\n  }\r\n}\r\n\r\nvoid draw(){\r\n  background(255);\r\n  strokeWeight(1);\r\n  switch(mode){\r\n  case 1:\r\n    solution_dashed();\r\n    break;\r\n  case 2:\r\n    solution_line_live();\r\n    break;\r\n  case 3:\r\n    solution_line_spicy();\r\n    break;\r\n  case 4:\r\n    solution_circle_two_ways();\r\n    break;\r\n  case 5:\r\n    solution_spiral();\r\n    break;\r\n  case 6:\r\n    solution_offset();\r\n    break;\r\n  case 7:\r\n    solution_weighted();\r\n    break;\r\n  case 8:\r\n    solution_calli();\r\n    break;\r\n  default:\r\n    break;\r\n  }\r\n}\r\n\r\npublic class Point {\r\n  public int x;\r\n  public int y;\r\n  \r\n  public Point(int x0, int y0) {\r\n    x = x0;\r\n    y = y0;\r\n  }\r\n  \r\n  public Point clone() {\r\n    return new Point(x, y);\r\n  }\r\n  \r\n  public double distance(Point p) {\r\n    return dist(x, y, p.x, p.y);\r\n  }\r\n}\r\n\r\n\/\/ Draws a dashed line between the indicated points\r\nvoid dashed_line(int x_start, int y_start, int x_end, int y_end){\r\n  int dash_size = 10;\r\n  \r\n  \/\/ Find length of line, then divide by 2*dash len to get intervals\r\n  float diffx = x_start-x_end;\r\n  float diffy = y_start-y_end;\r\n  \r\n  float len =(float) Math.sqrt((diffx*diffx)+(diffy*diffy));\r\n  int intervals = (int) (len\/dash_size);\r\n  if (intervals == 0) return; \/\/ Prevent 0 division error\r\n  \r\n  float dx = -diffx\/intervals;\r\n  float dy = -diffy\/intervals;\r\n  boolean draw = true;\r\n  \r\n  for (int i = 0; i &lt; intervals; i++) {\r\n    float x0 = x_start+(dx*i);\r\n    float y0 = y_start+(dy*i);\r\n    float x1 = x0+dx;\r\n    float y1 = y0+dy;\r\n    if (draw) {\r\n      line(x0, y0, x1, y1);\r\n    }\r\n    draw = !draw; \/\/ Alternate drawing line to create dash\r\n  }\r\n}\r\n\r\nvoid solution_dashed(){\r\n  \r\n  \/\/ Compute line endpoints\r\n  int x_start = width\/2;\r\n  int y_start = height\/2;\r\n  int x_end = mouseX;\r\n  int y_end = mouseY;\r\n  \r\n  \/\/ Draw dashed line\r\n  dashed_line(x_start, y_start, x_end, y_end);\r\n}\r\n\r\n\/*~~~~~~~~~~~~*\/\r\n\/* SOLUTION 2 *\/\r\n\/*~~~~~~~~~~~~*\/\r\n\r\n\/\/ Used to move points\r\nvoid shift_array(int A[]) {\r\n  for (int i = A.length-2; 0 &lt;= i; i--) {\r\n    A[i+1] = A[i];\r\n  }\r\n}\r\n\r\nvoid shift_array2D(int A[][]) {\r\n  for (int i = A.length-2; 0 &lt;= i; i--) {\r\n    A[i+1][0] = A[i][0];\r\n    A[i+1][1] = A[i][1];\r\n  }\r\n}\r\n\r\nvoid shift_array_points(Point A[]) {\r\n  for (int i = A.length-2; 0 &lt;= i; i--) {\r\n    if (A[i] != null) A[i+1] = A[i].clone();\r\n  }\r\n}\r\n\r\nvoid live1() {\r\n  if (xposes[0] != -1 &amp;&amp; yposes[0] != -1) {\r\n    if (xposes[0] != mouseX || yposes[0] != mouseY) {\r\n      shift_array(xposes);\r\n      shift_array(yposes);\r\n      xposes[0] = mouseX;\r\n      yposes[0] = mouseY;\r\n    }\r\n  } else {\r\n    xposes[0] = mouseX;\r\n    yposes[0] = mouseY;\r\n  }\r\n  \r\n  \/\/ Draw curve\r\n  for (int i = 0; i &lt; max_points-1; i++){\r\n    if (xposes[i+1] == -1) return;\r\n    line(xposes[i], yposes[i], xposes[i+1], yposes[i+1]);\r\n  }\r\n}\r\n\r\nvoid live2() {\r\n  if (poses[0][0] != -1 &amp;&amp; poses[0][1] != -1) {\r\n    if (poses[0][0] != mouseX || poses[0][1] != mouseY) {\r\n      shift_array2D(poses);\r\n      poses[0][0] = mouseX;\r\n      poses[0][1] = mouseY;\r\n    }\r\n  } else {\r\n    poses[0][0] = mouseX;\r\n    poses[0][1] = mouseY;\r\n  }\r\n  \r\n  \/\/ Draw curve\r\n  for (int i = 0; i &lt; max_points-1; i++){\r\n    if (poses[i+1][0] == -1) return;\r\n    line(poses[i][0], poses[i][1], poses[i+1][0], poses[i+1][1]);\r\n  }\r\n}\r\n\r\nvoid live3() {\r\n  if (points[0] != null) {\r\n    if (points[0].x != mouseX || points[0].y != mouseY) {\r\n      shift_array_points(points);\r\n      points[0] = new Point(mouseX, mouseY);\r\n    }\r\n  } else {\r\n    points[0] = new Point(mouseX, mouseY);\r\n  }\r\n  \r\n  \/\/ Draw curve\r\n  for (int i = 0; i &lt; max_points-1; i++){\r\n    if (points[i+1] == null) return;\r\n    line(points[i].x, points[i].y, points[i+1].x, points[i+1].y);\r\n  }\r\n}\r\n\r\nvoid solution_line_live() {\r\n  \/\/ live1();\r\n  \/\/ live2();\r\n  live3();\r\n}\r\n\r\n\/*~~~~~~~~~~~~*\/\r\n\/* SOLUTION 3 *\/\r\n\/*~~~~~~~~~~~~*\/\r\nvoid add_spice(int spice_level) {\r\n  for (int i = 0; i &lt; max_points; i++) {\r\n    Point p = points[i];\r\n    if (p == null) return;\r\n    p.x += spice_level*random(-1,1);\/\/ If the lower bound equals upper, cig effect\r\n    p.y += spice_level*random(-1,1);\r\n  }\r\n}\r\nvoid solution_line_spicy() {\r\n  live3();\r\n  add_spice(1);\r\n}\r\n\r\n\/*~~~~~~~~~~~~*\/\r\n\/* SOLUTION 4 *\/\r\n\/*~~~~~~~~~~~~*\/\r\nvoid solution_circle_two_ways() {\r\n  noLoop();\r\n  \r\n  int cxA = width\/4;\r\n  int cyA = height\/2;\r\n  int rA = min(width,height)\/4;\r\n  circle_A(cxA, cyA, rA);\r\n  \r\n  int cxB = (3*width)\/4;\r\n  int cyB = cyA;\r\n  int rB = min(width,height)\/4;\r\n  circle_B(cxB, cyB, rB);\r\n}\r\n\r\nvoid circle_A(int cx, int cy, int r){\r\n  int n = 30;\r\n  \r\n  for (int i = 0; i &lt; n; i++){\r\n    float  dtheta = 2*PI\/n;\r\n    \r\n    int start_x = (int)(Math.cos(i*dtheta)*r)+cx;\r\n    int start_y = (int)(Math.sin(i*dtheta)*r)+cy;\r\n    int next_x = (int)(Math.cos((i+1)*dtheta)*r)+cx;\r\n    int next_y = (int)(Math.sin((i+1)*dtheta)*r)+cy;\r\n    print(i);\r\n    line(start_x, start_y, next_x, next_y);\r\n  }\r\n}\r\n\r\n\/\/ Sampling points within square of side length r\r\n\/\/ for points that are near the circle boundary\r\nvoid circle_B(int cx, int cy, int r) {\r\n  \/\/ Settings\r\n  float epsilon = .5;\r\n  int divs = 400;\r\n  \r\n  \/\/ Geometry variables\r\n  float x_start = cx-r;\r\n  float y_start = cy-r;\r\n  float dx =(2*r)\/divs;\r\n  float dy = dx;\r\n  \r\n  for (int i = 0; i &lt;= divs; i++) {\r\n    for (int j = 0; j &lt;= divs; j++) {\r\n      float x_sample = x_start + i*dx;\r\n      float y_sample = y_start + j*dy;\r\n      float dist_to_circle = (dist(cx, cy, x_sample, y_sample))-r;\r\n      if ((-epsilon &lt; dist_to_circle) &amp;&amp; (dist_to_circle &lt; epsilon)){ point(x_sample, y_sample); } } } } \/* SOLUTION 5 *\/ \/\/ Could generalize to have r = r(i), where r(i) is any contiuous function on R-&gt;R\r\nvoid draw_spiral(float cx, float cy, float radius, int rad_divs, int theta_divs) {\r\n  float dtheta = 2*PI\/theta_divs;\r\n  float dr = radius\/rad_divs;\r\n  \r\n  for (int i = 0; i &lt; rad_divs; i++) {\r\n    float x0 = cx+dr*i*cos(dtheta*i);\r\n    float y0 = cy+dr*i*sin(dtheta*i);\r\n    float x1 = cx+dr*(i+1)*cos(dtheta*(i+1));\r\n    float y1 = cy+dr*(i+1)*sin(dtheta*(i+1));\r\n    line(x0, y0, x1, y1);\r\n  }\r\n}\r\n\r\nvoid solution_spiral() {\r\n  int cx = width\/2;\r\n  int cy = height\/2;\r\n  int radius = width\/2;\r\n   \r\n  draw_spiral(cx, cy, radius, 3000, 300);\r\n}\r\n\r\n\/*\r\n  SOLUTION 6\r\n*\/\r\n\r\nvoid solution_offset() {\r\n  live3();\r\n  draw_offset_curve(50);\r\n}\r\n\r\nPoint offset_point(Point p0, Point p1, float offset) {\r\n  float diffx = p1.x-p0.x;\r\n  float diffy = p1.y-p0.y;\r\n  float theta;\r\n  int x_off, y_off;\r\n\r\n  theta = atan2(diffy, diffx);\r\n  \r\n  float theta_off = theta + PI\/2;\r\n  \r\n  x_off = (int)(p1.x+offset*cos(theta_off));\r\n  y_off = (int)(p1.y+offset*sin(theta_off));\r\n  \r\n  return new Point(x_off, y_off);\r\n}\r\n\r\n\/\/ Implicitly uses points array\r\nvoid draw_offset_curve(int offset) {\r\n  if (points[0] != null &amp;&amp; points[1] != null) {\r\n    if (points[0].x != mouseX || points[0].y != mouseY) {\r\n      points[0] = new Point(mouseX, mouseY);\r\n    }\r\n  } else {\r\n    points[0] = new Point(mouseX, mouseY);\r\n  }\r\n  \r\n  \/\/ Draw curve\r\n  if (points[0] != null\r\n  &amp;&amp; (points[1] != null)) {\r\n    Point p_off_curr;\r\n    Point p_off_prev = offset_point(points[0], points[1], offset);\r\n    for (int i = 1; i &lt; max_points-1; i++){\r\n      if (points[i+1] == null) return;\r\n      p_off_curr = offset_point(points[i], points[i+1], offset);\r\n    \r\n      line(p_off_prev.x, p_off_prev.y, p_off_curr.x, p_off_curr.y);\r\n    \r\n      p_off_prev = p_off_curr;\r\n    }\r\n  }\r\n}\r\n\r\n\/*\r\n  SOLUTION 7\r\n*\/\r\nvoid draw_weighted_line(int x0, int y0, int x1, int y1, int hatches, float dw) {\r\n  float diffx = x1 - x0;\r\n  float diffy = y1 - y0;\r\n  float theta;\r\n\r\n  theta = atan2(diffy, diffx);\r\n  \r\n  float theta_perp = theta + PI\/2;\r\n  \r\n  \/\/ Get starting distance along perpendicular\r\n  float w0 = (dw * hatches)\/2;\r\n  \r\n  for (int i = 0; i &lt; hatches; i++) {\r\n    float x_offset = (dw*i-w0)*sin(theta);\r\n    float y_offset = (w0-dw*i)*cos(theta);\r\n    print(x_offset, y_offset, \"\\n\");\r\n    \r\n    float nx0 = x0 + x_offset;\r\n    float ny0 = y0 + y_offset;\r\n    float nx1 = x1 + x_offset;\r\n    float ny1 = y1 + y_offset;\r\n    \r\n    line(nx0, ny0, nx1, ny1);\r\n  }\r\n  \r\n}\r\n\r\nvoid solution_weighted() {\r\n  \r\n  int now = millis();\r\n  \r\n  \r\n  int hatches = 20;\r\n  float dw = 1;\r\n  int x0 = width\/2;\r\n  int y0 = height\/2;\r\n  int x1 = mouseX;\r\n  int y1 = mouseY;\r\n  \r\n  draw_weighted_line(x0, y0, x1, y1, hatches, dw);\r\n  \r\n  if (recording) {\r\n    endRecord();\r\n    recording = false;\r\n  }\r\n}\r\n\/*\r\n  SOLUTION 8\r\n*\/\r\n\r\nvoid solution_calli() {\r\n  draw_calligraphic(100);\r\n}\r\n\r\n\/\/ Needs some kind of smoothing or alternative draw method\r\nvoid draw_calligraphic(float max_width) {\r\n  float k = 5.0;\r\n  float e = 0.47;\r\n  if (points[0] != null) {\r\n    if (points[0].x != mouseX || points[0].y != mouseY) {\r\n      shift_array_points(points);\r\n      points[0] = new Point(mouseX, mouseY);\r\n    }\r\n  } else {\r\n    points[0] = new Point(mouseX, mouseY);\r\n  }\r\n  \r\n  \/\/ Draw curve\r\n  for (int i = 0; i &lt; max_points-1; i++){\r\n    if (points[i+1] == null) return;\r\n    float d = dist(points[i].x, points[i].y, points[i+1].x, points[i+1].y);\r\n    strokeWeight((float)Math.pow(max_width\/(d+1), e)*k);\r\n    line(points[i].x, points[i].y, points[i+1].x, points[i+1].y);\r\n  }\r\n  \r\n  if (recording) {\r\n    endRecord();\r\n    recording = false;\r\n  }\r\n  \r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Screengrabs A B C D E F G H SVGs H G<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/posts\/920"}],"collection":[{"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/comments?post=920"}],"version-history":[{"count":1,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/posts\/920\/revisions"}],"predecessor-version":[{"id":931,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/posts\/920\/revisions\/931"}],"wp:attachment":[{"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/media?parent=920"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/categories?post=920"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/tags?post=920"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}