Designing emergent systems

Designing for Emergence

What is Designing for Emergence?


Emergence as a process occurs in systems with a multiple number of components, in which the components follow simple individual rules, as well as interact with other components.

Coral formation are a great biological example in which the actual individual coral polyps with simple instructions to create complex skeleton structures as each pore in a coral structure is actually an individual polyp.

Here we have a series of distinctly different coral structures but most share the underlying behaviours and incentives.

Casey Reas


Casey Reas and Ben Fry where the originators of the programming langauge called processing. The first version launched in 2001 Java based but with key drawing tool that allowed artists and designers to expore drawing with code in an eaiser format. Processing is not the first language to aim for this but it has the largest adoption of any media focused programming language.

Casey Reas Website

Process Compendium - Casey Reas


The Process Compendium is based around a series of text descriptions of processes that are defined in terms of elements, which are dynamic shapes comprised of forms with one or more behaviours. Reas' approach to generative art builds on the tradition of conceptual artists like Sol LeWitt, who's Wall Drawings consisted of a series of instructions that skilled technicians are required to follow in order to produce the artwork in a gallery. The Process Compendium contains the following library of forms, behaviours and elements.

Process Compendium


Elements are composed with these building blocks. An element is is way to describe a certain configuration of the class. The class can be deployed in different ways to achieve different results. To form a work we need on form for the element to take on and a combination of behaviors. The intersection of these elements is what draws our work to screen.

Forms
Form 1: Circle
Form 2: Line

Behaviors
Behavior 1: Move in a straight line
Behavior 2: Constrain to surface
Behavior 3: Change direction while touching another Element
Behavior 4: Move away from an overlapping Element
Behavior 5: Enter from the opposite edge after moving off the surface
Behavior 6: Orient toward the direction of an Element that is touching
Behavior 7: Deviate from the current direction

Process 13


Bisect a rectangular surface and define the dividing line as the origin for a large group of Element 1. When each Element moves beyond the surface, move its position back to the origin. Draw a line from the centers of Elements that are touching. Set the value of the shortest possible line to black and the longest to white, with varying grays representing values in between.

E1: F1 + B1 + B2 + B3 + B4

Element 1
Form 1: Circle
Behavior 1: Move in a straight line
Behavior 2: Constrain to surface
Behavior 3: Change direction while touching another Element
Behavior 4: Move away from an overlapping Element

Form 1


Form 1: Circle


              class Circle {

                constructor(x, y, radius) {
                  this.x = x;
                  this.y = y;
                  this.radius = radius;
                
                  this.heading = random(PI * 2);
                  this.speed = 1;
                }

                update() {
                  // We call this debug method just so we can see how 
                  // The elements are interacting
                  this.debug();
                }

                debug() {
                  stroke(50);
                
                  push();
                  translate(this.x, this.y);
                  rotate(this.heading);
                  ellipse(0, 0, this.radius, this.radius);
                
                  // Direction we are going
                  line(0, 0, this.radius, 0);
                  pop();
                
                } 
              }
              

Behavior 1


Behavior 1: Move in a straight line


              // Constant linear motion
              behaviour1() {
                // Here we are using sin and cos to modify our speed value
                // to all us to change the x and y coordinates
                let dx = this.speed * cos(this.heading);
                let dy = this.speed * sin(this.heading);
                this.x += dx;
                this.y += dy;
              }
              

Behavior 2


Behavior 2: Constrain to surface


              behaviour2() {
                // Constrain to surface
                if (this.x < this.radius) this.x=this.radius; 
                if (this.y < this.radius) this.y=this.radius;
                if (this.x> width - this.radius) this.x = width - this.radius;
                if (this.y > height - this.radius) this.y = height - this.radius;
              }
              

Behavior 3


Behavior 3: Change direction while touching another Element


              behaviour3() {
                // While touching another, change direction
                // check all other circles objects with this for loop
                for (let i = 0; i < circles.length; i++) {
                  // make sure that the object being checked is not the current object 
                  if(circles[i] !=this) { 
                    // test if the circles are touching with the touching method 
                    if (this.touching(circles[i])) { 
                      // update the heading parameter if this is true 
                      this.heading +=random(-currentAngle, currentAngle); 
                    } 
                  } 
                } 
              }
              

Touching and Distance Method


We can also see that we need to define a method that will test whether a circle is touching another, called touching() this method will need to take another circle as a parameter and return a Boolean value. Here's an outline of what that function might look like:

As you can see, the touching() function also calls for the creation of another function that calculates the distance between (the centres of) two circles. Here's an implementation of that function


              touching(other) {
                // Detect if circles are touching
                return (this.distance(other) < this.radius + other.radius); 
              }
              
              distance(other) {
                // calculate the distance between
                circles return dist(this.x, this.y, other.x, other.y); 
              }
              

Behavior 4


Behavior 4: Move away from an overlapping Elements

We have almost completed all of the behaviours required to implement Process 4, the last behaviour is Behaviour 4, which states that an element should move away from another element that it is touching. Again, we will start by sketching out the method:


              behaviour4() {
                // While touching another, move away from its centre
                for (let i = 0; i < circles.length; i++) { 
                  // If the current circle is not this one 
                  if (circles[i] !=this) { 
                    // If the current circle is touching this one 
                    if (this.touching(circles[i])) {
                      // Calculate the distance to the other circle
                      let d= this.distance(circles[i]);
                      // Calculate the direction in x and y to the other circle 
                      let dx=(circles[i].x - this.x) / d; 
                      let dy=(circles[i].y - this.y) / d;
                      // Move this circle in the opposite direction using the current speed
                      this.x -=this.speed / resist * dx; 
                      this.y -=this.speed / resist * dy; 
                    } 
                  }
                }
              }
              

Process 4


A rectangular surface filled with varying sizes of Element 1. Draw a line from the centers of Elements that are touching. Set the value of the shortest possible line to black and the longest to white, with varying grays representing values in between.

Element 1: F1 + B1 + B2 + B3 + B4

F1: Elements are in the form of Circles
B1: B1: Move in a straight line
B2: Constrain to a surface
B3: Change direction while touching another element
B4: Move away from an overlapping element

Process Compendium with all behaviors




          let circleNum = 15;
          let radiusMin = 20;
          let radiusMax = 50;

          let currentAngle;

          let circles = [];
          let resist = 2;


          function setup() {
            createCanvas(windowWidth, windowHeight);
            background(245);

            for (let i = 0; i < circleNum; i++) {
              circles[i] = new Circle(random(width), random(height), random(radiusMin, radiusMax));
            }

            currentAngle = PI * 2 / 36;


            ellipseMode(RADIUS);
            stroke(50);
            noFill();
          }

          function windowResized() {
            resizeCanvas(windowWidth, windowHeight);
            refresh();
          }

          function refresh() {
            circles = [];
            for (let i = 0; i < circleNum; i++) {
              circles[i] = new Circle(random(width), random(height), random(radiusMin, radiusMax));
            }
          }

          function draw() {

            background(245);
            
            for (let i = 0; i < circles.length; i++) {
              circles[i].update();
            }

          }

          class Circle {
          
            constructor(x, y, radius) {
              this.x = x;
              this.y = y;
              this.radius = radius;

              this.heading = random(PI * 2);
              this.speed = 1;
            }
            
            update() {
              this.behaviour1();
              this.behaviour2();
              this.behaviour3();
              this.behaviour4();
              // this.behaviour5();
              // this.behaviour6();
              // this.behaviour7();

              this.debug();

                // this.form1();
            }

            debug() {

              stroke(50);
              
              push();
              translate(this.x, this.y);
              rotate(this.heading);
              ellipse(0, 0, this.radius, this.radius);

              // Direction we are going
              line(0, 0, this.radius, 0);
              pop();

              stroke(192, 0, 0);

              for (let i = 0; i < circles.length; i++) {
                if (this.touching(circles[i])) {
                  line(this.x, this.y, circles[i].x, circles[i].y);
                }
              }

            }

            form1() {

              for (let i = 0; i < circles.length; i++) {

                if (this.touching(circles[i])) {
                  // Make sure that cirlces are not being draw on top of eachother
                  if (this.distance(circles[i]) > 0) {
                    // Calculate the grey value using the map function based on the distance between the circles
                    stroke(map(this.distance(circles[i]), 0, this.radius + circles[i].radius, 0, 255), 50);
                    // Draw a line between the centres of the circles
                    line(this.x, this.y, circles[i].x, circles[i].y);
                  }
                }

              }

            }

            behaviour1() {
              // Constant linear motion
              let dx = this.speed * cos(this.heading);
              let dy = this.speed * sin(this.heading);
              this.x += dx;
              this.y += dy;
            }

            behaviour2() {
              // Constrain to surface
              if (this.x < this.radius) this.x = this.radius;
              if (this.y < this.radius) this.y = this.radius;
              if (this.x > width - this.radius) this.x = width - this.radius;
              if (this.y > height - this.radius) this.y = height - this.radius;
            }

            behaviour3() {
              // While touching another, change direction
              // check all other circles objects with this for loop
              for (let i = 0; i < circles.length; i++) {
                // make sure that the object being checked is not the current object
                if (circles[i] != this) {
                  // test if the circles are touching with the touching method
                  if (this.touching(circles[i])) {
                    // update the heading parameter if this is true
                    this.heading += random(-currentAngle, currentAngle);
                  }
                }
              }
            }

            behaviour4() {

              // While touching another, move away from its centre
              for (let i = 0; i < circles.length; i++) {
                // If the current circle is not this one
                if (circles[i] != this) {
                  // If the current circle is touching this one
                  if (this.touching(circles[i])) {
                    
                    // Calculate the distance to the other circle
                    let d = this.distance(circles[i]);
                    // Calculate the direction in x and y to the other circle
                    let dx = (circles[i].x - this.x) / d;
                    let dy = (circles[i].y - this.y) / d;
                    // Move this circle in the opposite direction using the current speed
                    this.x -= this.speed / resist * dx;
                    this.y -= this.speed / resist * dy;
                  }
                }
              }

            }

            behaviour5() {
              // Enter from the opposite edge after moving off the surface
              if (this.x > width + this.radius) this.x = -this.radius; //super simple if statements dont need a new block {  }
              if (this.x < -this.radius) this.x = width + this.radius;
              if (this.y > height + this.radius) this.y = -this.radius;
              if (this.y < -this.radius) this.y = height + this.radius;
            }

            behaviour6() {
              // Orient toward the direction of an Element that is touching 
              for (let i = 0; i < circles.length; i++) {
                // If a circle is not this one (not itself)
                if (circles[i] != this) {
                  // If the two circles are touching
                  if (this.touching(circles[i])) {
                    let other = circles[i];
                    // Calculate the direction towards the other circle using the `atan2()` function
                    let direction = atan2(other.y - y, other.x - x);
                    // Calculate the difference between the current heading and the direction towards the other element
                    let delta = direction - this.heading;
                    // Check to see which way would be shorter to turn
                    if (delta > PI) delta -= TAU;
                    if (delta < -PI) delta += TAU;
                    // Update the heading by moving 1% of the way towards the other element
                    this.heading += delta * 0.01;
                  }
                }
              }
            }

            behaviour7() {
              // Deviate from the current direction
              if (random(1) < 0.5) {
                this.heading += random(-currentAngle, currentAngle);
              }
            }

            touching(other) {
              // 	Detect if circles are touching
              return (this.distance(other) < this.radius + other.radius);
            }

            distance(other) {
              // 	calculate the distance between circles
              return dist(this.x, this.y, other.x, other.y);
            }

          }
          

Adding Behaviours to Process Compendium Challenge


Here

Unit survey


Survey Link

Can you answer these questions?


What is your design concept?
What inspired you?
What is your interaction design?

Assignment due: