We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpOther Libraries › Avoid overlap in VerletParticles in toxiclibs
Page Index Toggle Pages: 1
Avoid overlap in VerletParticles in toxiclibs (Read 1397 times)
Avoid overlap in VerletParticles in toxiclibs
Mar 14th, 2010, 3:38pm
 
I am trying to create a force directed graph with toxiclibs, using VerletParticle2D and VerletSpring2D for my nodes and edges respectively.  My one problem is keeping the particles which aren't connected to each other by a spring from overlapping and hiding part of the data.  Is there a good way to avoid overlaps?  I don't need anything particularly "realistic," as far as collisions go, just a way to make sure all my particles are visible.

Toxiclibs is a wonderful library, and I am delighted to have found it.  Any help would be most appreciated.

FYI: At the moment the nodes are rendered as ellipses with their height and width based on the text displayed inside.
Re: Avoid overlap in VerletParticles in toxiclibs
Reply #1 - Mar 14th, 2010, 4:19pm
 
Hey jabauer, glad to hear you like the libs. Daniel Shiffman has recently created a condensed version of a force directed graph using toxiclibs for his ITP class. You can see the demo & download the example from here:

shiffman.net/teaching/nature/toxiclibs/

For use cases like this, the library provides an extensible system to create custom spring types and the one used here is a VerletMinDistanceSpring2D. It only is active when its compressed below its normal rest length, but does not contract when longer than needed. So in a way it's a "one-way" spring... Hope that helps!
Re: Avoid overlap in VerletParticles in toxiclibs
Reply #2 - Mar 14th, 2010, 8:48pm
 
@toxi
Many thanks for the quick reply.  I had seen Shiffman's code and was adapting it for my project.  I tried the MinDistance spring as you suggested, but it didn't keep the nodes from overlapping -- maybe I'm doing something wrong.  I've attached the code (I switched back to the VerletSpring2D because with MinDistance the nodes settle too fast to see the physics behind the springs).  If you have a moment, could you run the program?  On most runs the graph settles with one of the nodes obscured.  I've commented out my partial attempt to surround each node with a CircularConstraint.  I wasn't sure if the constraint would move with the node.

Many, many thanks for your time.

import toxi.geom.*;
import toxi.physics2d.*;
import toxi.physics2d.constraints.*;

//reference to the physics world
VerletPhysics2D physics;

ArrayList particles;
ArrayList springs;

HashMap particleTable = new HashMap();

Particle rolloverItem;

PFont f;

void setup( ) {
 size(400, 400);
 smooth( );
 frameRate(30);
 f = createFont("GilSans",10, true);
 
 particles = new ArrayList();
 springs = new ArrayList();
 
 //Initialize the  physics
 physics = new VerletPhysics2D( );
 physics.setGravity(new Vec2D(0, 0.5));
 
 //This is the center of the world
 Vec2D center = new Vec2D(width/2, height/2);
 //these are the world's dimensions (33%, a vector point out from the center in both directions)
 Vec2D extent = new Vec2D(width/3, height/3);
 
 //Set the world's bounding box
 physics.setWorldBounds(Rect.fromCenterExtent(center, extent));
 
 loadData();

}

void loadData( ) {
 createParticle("John", "husband", "2");
 createParticle("Abigail", "wife", "1");
 createParticle("John Quincy", "husband", "3");
 createParticle("Louisa Catherine", "wife", "4");  
 createSpring("1","2");
 createSpring("1","3");
 createSpring("2","3");
 createSpring("3","4");
}


void draw( ) {
 //update the physics world
 physics.update( );
 background(255);
 stroke(0);
 
 rolloverItem = null;
 
 //draw a line between each particle that is connected by a spring
 for (int i = 0; i < springs.size(); i++) {
   VerletSpring2D s = (VerletSpring2D) springs.get(i);
   line(s.a.x, s.a.y, s.b.x, s.b.y);
 }
 
 //display all particles
 for (int i = 0; i < particles.size(); i++) {
   Particle p = (Particle) particles.get(i);
   p.display();
 }
 
/*
 if (rolloverItem !=null) {
   rolloverItem.drawName( );
 }
 if (mousePressed) {
   p1.drawName( );
 }
 */
}

Particle createParticle(String name, String type, String id) {
 Particle p = new Particle(Vec2D.randomVector(), 1, name, type, id);
 particles.add(p);
 particleTable.put(id, p);
 physics.addParticle(p);
 return p;
}

Particle findParticle(String id) {
   Particle p = (Particle) particleTable.get(id);
   if (p == null) {
     return createParticle("bad id", "dummy", id);
   }
   return p;
}

/*
CircularConstraint createConstraint(String id) {
 Particle p = findParticle(id);
 CircularConstraint c = new CircularConstraint(p.pos, 30);
 return c;
} */

VerletSpring2D createSpring(String id1, String id2) {
 Particle p1 = findParticle(id1);
 Particle p2 = findParticle(id2);
 VerletSpring2D s = new VerletSpring2D(p1, p2, 100, 0.01);
 springs.add(s);
 physics.addSpring(s);
 return s;
}


class Particle extends VerletParticle2D {
 
 String name;
 String type;
 String id;
 float w, h;
 
 Particle(Vec2D pos, float g, String name, String type, String id) {
   super(pos, g); //g for gravity
   this.name = name;
   this.type = type;
   this.id = id;
 }
 
 void display( ) {
   if (type == "husband") {
     fill(#009430); //green
   } else {
     fill(#e86a10); //orange
   }
   //fill(175);
   noStroke();
   
   textFont(f);
   float w = textWidth(name) + 15;
   float h = textAscent( ) + textDescent( ) + 10;
/*   if (w > h) {
     r = w;
   } else {
     r = h;
   } */
   ellipse(x, y, w, h);
   
   fill(0);
   textAlign(CENTER, CENTER);
   textFont(f);
   text(name, x, y);
   
   if (mouseInside()) {
     rolloverItem = this;
   }
 }
 
 void drawName( ) {
   fill(0);
   textAlign(CENTER, CENTER);
   textFont(f);
   text(name, x, y);
 }
 
 boolean mouseInside( ) {
   return (mouseX > x - w && mouseX < x + w &&
           mouseY > y - h && mouseY < y + h);
 }
 
 
   
}
Re: Avoid overlap in VerletParticles in toxiclibs
Reply #3 - Mar 14th, 2010, 10:50pm
 
Hey, you were so close with that... I've updated the createSpring() method below and think that solves it (You might have to tweak the rest lengths a bit more to optimize the constellations). The key is to not just create single connections, but link up particles with all others. Don't worry that you're seemingly duplicating some of these connections. The physics engine will ignore those duplicates and only allow 1 spring between 2 particles...

Code:
void createSpring(String id1, String id2) {
 Particle p1 = findParticle(id1);
 Particle p2 = findParticle(id2);
 // connect first particle with all others
 // but use different spring types
 for(Iterator i=particles.iterator(); i.hasNext();) {
   Particle p = (Particle)i.next();
   if (p!=p1) {
     VerletSpring2D s;
     if (p==p2) {
       // create main connection to target particle
       s = new VerletSpring2D(p1, p, 100, 0.01);
       springs.add(s);
     }
     else {
       // ensure min distance to all other nodes
       s = new VerletMinDistanceSpring2D(p1, p, 60, 0.01);
     }
     physics.addSpring(s);
   }
 }
}


Hth!
Re: Avoid overlap in VerletParticles in toxiclibs
Reply #4 - Mar 15th, 2010, 8:57am
 
Worked like a charm.  Thank you once again.
Re: Avoid overlap in VerletParticles in toxiclibs
Reply #5 - Mar 15th, 2010, 10:48am
 
Great! Btw. You might want to reduce the strength of the gravity to avoid nodes ending up in horizontal rows and also add some code to calculate the current bounding box each frame, then move the drawing origin to the center coordinates of that rectangle and adjust the render scale accordingly so that the whole graph is visible. Adjusting this every frame you'll always fill the window as much as possible and also keep the graph from drifting around (caused by the interactions of springs). You can find an example for this all in my Fid.Gen project...
Page Index Toggle Pages: 1