We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I am not sure if I am missing something specific to the toxi library, but I keep getting a ConcurrentModificationException when I try to control my particles in my secondary window via the mouse in my primary window. The crash seems to be random, sometimes it crashes on one click, sometimes I can drag the particles around for a second or two either way I can;t seem to figure it out. Can anyone help, please?
Particles p;
AttractionBehavior mouseAttractorP;
Vec2D mousePos;
FinalPanel tp;
void setup() {
size(1024, 768, P3D);
frameRate(60);
tp = new FinalPanel(400,400);
surface.setTitle("Touch Panel");
}
void draw() {
background(0, 0, 0);
}
void mousePressed() {
mousePos = new Vec2D(mouseX, mouseY);
if (mouseButton == LEFT) {
// create a new attraction force field around the mouse position
mouseAttractorP = new AttractionBehavior(mousePos, 250, 7);
p.physics.addBehavior(mouseAttractorP);
}
if (mouseButton == RIGHT) {
// create a new repulsion force field around the mouse position
mouseAttractorP = new AttractionBehavior(mousePos, -33, -1);
p.physics.addBehavior(mouseAttractorP);
}
}
void mouseDragged() {
// update mouse attraction focal point
mousePos.set(mouseX, mouseY);
}
void mouseReleased() {
// remove the mouse attraction when button has been released
p.physics.removeBehavior(mouseAttractorP);
}
import toxi.geom.*;
import toxi.physics2d.*;
import toxi.physics2d.behaviors.*;
import toxi.math.*;
import megamu.mesh.*;
class Particles {
final PApplet g;
int NUM_PARTICLES;
VerletPhysics2D physics;
AttractionBehavior mouseAttractor;
float [][] pos;
float eSize;
float xOff;
Delaunay delaunay;
int [] polygons;
Particles(PApplet _p) {
g = _p;
xOff = 0;
NUM_PARTICLES = 666;
eSize = 1;
physics = new VerletPhysics2D();
physics.setDrag(0.7);
physics.setWorldBounds(new Rect(0, 0, width, height));
physics.addBehavior(new GravityBehavior(new Vec2D(-0.00007, -0.0003f)));
for (int i = 0; i<NUM_PARTICLES; i++) {
addParticle();
}
}
void addParticle() {
VerletParticle2D p = new VerletParticle2D(Vec2D.randomVector().scale(133).addSelf(width / 2, height/2));
physics.addParticle(p);
// add a negative attraction force field around the new particle
physics.addBehavior(new AttractionBehavior(p, 13, 0.3f, 3f));
}
void drawParticles() {
// store particles delaunayitions to do delaunay triangulation
pos = new float[NUM_PARTICLES][2];
for ( int i=0; i<NUM_PARTICLES; i++) {
// particle system using verlet integration
VerletParticle2D p = physics.particles.get(i);
g.pushStyle();
g.fill(255, 33);
g.ellipse(p.x, p.y, g.noise(xOff) * 13, g.noise(xOff) * 13);
g.popStyle();
pos[i][0] = physics.particles.get(i).x;
pos[i][1] = physics.particles.get(i).y;
}
}
// delaunay triangulation logic taken from here :
// http://www.openprocessing.org/sketch/43503
void drawLines() {
// delaunay triangulation
delaunay = new Delaunay(pos);
// getEdges returns a 2 dimensional array for the lines
float[][] edges = delaunay.getEdges();
for (int i=0; i<edges.length; i++)
{
// use the edges values to draw the lines
float startX = edges[i][0];
float startY = edges[i][1];
float endX = edges[i][2];
float endY = edges[i][3];
float distance = dist(startX, startY, endX, endY);
// remap the distance to opacity values
float trans = 255-map(distance, 0, 33, 33, 0);
// stroke weight based on distance
// fast invert square root helps for performance
float sw = 3f/sqrt(distance*2);
g.ellipseMode(CENTER);
g.pushStyle();
g.strokeWeight(sw);
g.stroke(g.noise(1)*233);
g.fill(g.random(255), g.random(255), g.random(255));
g.noFill();
g.ellipse(startX, startY, eSize, eSize);
g.popStyle();
//pushStyle();
//stroke(200, 3);
//noFill();
//bezier(startX, startY, endX, mouseY, width/2, mouseY, width/2, height/2);
//popStyle();
}
}
}
class FinalPanel extends PApplet {
int w, h;
public FinalPanel(int _w, int _h) {
super();
w = _w;
h = _h;
PApplet.runSketch(new String[]{this.getClass().getName()}, this);
}
public void settings() {
size(w, h, P3D);
}
public void setup() {
blendMode(ADD);
surface.setTitle("Final Panel");
surface.setLocation(0, 0);
p = new Particles(this);
}
void draw() {
background(0);
p.physics.update();
p.drawParticles();
p.drawLines();
}
}
Answers
I'd say there's a good chance this has something to do with multiple windows and the OpenGL renderer - there's a whole load of stuff in there that assumes there's only window. It's a shame as it's fairly easily fixed.
It is not a problem withe the toxiclibs library it is caused because each window is running in its own thread but they are trying to share the same data
p
. So there will be times when both threads are trying to modify/usep
at the same time, hence the exception. It has nothingOne way to do it without having to worry about synchronizing threads is to move all the code that modifies
p
into the second window.Note:
Unfortunately I can't run your program as it doesn't recognise
import megamu.mesh.*;
or the AttractionBehaviour class so what follows is un-tested. I am also assuming you are using PS3.
Modify the FinalPanal class to. Notice that adding and removing behaviours are now performed in the
pre
method. This is called automatically just before each call todraw
Now we need to modify the mouse event handlers in the main window.
Notice that we are no longer modifying
p
in this window.As I said I have not been able to test this code so I can't guarantee that it will work.
@quark ... I see what you are saying. I stumbled across your G4P library and was thinking I needed to pass my data from one window to the other, was just unsure of how to do that.
I tried your code (there is a slight typo in the FinalPanel class ... AttractionBehavior toAdd = null, toRemove = null; ... <---fixed) and I can get it to run, but the second window isn't recognizing the mouse event.
I noticed your example Showcase allows you to control one window from the other, I am going to try to break down that program to see if I can figure this out for mine. Thanks for your help.
try changing line 24 to
public void pre() {
also put a
println
inside this method to confirm that it is being executed every frame.making that
public
didn't help, I also went ahead and made themouseEvents
public
as well ... no luck.doesn't seem to be executing. added
println
but see nothing in the console.You also need to make the class public i.e.
public class FinalPanel extends PApplet {
@quark ... that was it! I had my constructor set to
public
and failed to set my class topublic
also.At the moment tho, I am failing to receive my mouseEvent for the
if (mouseButton == RIGHT) {}
in the secondary window.I am at least making progress and I thank you greatly for your help!
Again put a
println
inside the if statement.println
is your friend :)@quark ... yes, something I need to be using more often!
anyways, I got it working. My force amount was too low to notice. All is good now. Thank you, your help is much appreciated!
working code below ...
Glad I could help