{"id":1416,"date":"2021-10-04T14:56:42","date_gmt":"2021-10-04T18:56:42","guid":{"rendered":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/?p=1416"},"modified":"2021-10-04T15:01:38","modified_gmt":"2021-10-04T19:01:38","slug":"bookoobread-blobfamily","status":"publish","type":"post","link":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/bookoobread\/10\/04\/bookoobread-blobfamily\/","title":{"rendered":"bookooBread &#8211; BlobFamily"},"content":{"rendered":"<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1422 alignleft\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/10\/PXL_20211004_175220698-481x480.jpg\" alt=\"\" width=\"425\" height=\"424\" \/><\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1423 alignleft\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/10\/PXL_20211004_175537538-640x441.jpg\" alt=\"\" width=\"522\" height=\"360\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>My interpretation of the blob family prompt manifested itself into an exploration of the differential growth algorithm. After dong some research on the algorithm itself and code tracing a few different implementations, I landed on Alberto Giachino&#8217;s <a href=\"http:\/\/www.codeplastic.com\/2017\/07\/22\/differential-line-growth-with-processing\/\">Differential line growth with Processing.<\/a> I have mostly been working in processing, and it didn&#8217;t require any outside dependencies, so it made the most sense to use this one as the base. Jason Webb&#8217;s <a href=\"https:\/\/github.com\/jasonwebb\/2d-differential-growth-experiments\">2D differential growth experiments<\/a> also really helped in understanding how the algorithm works and how it can be modified. After playing around with the algorithm in the hatching exercise from last week, I wanted to see how I could constrain the growth to one specific shape, more specifically, in a blob-like shape. Golan had showed me how to use an off-screen graphics buffer, so I thought it might work if I tried to use that as a layer mask of sorts. The off-screen graphics buffer would contain white filled blobs (made by modifying the radius of a circle with perlin noise) against a black background. Therefore, whenever a node was at the location of a white fill, the differential growth cohesion and separation forces would act. When a node on the circle was at location of a black fill, there were no forces acting on it to make it grow. The nodes that were growing then formed into the shape of the blobs, and to my surprise, sort of collapsed in on each other where the edges were. It made what looked like the outline of the blob. The two pictures below are the blobs rendered digitally.<\/p>\n<p>I had a lot of fun with this one! I love texture and I love organic\/bodily looking things.<\/p>\n<p style=\"text-align: center\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1421\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/10\/differential_growth_blob1.svg\" alt=\"\" width=\"289\" height=\"289\" \/> <img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1419\" src=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-content\/uploads\/2021\/10\/differential_growth_10.svg\" alt=\"\" width=\"292\" height=\"292\" \/><\/p>\n<p>Here is the code:<\/p>\n<p><!--more--><\/p>\n<pre><code>\r\nimport processing.javafx.*;\r\nimport processing.svg.*;\r\nboolean doRecord = false;\r\n\/\/ This code is by Alberto Giachino with suggestions from Frederik Vanhoutte\r\n\/\/ From: http:\/\/www.codeplastic.com\/2017\/07\/22\/differential-line-growth-with-processing\/\r\n\r\n\/\/ PARAMETERS\r\nfloat _maxForce = 0.9; \/\/ Maximum steering force\r\nfloat _maxSpeed = 35; \/\/ Maximum speed\r\nfloat _desiredSeparation = 25;\r\nfloat _separationCohesionRation = 0.6;\r\nfloat _maxEdgeLen = 6;\r\n\r\nPGraphics pg;\r\nDifferentialLine _diff_line1;\r\n\r\nvoid setup() {\r\n  size(600, 600, FX2D);\r\n\r\n  \/\/ Make our masks\r\n  pg = createGraphics(600, 600);\r\n  pg.beginDraw();\r\n  pg.background(0);\r\n  pg.noStroke();\r\n  pg.fill(255);\r\n  for (int i = 0; i&lt;=5; i++) {\r\n    randomSeed(1000*i + 13);\r\n    noiseSeed(i+13);\r\n    float radius = random(80, 130);\r\n    float randX = random(10, width-10);\r\n    float randY = random(10, height-10);\r\n    createBounds(randX, randY, radius);\r\n  }\r\n  pg.endDraw();\r\n\r\n  _diff_line1 = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);\r\n\r\n  float nodesStart = 30;\r\n  float angInc = TWO_PI\/nodesStart;\r\n  float rayStart = 150;\r\n  for (float a=0; a&lt;TWO_PI; a+=angInc) {\r\n    float x = width\/2 + cos(a) * rayStart;\r\n    float y = height\/2 + sin(a) * rayStart;\r\n    _diff_line1.addNode(new Node(x, y, _diff_line1.maxForce, _diff_line1.maxSpeed));\r\n  }\r\n}\r\n\r\n\/\/make array of off screen graphics buffer blob class in future\r\nvoid draw() {\r\n  background(255);\r\n  \/\/image(pg, 0, 0);\r\n\r\n  if (doRecord) {\r\n    beginRecord(SVG, \"differential_growth_10.svg\");\r\n  }\r\n  stroke(0);\/\/255, 0, 0);\r\n  strokeWeight(1.0);\r\n  noFill();\r\n\r\n  _diff_line1.separationCohesionRation = 0.7;\r\n  _diff_line1.maxForce = 0.6;\r\n\r\n  _diff_line1.run();\r\n  _diff_line1.renderLine();\r\n\r\n  if (doRecord) {\r\n    endRecord();\r\n    doRecord = false;\r\n  }\r\n}\r\n\r\nvoid createBounds(float randX, float randY, float radius) {\r\n\r\n  float yoff = 0.0;\r\n\r\n  pg.pushMatrix();\r\n  pg.translate(randX, randY);\r\n\r\n  pg.beginShape();\r\n  float xoff = 0;\r\n  for (float a = 0; a &lt; TWO_PI; a += 0.1) {\r\n    float offset = map(noise(xoff, yoff), 0, 1, -25, 25);\r\n    float r = radius + offset;\r\n    float x = r * cos(a);\r\n    float y = r * sin(a);\r\n    pg.vertex(x, y);\r\n    xoff += 0.1;\r\n  }\r\n  pg.endShape();\r\n  pg.popMatrix();\r\n\r\n  yoff += 0.01;\r\n}\r\n\r\nvoid keyPressed() {\r\n  if (key == ' ') {\r\n    doRecord = true;\r\n  }\r\n}\r\n\r\nclass DifferentialLine {\r\n  ArrayList nodes;\r\n  float maxForce;\r\n  float maxSpeed;\r\n  float desiredSeparation;\r\n  float sq_desiredSeparation;\r\n  float separationCohesionRation;\r\n  float maxEdgeLen;\r\n  DifferentialLine(float mF, float mS, float dS, float sCr, float eL) {\r\n    nodes = new ArrayList();\r\n    maxSpeed = mF;\r\n    maxForce = mS;\r\n    desiredSeparation = dS;\r\n    sq_desiredSeparation = sq(desiredSeparation);\r\n    separationCohesionRation = sCr;\r\n    maxEdgeLen = eL;\r\n  }\r\n  void addNode(Node n) {\r\n    nodes.add(n);\r\n  }\r\n  void addNodeAt(Node n, int index) {\r\n    nodes.add(index, n);\r\n  }\r\n\r\n  void run() {\r\n    differentiate();\r\n    growth();\r\n  }\r\n\r\n  void growth() {\r\n    for (int i=0; i&lt;nodes.size()-1; i++) { Node n1 = nodes.get(i); Node n2 = nodes.get(i+1); float d = PVector.dist(n1.position, n2.position); if (d&gt;maxEdgeLen) { \/\/ Can add more rules for inserting nodes\r\n        int index = nodes.indexOf(n2);\r\n        PVector middleNode = PVector.add(n1.position, n2.position).div(2);\r\n        addNodeAt(new Node(middleNode.x, middleNode.y, maxForce*noise(i), maxSpeed), index);\r\n      }\r\n    }\r\n  }\r\n  void differentiate() {\r\n    PVector[] separationForces = getSeparationForces();\r\n    PVector[] cohesionForces = getEdgeCohesionForces();\r\n    for (int i=0; i&lt;nodes.size(); i++) { int nx = (int)round(nodes.get(i).position.x); int ny = (int)round(nodes.get(i).position.y); PVector separation = separationForces[i]; PVector cohesion = cohesionForces[i]; separation.mult(separationCohesionRation); color c = pg.get(nx, ny); int val = (int) red(c); if (val &gt; 0) {\r\n\r\n        nodes.get(i).applyForce(separation);\r\n        nodes.get(i).applyForce(cohesion);\r\n        nodes.get(i).update();\r\n      }\r\n    }\r\n  }\r\n\r\n  PVector[] getSeparationForces() {\r\n    int n = nodes.size();\r\n    PVector[] separateForces=new PVector[n];\r\n    int[] nearNodes = new int[n];\r\n    Node nodei;\r\n    Node nodej;\r\n    for (int i=0; i&lt;n; i++) {\r\n      separateForces[i]=new PVector();\r\n    }\r\n    for (int i=0; i&lt;n; i++) {\r\n      nodei=nodes.get(i);\r\n      for (int j=i+1; j&lt;n; j++) { nodej=nodes.get(j); PVector forceij = getSeparationForce(nodei, nodej); if (forceij.mag()&gt;0) {\r\n          separateForces[i].add(forceij);\r\n          separateForces[j].sub(forceij);\r\n          nearNodes[i]++;\r\n          nearNodes[j]++;\r\n        }\r\n      }\r\n      if (nearNodes[i]&gt;0) {\r\n        separateForces[i].div((float)nearNodes[i]);\r\n      }\r\n      if (separateForces[i].mag() &gt;0) {\r\n        separateForces[i].setMag(maxSpeed);\r\n        separateForces[i].sub(nodes.get(i).velocity);\r\n        separateForces[i].limit(maxForce);\r\n      }\r\n    }\r\n    return separateForces;\r\n  }\r\n  PVector getSeparationForce(Node n1, Node n2) {\r\n    PVector steer = new PVector(0, 0);\r\n    float sq_d = sq(n2.position.x-n1.position.x)+sq(n2.position.y-n1.position.y);\r\n    if (sq_d&gt;0 &amp;&amp; sq_d&lt;sq_desiredSeparation) {\r\n      PVector diff = PVector.sub(n1.position, n2.position);\r\n      diff.normalize();\r\n      diff.div(sqrt(sq_d)); \/\/Weight by distacne\r\n      steer.add(diff);\r\n    }\r\n    return steer;\r\n  }\r\n  PVector[] getEdgeCohesionForces() {\r\n    int n = nodes.size();\r\n    PVector[] cohesionForces=new PVector[n];\r\n    for (int i=0; i&lt;nodes.size(); i++) {\r\n      PVector sum = new PVector(0, 0);\r\n      if (i!=0 &amp;&amp; i!=nodes.size()-1) {\r\n        sum.add(nodes.get(i-1).position).add(nodes.get(i+1).position);\r\n      } else if (i == 0) {\r\n        sum.add(nodes.get(nodes.size()-1).position).add(nodes.get(i+1).position);\r\n      } else if (i == nodes.size()-1) {\r\n        sum.add(nodes.get(i-1).position).add(nodes.get(0).position);\r\n      }\r\n      sum.div(2);\r\n      cohesionForces[i] = nodes.get(i).seek(sum);\r\n    }\r\n    return cohesionForces;\r\n  }\r\n  \/\/void renderShape() {\r\n  \/\/  beginShape();\r\n  \/\/  for (int i=0; i&lt;nodes.size(); i++) {\r\n  \/\/    vertex(nodes.get(i).position.x, nodes.get(i).position.y);\r\n  \/\/  }\r\n  \/\/  endShape(CLOSE);\r\n  \/\/}\r\n  void renderLine() {\r\n    beginShape();\r\n    for (int i=0; i&lt;nodes.size(); i++) {\r\n      PVector p1 = nodes.get(i).position;\r\n      curveVertex(p1.x, p1.y);\r\n      \/\/point(p1.x,p1.y);\r\n    }\r\n    endShape(CLOSE);\r\n\r\n    \/\/for (int i=0; i&lt;nodes.size()-1; i++) {\r\n    \/\/  PVector p1 = nodes.get(i).position;\r\n    \/\/  PVector p2 = nodes.get(i+1).position;\r\n    \/\/  line(p1.x, p1.y, p2.x, p2.y);\r\n    \/\/  if (i==nodes.size()-2) {\r\n    \/\/    line(p2.x, p2.y, nodes.get(0).position.x, nodes.get(0).position.y);\r\n    \/\/  }\r\n    \/\/}\r\n  }\r\n}\r\nclass Node {\r\n  PVector position;\r\n  PVector velocity;\r\n  PVector acceleration;\r\n  float maxForce;\r\n  float maxSpeed;\r\n  Node(float x, float y, float mF, float mS) {\r\n    acceleration = new PVector(0, 0);\r\n    velocity =PVector.random2D();\r\n    position = new PVector(x, y);\r\n    maxSpeed = mF;\r\n    maxForce = mS;\r\n  }\r\n  void applyForce(PVector force) {\r\n    acceleration.add(force);\r\n  }\r\n  void update() {\r\n    velocity.add(acceleration);\r\n    velocity.limit(maxSpeed);\r\n    velocity.limit(maxSpeed);\r\n    position.add(velocity);\r\n    acceleration.mult(0);\r\n    if (position.x == width-200 || position.x == 200) {\r\n      velocity.x = 0;\r\n      maxForce = 0;\r\n    }\r\n    if (position.y == height-200 || position.x == 200) {\r\n      velocity.y = 0;\r\n      maxForce = 0;\r\n    }\r\n  }\r\n  PVector seek(PVector target) {\r\n    PVector desired = PVector.sub(target, position);\r\n    desired.setMag(maxSpeed);\r\n    PVector steer = PVector.sub(desired, velocity);\r\n    steer.limit(maxForce);\r\n    return steer;\r\n  }\r\n}\r\n<\/code><\/pre>\n<p><code><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; My interpretation of the blob family prompt manifested itself into an exploration of the differential growth algorithm. After dong some research on the algorithm itself and code tracing a few different implementations, I landed on Alberto Giachino&#8217;s Differential line growth &hellip; <a href=\"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/bookoobread\/10\/04\/bookoobread-blobfamily\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;bookooBread &#8211; BlobFamily&#8221;<\/span><\/a><\/p>\n","protected":false},"author":12,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[13],"tags":[],"_links":{"self":[{"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/posts\/1416"}],"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\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/comments?post=1416"}],"version-history":[{"count":3,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/posts\/1416\/revisions"}],"predecessor-version":[{"id":1425,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/posts\/1416\/revisions\/1425"}],"wp:attachment":[{"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/media?parent=1416"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/categories?post=1416"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/60-428\/f2021\/wp-json\/wp\/v2\/tags?post=1416"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}