Processing, Tutorials
comments 14

Working with Toxiclibs [Processing, Tutorial]

Toxiclibs BreakCircle by Amnon Owed

Would you like to create what you see in those videos? Well, read on! Because in this article I will show you how you can do just that using Processing and Toxiclibs. As Processing’s biggest open source collection of libraries, Toxiclibs can assist you in areas like geometry, physics, math and color. With so much code candy for the taking, the libs can still be a bit daunting for many people, especially Processing beginners. That’s why – in addition to great functionality and documentation – clear and inspiring examples on how to use the library are so important. Fortunately the collection of code examples bundled with the libs is growing steadily. I hope my examples can add to that and be helpful to those learning how to use this wonderful collection of code which is shared and continuously developed by Karsten Schmidt.

I’ve already shared the two code examples from the first video on my blog. As the video shows these concern creating, picking and dragging polygon shapes (example 1) and the destruction of voronoi tesselated circles (example 2). The full source code and a more detailed explanation of those two examples can be found HERE. In this follow-up I will share the source code for the two brand new examples you see in the second video. This time I’m venturing a little deeper into the physics capabilities of Toxiclibs, more specifically the VerletSpring2D class (example 3) and we will explore a whole new area of the libs, namely the color library (example 4). All of the examples are commented as much as possible. So by running them and looking through the code, you should be able to understand what’s going on. The rest of this blog post can be considered additional background information ranging from general description to specific pointers. Note (06/05/2011): Karsten came up with some useful suggestions to further improve the code, which of course I was happy to apply. This explains why the visuals when running the code may differ ever so slightly from what you see in the movie.

Example 3: The Infinite Rope

What’s better than a rope? Exactly. An INFINITE rope! This example demonstrates both the creation and efficient removal of particles, springs and behaviors. The three pillars of the physics system. Specifically it uses the VerletSpring2D class, which can connect two VerletParticles in space. For simplicity it’s kept 2D, but everything with regard to physics in Toxiclibs also has a 3D equivalent. Let me describe the way it works and some of the specific choices and solutions. When the mouse is dragged a new particle is created and connected to the last one, effectively making a digital rope. Release the mouse to start a new rope. Push behavior is added to each particle to make it look a little more realistic. For aesthetic reasons, the color of every segment is determined by the direction of each spring.

The most important part of the sketch however, is the code that removes off-screen objects. This is absolutely imperative to keep things running smoothly. Let me elaborate on two specific aspects with regard to the removeOffscreen() function. First, it’s running backwards through the for loop! This is because we are removing things from the list. Meaning the list is getting shorter while you are going through it. Therefore you need to go backwards to prevent problems and to make sure you cover every item in the list. Second, notice that I remove behavior(i+1) for particle(i). The reason is that the first behavior on the list is the gravity we added in setup(). Therefore the behavior of particle 1 can be found in position 2 of the behavior list and so on.

//  Toxiclibs Code Example: The Infinite Rope
//  by Amnon Owed (05/05/2011)
//  minor refactorings by Karsten Schmidt (06/05/2011)

import processing.opengl.*;

import toxi.physics2d.behaviors.*;
import toxi.physics2d.*;
import toxi.geom.*;
import toxi.color.*;

VerletPhysics2D physics;
VerletParticle2D prev;

int continuous,current; // variables to create a new continuous line on each mouse drag
 
void setup() {
  size(1280,720,OPENGL);
  physics = new VerletPhysics2D();
  // add gravity in positive Y direction
  physics.addBehavior(new GravityBehavior(new Vec2D(0,0.1)));
  // set the stroke weight of the line
  strokeWeight(2);
}
 
void draw() {
  background(255);
  // update all the physics stuff (particles, springs, gravity)
  physics.update();
 
  // draw a line segment for each spring and change the color of it based on the x position
  for(VerletSpring2D s : physics.springs) {
    // map the direction of each spring to a hue
    float currHue=map(s.b.sub(s.a).heading(),-PI,PI,0,1);
    // define a color in HSV and convert into ARGB format (32bit packed integer)
    stroke(TColor.newHSV(currHue,1,1).toARGB());
    line(s.a.x,s.a.y,s.b.x,s.b.y);
  }
 
  // remove stuff that is off the screen to keep things running smoothly ;-)
  removeOffscreen();
}
 
void removeOffscreen() {
  // remove off-screen springs
  for (Iterator i=physics.springs.iterator(); i.hasNext();) {
    VerletSpring2D s=i.next();
    if (s.a.y > height+100 || s.b.y > height+100) {
      i.remove();
    }
  }
 
  // remove off-screen particles & behaviors
  for (int i=physics.particles.size()-1; i>=0; i--) {
    VerletParticle2D p = physics.particles.get(i);
    if (p.y > height+200) {
      physics.removeParticle(p);
      ParticleBehavior2D b = physics.behaviors.get(i+1);
      physics.removeBehavior(b);
    }
  }
}
 
void mouseDragged() {
  // create a locked (unmovable) particle at the mouse position
  VerletParticle2D p = new VerletParticle2D(mouseX,mouseY);
  p.lock();
  // if there is at least one particle and this is the current continuous line
  if (physics.particles.size() > 0 && continuous == current) {
    // get the previous particle (aka the last in the list)
    VerletParticle2D prev = physics.particles.get(physics.particles.size()-1);
    // create a spring between the previous and the current particle of length 10 and strength 1
    VerletSpring2D s = new VerletSpring2D(p,prev,10,1);
    // add the spring to the physics system
    physics.addSpring(s);
  } else {
    current = continuous;
  }
  // unlock previous particle
  if (prev!=null) {
    prev.unlock();
  }
  // add the particle to the physics system
  physics.addParticle(p);
  // create a forcefield around this particle with radius 20 and force -1.5 (aka push)
  ParticleBehavior2D b = new AttractionBehavior(p,20,-1.5);
  // add the behavior to the physics system (will be applied to all particles)
  physics.addBehavior(b);
  // make current particle the previous one...
  prev=p;
}
 
void mouseReleased() {
  if (prev!=null) {
    prev.unlock();
  }
  continuous++;
}

Example 4: NamedColors

Aesthetically somewhat similar, but technically completely different, this example is meant to demonstrate how to use TColors in general and NamedColors in particular. If Vec2D/Vec3D is the heart of the geometry lib, then you could say TColor is the heart of the color lib. It’s the basis for much greater possibilities such as color ranges, themes and gradients. But to grasp this part of Toxiclibs, I think it’s best to start with a basic example. For this I chose the NamedColors since they are less abstract than working with numbers alone. In the color portion of Toxiclibs there is a list of 143 NamedColors that you can use. They have names like azure, darkturquoise, lavender, orange and last but not least peachpuff. When working with TColors it’s important to remember that you need to convert them into something that can be used in a Processing fill() or stroke() function. In this example you can see that every time the color is actually used, it’s converted into a packed ARGB int using the toARGB() function.

So let me walk through the sketch real quick. In setup() all the names are loaded into an ArrayList of strings for the purpose of sorting them alphabetically. Running the sketch presents you with the full color palette. I’ve applied some ZoomLensInterpolation to bring out the selected color (mouseX) and made it move up and down with the user (mouseY). There are some checks to make sure both the name and it’s background are kept within the boundaries of the screen. The right mouse button changes the background color, while the left mouse button creates a colorWorm at the mouse position. Press any key to toggle the palette on/off, the mouse functionality will keep working. The colorWorm is basically a list of up to 25 points, a direction and a certain color. It starts at the mouse position and then moves randomly, adding new points along the way. Since the directional change is limited to 30 degrees, it will generally keep going into a certain direction instead of wriggling around the same spot. To make it a little smoother all the points are loaded into a Spline2D which is then subdivided. From the vertices that come out, the line is drawn.

//  Toxiclibs Code Example: NamedColors
//  by Amnon Owed (05/05/2011)
//  minor refactorings by Karsten Schmidt (06/05/2011)

import processing.opengl.*;
import toxi.geom.*;
import toxi.color.*;
import toxi.math.*;

ArrayList  names = new ArrayList  ();
ArrayList  worms = new ArrayList  ();

ZoomLensInterpolation zoomLens = new ZoomLensInterpolation();

boolean showColorPalette = true;
int selectedColorID;

// screen center
Vec2D center;

// background color (readonly colors can't be modified)
ReadonlyTColor bg;

void setup() {
  size(1280, 720, OPENGL);
  center = new Vec2D(width/2, height/2);
  // create a list of all the Toxiclibs NamedColors
  names = NamedColor.getNames();
  // sort it alphabetically
  Collections.sort(names);
  textFont(createFont("SansSerif", 28));
  // set zoom lens to a dilating characteristic
  // setting the first parameter to a negative value will create a bundling effect
  zoomLens.setLensStrength(0.45, 1);
  // set the background color to deepskyblue
  bg = NamedColor.getForName("deepskyblue");
}

void draw() {
  // convert the bg color into ARGB color format (32bit packed integer)
  background(bg.toARGB());

  // run through all the worms (backwards cause we are also removing some from the list)
  for (Iterator i=worms.iterator(); i.hasNext();) {
    ColorWorm w = i.next();
    // if the worm's last point is 'off the screen' remove the worm
    // distanceToSquared() is faster than distanceTo() since it avoids
    // the square root calculation and we don't need precise values here...
    if (w.points.get(0).distanceToSquared(center) > 640000) {
      i.remove();
    } 
    else {
      // otherwise update and display the worm
      w.update();
      w.display();
    }
  }

  // set the zoom location based on the normalized mouseX (0.0 .. 1.0 interval)
  float normX=(float)mouseX / width;
  // interpolate focal point to new mouse position (15% step per frame)
  zoomLens.setLensPos(normX, 0.15);
  // determine the selected color based on mouseX
  // by finding which color area contains mouseX
  float focalX=zoomLens.interpolate(0, width, normX);
  for (int i=0, num=names.size()-1; i<=num; i++) {
    float x=zoomLens.interpolate(0, width, (float)i/num);
    float x2=zoomLens.interpolate(0, width, (float)(i+1)/num);
    // select color if focalX is between x and x2
    if (focalX >= x && focalX < x2) {
      selectedColorID=i;
      break;
    }
  }

  // toggle the color palette
  if (showColorPalette) {
    drawColorPalette();
  }

  if (mousePressed) {
    // Create worms with the LEFT mouse button
    if (mouseButton == LEFT) {
      Vec2D mouse = new Vec2D(mouseX, mouseY);
      ReadonlyTColor c = NamedColor.getForName(names.get(selectedColorID));
      worms.add(new ColorWorm(mouse, c));
      // Change the background color with the RIGHT or MIDDLE mouse button
    } 
    else {
      bg = NamedColor.getForName(names.get(selectedColorID));
    }
  }
}

// Press ANY key to toggle the color palette
void keyPressed() {
  showColorPalette = !showColorPalette;
}

class ColorWorm {
  List  points = new ArrayList  ();
  Vec2D direction;
  TColor c;

  ColorWorm(Vec2D origin, ReadonlyTColor c) {
    // at the origin point (mouseX,mouseY)
    points.add(origin);
    // create a copy of the readonly color for later manipulation
    this.c = c.copy();
    // create a random direction
    direction = Vec2D.randomVector();
  }

  void update() {
    // every second frame (not too fast, not too slow)
    if (frameCount % 2 == 0) {
      // create a new point given the last point's coordinates
      Vec2D p = points.get(points.size()-1).copy();
      // rotate the direction randomly somewhere between -30 and 30 degrees
      direction.rotate(radians(random(-30, 30)));
      // create a movement vector in that direction with a random magnitude between 15 and 30
      Vec2D move = direction.getNormalizedTo(random(15, 30));
      // move the point in that direction and with that distance
      p.addSelf(move);
      // add the new point to the list
      points.add(p);
    }

    // truncate at 25 points (remove the oldest point)
    while (points.size () > 25) {
      points.remove(0);
    }
  }

  void display() {
    // need at least 3 points to construct a spline
    if (points.size()>2) {
      // create Spline2D from the points
      Spline2D s = new Spline2D(points);
      // subdivide it by 8 into a list of vertices
      List  vertices = s.computeVertices(8);
      noFill();
      strokeWeight(2);
      // draw a line through all the vertices
      beginShape();
      for (int i=0,num=vertices.size()-1; i<=num; i++) {
        Vec2D v = vertices.get(i);
        // the position in the list determines the transparency of the segment
        c.setAlpha(map(i, 0, num, 0, 1));
        // convert the color into ARGB color format (32bit packed integer)
        stroke(c.toARGB());
        vertex(v.x, v.y);
      }
      endShape();
    }
  }
}

void drawColorPalette() {
  noStroke();

  // display all the colors over the width of the screen
  for (int i=0,num=names.size()-1; i<=num; i++) {
    // determine the color swatch's position & width based on
    // it's relative position and the zoom location (mouseX)
    float x = zoomLens.interpolate(0, width, (float)i / num);
    float x2 = zoomLens.interpolate(0, width, (float)(i+1) / num);
    // convert the color into ARGB color format (32bit packed integer)
    fill(NamedColor.getForName(names.get(i)).toARGB());
    // move the colors vertically with mouseY
    rect(x, mouseY-15, x2-x, 30);
  }

  // get the name of the selectedColor
  String name = names.get(selectedColorID);
  float ascent = textAscent();
  float textwidth = textWidth(name);
  // keep the text and it's background fill within screen boundaries
  float x = min(mouseX, width-textwidth-8);
  float y = min(mouseY + 52, height-4);
  // draw a white text background
  fill(255);
  rect(x, y-ascent-4, textwidth+8, ascent+8);
  // draw a black text
  fill(0);
  text(name, x+4, y);
}

That concludes this round of code sharing. For all things Toxiclibs go to toxiclibs.org. A description of how to install contributed libraries for Processing can be found here. If you would like to know if and how Toxiclibs can help you with your project, but are unsure of where to start, I suggest asking for help in the Processing forum. There are quite a few people over there (including Karsten himself sometimes) who can help you out with advice and maybe even code examples. So good luck and get creative! ;-)

  • http://www.facebook.com/profile.php?id=551931496 Ashley James Brown

    stunning work as always and gt documentation

  • Victor Pardinho

    Hi, can you re-post making the videos didn’t play without a click on the play button?
    because now everytime I open my reader the musics starts to play. I believe other people will have the same problem and I don’t want to delete one of the best site about tech creativity because of this.
    Thank you.

  • http://www.creativeapplications.net Filip

    Done. Sorry about that, my fault :)

  • Victor

    Can you tell, how did you capture the video?

  • Processingparis

    Many thanks for these. We need more tutorials for ToxicLibs.

  • http://amnonp5.wordpress.com/ Amnon Owed

    I always capture image sequences using saveFrame(“/output/seq-####.tga”) and then composite them. The reason for choosing TGA is that it works close to real-time while ensuring 100% quality.

  • Victor

    Thank you!

  • Mtnsaray

    Finally someone made a guided us through toxiclibs, can’t thank you enough

  • Mtnsaray

    Finally someone made a guided us through toxiclibs, can’t thank you enough

  • Anonymous

    couldn’t get infinite single string to work; second one did just fine… am I missing something besides toxi?
    Processing 1.5.1

    Line 54…
    for (int i=physics.particles.size()-1; i>=0; i-) {

    processing.app.SketchException: unexpected token: )
        at processing.mode.java.JavaBuild.preprocess(JavaBuild.java:326)
        at processing.mode.java.JavaBuild.preprocess(JavaBuild.java:197)
        at processing.mode.java.JavaBuild.build(JavaBuild.java:156)
        at processing.mode.java.JavaBuild.build(JavaBuild.java:135)
        at processing.mode.java.JavaMode.handleRun(JavaMode.java:176)
        at processing.mode.java.JavaEditor$20.run(JavaEditor.java:481)
        at java.lang.Thread.run(Thread.java:680)

  • Anonymous

    Thanks!
    got all the other demos to work; infinite line threw a unexpected token error… line 54 – thought I was missing a semi or possibly a bad copy+paste… was there another class besides toxi (set) that I needed. processing.app.SketchException: unexpected token: )

    processing 1.5.1; mac os/x 10.6

    first time to toxi; haven’t worked with processing since ~1.2

  • Anonymous

    couldn’t find VerletSpring2D class, the link goes to a bad page…?

  • http://twitter.com/51joe Joe Stevens

    hi capitaltreasureson Line 54…
    for (int i=physics.particles.size()-1; i>=0; i-) {
    it should be
    for (int i=physics.particles.size()-1; i>=0; i–) {

    that is its missing another minus sign at the end

  • Vicente

    Fairly new to processing but i’ve downloaded and placed the toxiclibs libraries in the folder and all. but it Cannot find a class or type named “VerletPhysics2D” Ive restarted the program and all.