Design Computation
Render as Slideshow | Accordion

Rule-Based Design and Decisions

Dynamic, interactive form is a key advantage of computational systems.

This course will soon cover the creation of dynamic, behavior-driven systems that have a life of their own, that is, when the “rules” of these systems become complex enough, or are conceived with particular strategies like those from artificial life. Interactivity gives designers some level of control over the outcome, but the unpredictabie nature of the system is the useful part, if it is indeed moving towards an ordered state.

But it’s difficult to save your work. Sometimes, interactivity makes more difficult from a documentation standpoint. When the outcome of a computational system depends on a particular input from the user, we either must accept the output on its own as irreproducable, record the input so we can reproduce it, or accept the system as the object of design.

The last case is within the purview of interaction design and interfaces, and this course will attempt to move beyond the system as design. We want to use any system we encode to design.

Reading

  1. Technical
    In Processing, read pgs 51-59, Control 1: Decisions.
  2. re Computational Biology
    Read pgs 1 to 23 from The Algorithmic Beauty of Plants found at http://algorithmicbotany.org/papers/abop/abop.pdf. This is dense reading, but I think it’s useful to see how biology has used computational rules to model complex form. This article is the foundation for our understanding of Lindenmayer Systems (L-Systems), such as those demonstrated in the Processing examples.
  3. re Artificial Life
    Read the short article from Mathworld on Cellular Automaton.
    Weisstein, Eric W. “Cellular Automaton.” From MathWorld — A Wolfram Web Resource. http://mathworld.wolfram.com/CellularAutomaton.html

Assignment

Click here for the Rule-Based Design Assignment.

Systems from Rules

Systems are built from consistent rules.

If we think about the theories and equations that underlie the science of physics as a set of rules, then what we have is a system that can be encoded to simulate that system. In fact, that’s what programmers have done on many occasions.

There are libraries available for Processing that give us a kind of pseudo-physics, using point masses and springs between them. There are other libraries for different languages, one being the open dynamics library (ODE), that provides rigid-body dynamics. Game libraries are the most common, built for developers of games that mimic physical reality.

We use the generic term “rule” for a condition or governing principle that results in a particular behavior. The representation of that behavior can then be manifest by geometry, motion, etc.

This series of slides presents one of the necessary foundations for encoding rules.

Types of Rules

Conditionals

Simple if-then-else statements. If a condition is met, then do some action, but otherwise, do another action, or nothing.

Constraints

Different types of constraints exist, but in a program, they usually use the same kind of mechanism, which is limiting the value of a variable to a set of values. This can manifest many different ways in a sketch, such as connections between parts being limited in angle or length some how, but the underlying

Growth Rules

These can take the form of a repeated function that manipulates or adds to existing data. The geometric manifestation is either directly or indirectly derived from that data.

Conditional Logic and Control

In a way, computational processes model (or in some cases, mimic) decision-making processes. Mathematical decision-making is fundamentally based on conditional logic, on the comparison of data’s values and types, then responding to the results of those comparisons. It’s easy for a computer to test for things like the equality of two values.

Booleans

Conditional logic is based on “boolean” logic, named after George Boole, who defined a logical algebraic system in the mid 19th Centry. In Processing, we use boolean values (having the value true or false).


boolean johnnyIsADullBoy = true;
boolean johnnyBeGood = false;

Boolean variables are the simplest and most light-weight data type. They are really binary variables, that contain a single bit, either 0 or 1. Most languages have keywords that make dealing with booleans more English-like. In Processing, they are false (for 0) and true (for 1).

There is an entire system of binary arithmetic familiar to all computer scientists and electrical engineers. In binary, 1+1=10, 10+1=11, and 11+1=100. At the lowest level, computers only work in binary, and their internal mechanisms are combinations of transistors that perform addition, multiplication, basic logic operations, etc.

Expressions That Evaluate to Booleans

Like the boolean addition above, there are situations where a statement in Processing can evaluate to true or false, instead of an integer, like 2 + 2 does. These form the basis for conditional statements.

But to us at this point, booleans are important because we can test whether one is true or false.

Conditional Syntax

Conditional statements are those that depend on a boolean value, whether an expression is true or false.

The syntax for every conditional follows one of two templates:


if ( expressionIsTrue ) {
  // Do something here.
}

if ( expressionIsTrue ) {
  // This code is executed if the expression is true.
} else {
  // Otherwise do this code here.
}

Conditionals are more commonly called “if-statements.” They always start with “if”, have a set of parentheses for the expression that evaluates to a boolean, and at least one code block. The ‘else’ block is optional and is executed if the expression in parens is false, or not true.

There is also a shorthand for the first template:


if ( expressionIsTrue ) executeJustThisFunction();

if ( expressionIsTrue )
  executeJustThisFunction();

if ( expressionIsTrue )
  doThis();
else
  doThat();

You can omit the braces { } if the statement that depends on the conditional is just one line (one semicolon).

Comparisons

The easiest boolean expressions are the comparisons.

Comparisons follow the same pattern as in mathematics. We can compare two numbers to check if one is greater than, less than, or equal to the other.

Comparison Operators

Assume x and y are integers:

x == y equal to evaluates to true if x and y have the same value
x != y not equal to evalues to true if x and y have a different value
x > y greater than evaluates to true if x is greater than y
x < y less than evaluates to true if x is less than y
x >= y greater than or equal to evaluates to true if x is greater than or equal to y
x <= y less than or equal to evaluates to true if x is less than or equal to y
x != y does not equal evaluates to true if x and y do not contain the same value

The ones that differ most from mathematical use are == and !=, which are comparison operators. A single equal sign, =, is an assignment operator, that assigns a value to a variable.

Sample Code for Comparison Operators


int x = 1;
int y = 2;

println( "Is x greater than y?");
println( x > y );

println( "Is x less than y?" );
println( x < y );

println( "Is x equal to y?" );
println( x == y );

println( "Does x not equal y?" );
println( x != y );

println( "Is the sum of x and y more than 3?" );
println( x+y > 3 );
// Addition happens first.

Conditionals + Comparisons

Finally, we can encode conditional simple rules.

The comparison expressions on the previous page are placed inside the parentheses of the “if-statements” like so:


int x = 5;
int y = 6;

if( x > y ) {
  println( "ack!" );
} else {
  println( "whatever." );
}

Note: The first part of this conditional is read aloud: “If x is greater than y then print line …”

The Classic Bouncing Ball

This example is rather mundane though.


float ballX;
float ballY;

float speedX;
float speedY;

float ballR = 20;

void setup(){
  size( 700, 400 );
  smooth();
  fill(0);
  
  ballX = width/2;
  ballY = height/2;
  speedX = random(-10,10);
  speedY = random(-10,10);
}

void draw(){
  background(255);
  ellipse( ballX, ballY, ballR, ballR );
  
  ballX += speedX;
  ballY += speedY;
  
  // Uncomment to add pseudo-gravity.
  //speedY += 0.1;
  
  // If the ball's edge reaches a boundary,
  // make it bounce by reversing the speed.
  if( ballX-ballR < 0 ) speedX = -speedX;
  if( ballX+ballR > width ) speedX = -speedX;
  if( ballY-ballR < 0 ) speedY = -speedY;
  if( ballY+ballR > height ) speedY = -speedY;
}

More Boolean Logic

Sometimes conditions are conflated and more complex than just a single true or false.

The study of logic gives us a way of making more complex conditions. These mechanisms are what we need to start applying constraints.

AND

If two comparisons are required for a condition, we can use an AND operator to make sure that both conditions have to be true for the whole condition to be true:

“If x is less than y AND x is greater than Z … "

In Processing-speak:


if ( x < y && x > z ) {
 // Do something here.
}

If we have two conditions A and B (that evaluate to either true or false), here is the “truth table” for AND:
| A | B | A and B (A && B) |
| false | false | false |
| false | true | false |
| true | false | false |
| true | true | true |

OR

If either of two comparisons causes a condition, we can use an OR operator to check for the true of one or the other:

“If x is less than -5 OR x is greater than 5 … "

In Processing-speak:


if ( x < -5 || x > 5 ) {
 // Do something here.
}

If we have two conditions A and B (that evaluate to either true or false), here is the “truth table” for AND:
| A | B | A or B (A || B) |
| false | false | false |
| false | true | true |
| true | false | true |
| true | true | true |

NOT

This simply changes a condition from true to false, or false to true.

boolean theSkyIsGreen = false;

if ( !theSkyIsGreen ) {
println( “Whew. That was close.” );
}

Bouncing Ball With Paddle


float MAX_SPEED = 5;

float ballX;
float ballY;

float speedX;
float speedY;

float ballR = 20;

float paddleWidth = 100;
float paddleTop;

void setup(){
  size( 700, 400 );
  smooth();
  
  ballX = width/2;
  ballY = ballR + 10;
  speedX = random(-MAX_SPEED,MAX_SPEED);
  speedY = random(-MAX_SPEED,MAX_SPEED);
  
  paddleTop = height-20;
  
  fill(0);
}

void draw(){
  background(255);
  ellipse( ballX, ballY, ballR, ballR );
  rect( mouseX-paddleWidth/2, 
    paddleTop, paddleWidth, 10 );
  
  ballX += speedX;
  ballY += speedY;
  
  // Uncomment to add pseudo-gravity.
  speedY += 0.1;
  
  if( ballX-ballR < 0 ) speedX = -speedX;
  if( ballX+ballR > width ) speedX = -speedX;
  if( ballY-ballR < 0 ) speedY = -speedY;
  if( ballY+ballR > paddleTop && 
      ( ballX > mouseX-paddleWidth/2 && 
        ballX < mouseX+paddleWidth/2 ) ) 
    speedY = -speedY;
}

Interactivity With Processing

The Mouse

Processing has simplified ways of making sketches interactive with the mouse and keyboard.

So far we’ve been using two functions, setup() and draw(), by defining our own code blocks associated with those names. These are special, event-driven functions that Processing calls for us. We never explicitly call setup() or draw(). Processing calls setup() when a sketch is started, and then calls draw() periodically until the sketch is over or until we tell it not to.

There are other event-driven functions that we define the same way, that are linked to the mouse and keyboard.

Hello Mouse!

Mouse coordinates and mouse events…

There are several variables available everywhere in a sketch, provided by Processing, that give us the condition of the mouse:

mouseX current x-coordinate of the mouse cursor
mouseY current y-coordinate of the mouse
pmouseX last x-coordinate of the mouse
pmouseY last y-coordinate of the mouse
mousePressed a boolean, true if a mouse button is pressed
mouseButton equal to LEFT, CENTER, or RIGHT

Mouse Events

MOUSE EVENTS

Define these outside of setup() and draw():


  void mouseClicked(){
  // Code here is executed when a mouse button
  // is pressed and released.
}

void mousePressed(){
  // Code here executed when a mouse button
  // is pressed.
}

void mouseMoved(){
  // Executed when the mouse cursor moves.
}

void mouseDragged(){
  // Executed when the mouse moves while a button
  // is pressed.
}

void mouseReleased(){
  // Executed the moment a mouse button is
  // no longer being pressed, but was pressed.
}

A Simple Mouse Example


boolean drawing = false;

void setup(){
  size( 700, 400 );
  smooth();
  strokeWeight( 5 );
}

void draw(){
  if( !drawing )
    background( 255 );
  
  line( mouseX, mouseY, pmouseX, pmouseY );
}

void mousePressed(){
  drawing = !drawing;
}

Hello Keyboard!

Keyboard variables and events work the same way.

key Contains the value of the key pressed.
keyCode Contains special values for shift, alt, etc. See the Processing reference entry here.
keyPressed A boolean, true when a key is pressed.

Keyboard Events

Special functions defined like setup(), draw(), mousePressed(), etc. Called when these events occur.


void keyPressed(){
  // Code executed when a key is pressed. This
  // includes modifiers like shift, ctrl, etc.
}

void keyTyped(){
  // Behaves like keyPressed() except it excludes
  // modifiers like shift, ctrl, etc.
}

void keyReleased(){
  // You can probably guess...
}

An Example from the Processing Reference


// Run this program to learn how each of 
// these functionsrelate to the others 

// Empty draw() to keep the program running...
void draw() { } 

void keyPressed() {
  println("pressed " + int(key) + " " + keyCode);
}

void keyTyped() {
  println("typed " + int(key) + " " + keyCode);
}

void keyReleased() {
  println("released " + int(key) + " " + keyCode);
}