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 WebsiteProcess 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);
}
}
Can you answer these questions?
What is your design concept?
What inspired you?
What is your interaction design?
Assignment due: