How to make a sliding member in Processing with particle systems - spring

I am simulating particle system in Processing. Based on Daniel Shiffman’s Nature of Code book, I did a spring and then I started experimenting with sliders to do one that has longer or shorter length based on a slider.
Now, I am trying to make one that slides by the slider, the two particles move to the same direction of the two particles.
I did it with the PVector add, finding the new position and drawing the node, but it doesn’t work when I have multiple members and one is affected by the others.
I need to apply a force to do this: see applyForce() function.
void update(float distance) {
PVector force = PVector.sub(b.location, a.location);
float d = force.mag();
float x = d - distance;
//direction of the force
force.normalize();
force.mult(-1 * k* x/mass);
//apply to one node
b.applyForce(force);
force.mult(-1);
//apply opposite to the other node
a.applyForce(force);
}
//Newton's law: F = M * A
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acceleration.add(f);
}
Check the diagram below:
(a) is what I want to have, (b) is how it's doing it now.
In the first example the length is the same and the members slides (both particles).
In the second the length is bigger and does not slide
Please let me know if you know how to apply a force that slides the member.
Thank you

If I understood correctly, you're trying to do a few things:
change the spring's length
translate the spring's endpoints in the spring's direction
control the above parameters using sliders
The first part is trivial since the Spring object has a len property.
The second involves a bit of vector math:
the direction of a line is the subtraction of it's two end points
a vector can be scaled easily to any length by normalising it first (reducing it so it's length is equal to 1.0) and then multiplying by a scalar value.
A vector can be translated by simply adding another vector to itself
Here is a commented sketch implementing the points above:
//sliders to control spring rest length and translation
Slider rlength = new Slider("rest length", 5, 5, 200, 20, 50, 250, 100, false);
Slider translate = new Slider("translate", 5, 30, 200, 20, -10, 10, 0, false);
Spring spring = new Spring(new Bob(75,350),new Bob(350,75),(int)rlength.value);
void setup(){
size(400,400);
spring.k = 0.01;//tweak elasticity
}
void draw(){
// update
//update sliders
rlength.update(mouseX,mouseY,mousePressed);
translate.update(mouseX,mouseY,mousePressed);
//update spring
spring.a.update();
spring.b.update();
spring.update();
//make both points draggable
spring.a.drag(mouseX, mouseY);
spring.b.drag(mouseX, mouseY);
//draw
background(255);
rlength.draw();
translate.draw();
spring.display();
}
//handle mouse events for spring points dragging
void mousePressed() {
spring.a.clicked(mouseX, mouseY);
spring.b.clicked(mouseX, mouseY);
}
void mouseReleased() {
spring.a.stopDragging();
spring.b.stopDragging();
}
//handle slider events
void onSliderUpdate(Slider s){
if(s == rlength) spring.len = rlength.value;
if(s == translate){
//compute the direction of the spring by subtracting the two points
PVector direction = PVector.sub(spring.a.location,spring.b.location);
//normalize the vector -> it will not have a length/magnitude of 1.0, but will still point in the line direction
direction.normalize();
//scale or multiply the normalized vector to the translation amount
direction.mult(translate.value);
//finally, add the result to each spring point, essentially offsetting/translating
spring.a.location.add(direction);
spring.b.location.add(direction);
}
}
//Slider
class GUIElement{
float w,h,x,y;//width, height and position
color bg = color(200);//background colour
color fg = color(0);//foreground colour
String label;
GUIElement(String label,float x,float y,float w,float h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.label = label;
}
void update(int mx,int my,boolean md){}
void draw(){}
}
class Slider extends GUIElement{
float min,max,value,pvalue;//slider values: minimum, maximum and current
float cx,pw = 20;//current slider picker position, picker width
boolean updating,liveDrag = true,isInt = false;
//label to display on slider, it's position(x,y), size(w,h) and values(min, max and default/current)
Slider(String label,float x,float y,float w,float h,float min,float max,float value,boolean isInt){
super(label,x,y,w,h);
this.min = min;
this.max = max;
this.value = value;
this.isInt = isInt;
cx = map(value,min,max,x,x+w);
}
void update(int mx,int my,boolean md){
if(md){
if((mx >= x && mx <= (x+w)) &&
(my >= y && my <= (y+h))){
cx = mx;
value = map(cx,x,x+w,min,max);
updating = true;
if(liveDrag){
boolean updated = (isInt ? ((int)value != (int)pvalue) : (value != pvalue));
if(updated){
pvalue = value;
onSliderUpdate(this);
}
}
}else updating = false;
}else{
if(updating){
updating = false;
onSliderUpdate(this);
}
}
}
void draw(){
pushStyle();
noStroke();
fill(bg);
rect(x,y,w,h);
fill(fg,64);
rect(x,y,cx-x,h);//this displays a rect that stretches based on the value
fill(0);
text(label+": "+(isInt ? (int)value : value),x+pw,y+h*.75);
popStyle();
}
String toString(){
return label + ":" + value;
}
}
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com
// Bob class, just like our regular Mover (location, velocity, acceleration, mass)
class Bob {
PVector location;
PVector velocity;
PVector acceleration;
float mass = 12;
// Arbitrary damping to simulate friction / drag
float damping = 0.95;
// For mouse interaction
PVector dragOffset;
boolean dragging = false;
// Constructor
Bob(float x, float y) {
location = new PVector(x,y);
velocity = new PVector();
acceleration = new PVector();
dragOffset = new PVector();
}
// Standard Euler integration
void update() {
velocity.add(acceleration);
velocity.mult(damping);
location.add(velocity);
acceleration.mult(0);
}
// Newton's law: F = M * A
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acceleration.add(f);
}
// Draw the bob
void display() {
stroke(0);
strokeWeight(2);
fill(175);
if (dragging) {
fill(50);
}
ellipse(location.x,location.y,mass*2,mass*2);
}
// The methods below are for mouse interaction
// This checks to see if we clicked on the mover
void clicked(int mx, int my) {
float d = dist(mx,my,location.x,location.y);
if (d < mass) {
dragging = true;
dragOffset.x = location.x-mx;
dragOffset.y = location.y-my;
}
}
void stopDragging() {
dragging = false;
}
void drag(int mx, int my) {
if (dragging) {
location.x = mx + dragOffset.x;
location.y = my + dragOffset.y;
}
}
}
// Nature of Code 2011
// Daniel Shiffman
// Chapter 3: Oscillation
// Class to describe an anchor point that can connect to "Bob" objects via a spring
// Thank you: http://www.myphysicslab.com/spring2d.html
class Spring {
// Location
PVector anchor;
// Rest length and spring constant
float len;
float k = 0.2;
Bob a;
Bob b;
// Constructor
Spring(Bob a_, Bob b_, int l) {
a = a_;
b = b_;
len = l;
}
// Calculate spring force
void update() {
// Vector pointing from anchor to bob location
PVector force = PVector.sub(a.location, b.location);
// What is distance
float d = force.mag();
// Stretch is difference between current distance and rest length
float stretch = d - len;
// Calculate force according to Hooke's Law
// F = k * stretch
force.normalize();
force.mult(-1 * k * stretch);
a.applyForce(force);
force.mult(-1);
b.applyForce(force);
}
void display() {
strokeWeight(3);
stroke(0);
line(a.location.x, a.location.y, b.location.x, b.location.y);
ellipse(a.location.x, a.location.y,10,10);
ellipse(b.location.x, b.location.y,10,10);
}
}

Related

How can I create points, display them and store them in some sort of Array

I want to create points and then store them in an array. I'm doing this to put a linear regression through my data points afterwards. So I need to be able to cycle through all my points.
I could not find anything like that on the web for processing and as I was not really able to do it, I need your help. Here is my approach, but it doesn't seem to work:
ArrayList<dataPoint> dataPoints = new ArrayList<dataPoint>();
void setup(){
size(1000, 1000);
background(255);
}
void draw(){
for (int i = 1; i == dataPoints.size(); i++) {
// An ArrayList doesn't know what it is storing so we have to cast the object coming out
dataPoint Point = dataPoints.get(i);
Point.display();
}
}
void mousePressed() {
dataPoints.add(new dataPoint(mouseX, mouseY));
}
class dataPoint {
float x;
float y;
dataPoint(int tempX, int tempY) {
x = tempX;
y = tempY;
}
void display() {
strokeWeight(10);
stroke(255,0,0);
point(x,y);
}
}
I would like to have a program to create points and store them in an array (or something similar, that you can cycle through).
Most of your code makes sense, there are only two gotchas I could spot that may prevent you from cycling through all your points and visualising them:
your condition is will go to an array index out of bounds: try for (int i = 0; i < dataPoints.size(); i++)
remember to clear the frame, otherwise you're drawing on top of the same dots over and over again
Remember array indices start at 0 in Processing/Java (and likewise the last index will not be the size() of your array, but the 1 less, hence the < in the for condition)
Here is your code with the above tweaks:
ArrayList<dataPoint> dataPoints = new ArrayList<dataPoint>();
void setup(){
size(1000, 1000);
}
void draw(){
background(255);
for (int i = 0; i < dataPoints.size(); i++) {
// An ArrayList doesn't know what it is storing so we have to cast the object coming out
dataPoint Point = dataPoints.get(i);
Point.display();
}
}
void mousePressed() {
dataPoints.add(new dataPoint(mouseX, mouseY));
}
class dataPoint {
float x;
float y;
dataPoint(int tempX, int tempY) {
x = tempX;
y = tempY;
}
void display() {
strokeWeight(10);
stroke(255,0,0);
point(x,y);
}
}
Note that Processing has a handy PVector class (which has x,y properties) so you could do something like this:
ArrayList<PVector> dataPoints = new ArrayList<PVector>();
void setup(){
size(1000, 1000);
strokeWeight(10);
stroke(255,0,0);
noFill();
}
void draw(){
background(255);
beginShape();
for (int i = 0; i < dataPoints.size(); i++) {
PVector point = dataPoints.get(i);
vertex(point.x,point.y);
}
endShape();
}
void mousePressed() {
dataPoints.add(new PVector(mouseX, mouseY));
}
This a bit of a detail, but I recommend to following Java Naming Convention to keep the code consistent. (For example: renaming the dataPoint class to DataPoint and renaming the Point instance to point)

How to fix this Solar System model on processing?

I am trying to build a solar system model (only with the Earth, the Sun and the Moon) on Processing (version 3.4), using the Java Mode. I am new to processing and I have only used Java in this context (hence, I am also new to Java).
I have something which is partially working:
That's my code. First tab:
Planet sun;
void setup() {
size(900, 1200);
sun = new Planet(100, 10, 0);
sun.spawnMoons(1,2);
}
void draw() {
background(0);
translate(750, 900/2);
sun.show();
sun.orbit();
}
Second tab:
class Planet {
float radius;
float distance;
Planet[] planets;
float angle;
float orbitspeed;
Planet(float r, float d, float o) {
radius = r;
distance = 400;
angle = PI;
orbitspeed = o;
}
void orbit() {
angle = angle + orbitspeed;
if (planets != null) {
for (int i = 0; i < planets.length; i++) {
planets[i].orbit();
}
}
}
void spawnMoons(int total, int level) {
planets = new Planet[total];
for (int i = 0; i < planets.length; i++) {
float r = radius/(level*2);
float d = distance/(level*4);
float o = 0.01;
planets[i] = new Planet(r, d/(level*8), o);
if (level < 3) {
int num = 2;
planets[i].spawnMoons(num, level+1);
}
}
}
void show() {
pushMatrix();
fill(255, 100);
rotate(angle);
translate(distance, 0);
ellipse(0, 0, radius*2, radius*2);
if (planets != null) {
for (int i = 0; i < planets.length; i++) {
planets[i].show();
}
}
popMatrix();
}
}
However, my "Moon" is too far from my "Earth". I am trying to fix it, but I can't. Considering the way I built it, if I change the value on 11st line (second tab), it won't solve the problem:
distance = 10;
Considering the way I built it, the distance between the Earth and the Sun it is the same as the distance between the Earth and its moon.
I was able to make the radius of each object proportional to each other. Nonetheless, I am failing to do the same with the distance between them. The line bellow was supposed to keep the proportionality on distance but it fails:
float d = distance/(level*4);
How do I fix this?
Thanks.
This is the error:
Planet(float r, float d, float o) {
radius = r;
distance = 400; //<== here
angle = PI;
orbitspeed = o;
}
In the constructor the distance for each new planet is set at 400, so the logic in spawnMoons() does nothing.
If you apply the changes below, it will work as you want and you can start tweaking ;)
//in setup()
sun = new Planet(100, 400, 0);
//in the planet constructor
distance = d;
//in spawnMoons()
float d = distance/level;
planets[i] = new Planet(r, d, o);

how can I achieve the the effect that growing a set of curves and when any curve occurs others' trail, stop it's growth

i want to achieve like this:
enter image description here
here is my code:
class Particle {
PVector position;
PVector velocity;
PVector acceleration;
PVector initialVelocity;
ArrayList<PVector> path;
boolean life = true;
float q;
public Particle(PVector position, PVector initialVelocity) {
this.position = position;
this.acceleration = new PVector(0, 0);
this.initialVelocity = initialVelocity;
this.velocity = new PVector(0, 0);
path = new ArrayList<PVector>();
}
public void run() {
update();
display();
}
private void update() {
velocity.add(acceleration);
velocity.limit(maxspeed);
trail.add(position.copy());
position.add(velocity);
occurOtherCurves();
arriveBorders();
path.add(position.copy());
acceleration.mult(0);
}
private void display() {
beginShape();
stroke(0);
strokeWeight(1);
noFill();
for(PVector v : path) {
vertex(v.x, v.y);
}
}
public void followField(Field field) {
PVector desired = field.lookup(initialVelocity);
desired.mult(maxspeed);
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxforce);
applyForce(steer);
}
private void applyForce(PVector force) {
acceleration.add(force);
}
public boolean isEndMove(){
return !life;
}
private void arriveBorders(){
if(position.x <= 0 || position.x >= width || position.y <= 0 || position.y >= height)
life = false;
}
private void occurOtherCurves() {
for(PVector p : trail) {
if(p.x == position.x && p.y == position.y) life = false;
}
}
public ArrayList<PVector> getPath() {
return path;
}
}
in occurOtherCurves() function ,ArrayList<PVector> trail is a global variable used to record the positions that all particles passed through,but it didn't work.
thanks for you help.
Stack Overflow isn't really designed for general "how do I do this" type questions. It's for more specific "I tried X, expected Y, but got Z instead" type questions.
But generally, it sounds like what you'd want to do is check whether a new path segment intersects with any existing path segments. If it does, then truncate it at the intersection point (or just don't add it) and stop adding path segments to that path.
You'll have much better luck if you break your problem down into smaller pieces, and take on one piece at a time. Then if you get stuck, you can post a MCVE along with a specific technical question. Good luck.

Basic pool game issue

Im trying to create a basic pool game where one ball hits another and causes that second ball to continue in the same direction at the same speed and move the same amount of distance that the first ball moved. I've so far gotten everything to work except having that second ball continue on. Would anyone be able to help me make this work? I think my problem lies in click == 4 section of the code, but I don't understand how to fix it/add on to it.
Ball cue, billiard;
boolean fired = false;
String msg;
int click;
int steps = 20;
int difx, dify;
Boolean move = false;
void setup(){
msg = "";
size(600,300);
click = 0;
cue = new Ball(30, #FFFFFF);
billiard = new Ball(30, #000000);
}
void draw(){
background(#009900);
if(click == 0){
cue.xpos = mouseX;
cue.ypos = mouseY;
billiard.xpos = -15;
billiard.ypos = -15;
msg = "please place the cue ball";
}else if(click == 1){
billiard.xpos = mouseX;
billiard.ypos = mouseY;
msg = "click again to place billiard ball";
}else if(click ==2){
difx = cue.xpos-billiard.xpos;
dify = cue.ypos-billiard.ypos;
}else if(click == 3){
float cdistance = dist(cue.xpos,cue.ypos,billiard.xpos,billiard.ypos);
if(cdistance>billiard.ballDiam/2){
move = true;
cue.xpos-=difx/steps;
cue.ypos-=dify/steps;
msg = "You got it! Push c on your keyboard to restart";
}else{
move = false;
cue.visible = true;
click = 4;
}
}else if(click == 4){
float cdistance = dist(cue.xpos,cue.ypos,billiard.xpos,billiard.ypos);
if(cdistance<billiard.ballDiam){
if (dist(cue.xpos, cue.ypos, billiard.xpos, billiard.ypos) < sqrt(sq(difx)+sq(dify))) {
move = true;
billiard.xpos-=difx/steps;
billiard.ypos-=difx/steps;
}
}
}
cue.update();
billiard.update();
textSize(20);
text(msg,0,height-5);
}
void mouseClicked(){
if(!move){
click++;
}
}
class Ball{
int xpos, ypos;
int ballDiam;
color myColor;
boolean visible = true;
Ball(int tempdiam,color tempColor){
ballDiam=tempdiam;
myColor=tempColor;
}
void update(){
if(visible){
fill(myColor);
ellipse(xpos,ypos,ballDiam,ballDiam);
}
}
}
Your first stop should be the examples that come with Processing, specifically the CircleCollision example inside the Motion section. That example includes all of the logic for bouncing balls off of one another using more-or-less realistic physics.
/**
* Circle Collision with Swapping Velocities
* by Ira Greenberg.
*
* Based on Keith Peter's Solution in
* Foundation Actionscript Animation: Making Things Move!
*/
Ball[] balls = {
new Ball(100, 400, 20),
new Ball(700, 400, 80)
};
void setup() {
size(640, 360);
}
void draw() {
background(51);
for (Ball b : balls) {
b.update();
b.display();
b.checkBoundaryCollision();
}
balls[0].checkCollision(balls[1]);
}
class Ball {
PVector position;
PVector velocity;
float r, m;
Ball(float x, float y, float r_) {
position = new PVector(x, y);
velocity = PVector.random2D();
velocity.mult(3);
r = r_;
m = r*.1;
}
void update() {
position.add(velocity);
}
void checkBoundaryCollision() {
if (position.x > width-r) {
position.x = width-r;
velocity.x *= -1;
}
else if (position.x < r) {
position.x = r;
velocity.x *= -1;
}
else if (position.y > height-r) {
position.y = height-r;
velocity.y *= -1;
}
else if (position.y < r) {
position.y = r;
velocity.y *= -1;
}
}
void checkCollision(Ball other) {
// get distances between the balls components
PVector bVect = PVector.sub(other.position, position);
// calculate magnitude of the vector separating the balls
float bVectMag = bVect.mag();
if (bVectMag < r + other.r) {
// get angle of bVect
float theta = bVect.heading();
// precalculate trig values
float sine = sin(theta);
float cosine = cos(theta);
/* bTemp will hold rotated ball positions. You
just need to worry about bTemp[1] position*/
PVector[] bTemp = {
new PVector(), new PVector()
};
/* this ball's position is relative to the other
so you can use the vector between them (bVect) as the
reference point in the rotation expressions.
bTemp[0].position.x and bTemp[0].position.y will initialize
automatically to 0.0, which is what you want
since b[1] will rotate around b[0] */
bTemp[1].x = cosine * bVect.x + sine * bVect.y;
bTemp[1].y = cosine * bVect.y - sine * bVect.x;
// rotate Temporary velocities
PVector[] vTemp = {
new PVector(), new PVector()
};
vTemp[0].x = cosine * velocity.x + sine * velocity.y;
vTemp[0].y = cosine * velocity.y - sine * velocity.x;
vTemp[1].x = cosine * other.velocity.x + sine * other.velocity.y;
vTemp[1].y = cosine * other.velocity.y - sine * other.velocity.x;
/* Now that velocities are rotated, you can use 1D
conservation of momentum equations to calculate
the final velocity along the x-axis. */
PVector[] vFinal = {
new PVector(), new PVector()
};
// final rotated velocity for b[0]
vFinal[0].x = ((m - other.m) * vTemp[0].x + 2 * other.m * vTemp[1].x) / (m + other.m);
vFinal[0].y = vTemp[0].y;
// final rotated velocity for b[0]
vFinal[1].x = ((other.m - m) * vTemp[1].x + 2 * m * vTemp[0].x) / (m + other.m);
vFinal[1].y = vTemp[1].y;
// hack to avoid clumping
bTemp[0].x += vFinal[0].x;
bTemp[1].x += vFinal[1].x;
/* Rotate ball positions and velocities back
Reverse signs in trig expressions to rotate
in the opposite direction */
// rotate balls
PVector[] bFinal = {
new PVector(), new PVector()
};
bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y;
bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x;
bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y;
bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x;
// update balls to screen position
other.position.x = position.x + bFinal[1].x;
other.position.y = position.y + bFinal[1].y;
position.add(bFinal[0]);
// update velocities
velocity.x = cosine * vFinal[0].x - sine * vFinal[0].y;
velocity.y = cosine * vFinal[0].y + sine * vFinal[0].x;
other.velocity.x = cosine * vFinal[1].x - sine * vFinal[1].y;
other.velocity.y = cosine * vFinal[1].y + sine * vFinal[1].x;
}
}
void display() {
noStroke();
fill(204);
ellipse(position.x, position.y, r*2, r*2);
}
}
Here's your solution (but you have to re-design your program too):
Ball cue, billiard;
boolean fired = false;
String msg;
int click;
int steps = 20;
int difx, dify;
Boolean move = false;
Boolean continueMoving;
void setup() {
msg = "";
size(600, 300);
click = 0;
cue = new Ball(30, #FFFFFF);
billiard = new Ball(30, #000000);
continueMoving = false;
}
void draw() {
background(#009900);
if (click == 0) {
cue.xpos = mouseX;
cue.ypos = mouseY;
billiard.xpos = -15;
billiard.ypos = -15;
msg = "please place the cue ball";
}
else if (click == 1) {
billiard.xpos = mouseX;
billiard.ypos = mouseY;
msg = "click again to place billiard ball";
}
else if (click ==2) {
difx = cue.xpos-billiard.xpos;
dify = cue.ypos-billiard.ypos;
}
else if (click == 3) {
float cdistance = dist(cue.xpos, cue.ypos, billiard.xpos, billiard.ypos);
if (cdistance>billiard.ballDiam/2) {
move = true;
cue.xpos-=difx/steps;
cue.ypos-=dify/steps;
msg = "You got it! Push c on your keyboard to restart";
}
else {
move = false;
cue.visible = true;
click = 4;
}
}
else if (click == 4)
{
float cdistance = dist(cue.xpos, cue.ypos, billiard.xpos, billiard.ypos);
if (cdistance<billiard.ballDiam)
{
if (dist(cue.xpos, cue.ypos, billiard.xpos, billiard.ypos) < sqrt(sq(difx)+sq(dify))) {
move = true;
continueMoving = true;
billiard.xpos-=difx/steps;
billiard.ypos-=dify/steps;
//print(click);
}
}
}
if (continueMoving)
{
billiard.xpos-=difx/steps;
billiard.ypos-=dify/steps;
}
cue.update();
billiard.update();
textSize(20);
text(msg, 0, height-5);
print(click);
}//draw
void mouseClicked() {
if (!move) {
click++;
}
}
class Ball
{
int xpos, ypos;
int ballDiam;
color myColor;
boolean visible = true;
Ball(int tempdiam, color tempColor) {
ballDiam=tempdiam;
myColor=tempColor;
}
void update() {
if (visible) {
fill(myColor);
ellipse(xpos, ypos, ballDiam, ballDiam);
}
}
}//Ball class
First, instead of using if else statements, use switch. Second, don't put all your code in the draw function. You can put your switch statements there, but shift the code within the conditions to some functions outside the draw function. Google for how other people have created snooker/billiards games, and you'll get to know other different types of logic. Mostly, they will be using a for or while loop to keep the game going. And that loop will be the same as your draw function.
If you're new to StackOverflow, you can click the arrow button pointing up on this page to give me a few points for this answer, and click the tick mark to mark this as the accepted answer.

How to efficiently store past depth pixel data from depthMapRealWorld() method?

I can't get around a peculiar problem with SimpleOpenNI for Processing ao I'm asking for your help.
I'd like to store snapshots of pixel depth data (returned by .depthMapRealWorld() method as PVector arrays) on discrete time intervals, then process them further for a presentation. I tried adding them in an ArrayList, but it seems that the depthMapRealWorld() method is returning only a reference to a current depth data, not a real array. I tried in this sequence:
Just getting the data and adding it in an arraylist. On every call of the update() method the whole arraylist contained the same PVector array, even if the array at the zero position was added many iterations away!
Then I made the PVector array, along with its creation time, part of a class. Rewrote the sketch a little, but it didn't help. All of the arrays in the arraylist werw still the same.
Finally, in the constructor of the class, I "manually" copied the xyz coordinates of every vector from the PVector array into a int array. That seemed to solve the problem - the int arrays in the arraylist are now different from each other. But this solution introduced serious performance problems.
The question is: is there a more efficient way of storing these PVector arrays and retaining their value?
code:
import processing.opengl.*;
import SimpleOpenNI.*;
SimpleOpenNI kinect;
float rotation = 0;
int time = 0;
ArrayList dissolver;
ArrayList<Integer> timer;
int pSize = 10;
Past past;
void setup() {
dissolver = new ArrayList();
timer = new ArrayList();
size(1024, 768, OPENGL);
kinect = new SimpleOpenNI(this);
kinect.enableDepth();
translate(width/2, height/2, -100);
rotateX(radians(180));
stroke(255);
}
void draw() {
background(0);
translate(width/2, height/2, 500);
rotateX(radians(180));
kinect.update();
stroke (255, 255, 255);
past = new Past (kinect.depthMapRealWorld(), time);
if (dissolver.size() == pSize) { //remove the oldest arraylist element if when list gets full
dissolver.remove(0); //
}
if (time % 20 == 0) {
dissolver.add (past);
Past p1 = (Past) dissolver.get (0);
float [][] o2 = p1.getVector();
println ("x coord of a random point at arraylist position 0: " + o2[50000][0]); //for testing
}
if (dissolver.size() == pSize-1) {
//dissolve ();
}
time ++;
}
void dissolve () { //from the previous nonworking version; ignore
for (int offset = 0; offset < pSize-1; offset ++) {
PVector[] offPoints = (PVector[]) dissolver.get (offset);
int offTime = timer.get(offset);
for (int i = 0; i < offPoints.length; i+=10) {
int col = (time-offTime)*2; //why??
stroke (255, 0, col);
PVector currentPoint = offPoints[i];
if (currentPoint.z <1500) {
point(currentPoint.x, currentPoint.y, currentPoint.z); // - 2*(time-offTime) + random(0, 100)
}
}
}
}
class Past {
private PVector [] depth; //should contain this, not int
private float [][] depth1;
private int time;
Past (PVector [] now, int t) {
//should be like this: depth = now;
//clumsy and performancewise catastrophic solution below
depth1 = new float [now.length][3];
for (int i = 0; i< now.length; i+=10) {
PVector temp = now[i];
depth1 [i][0] = temp.x;
depth1 [i][1] = temp.y;
depth1 [i][2] = temp.z;
}
//arrayCopy(now, depth); this didn't work either
time = t;
}
float [][] getVector () {
return depth1;
}
int getTime () {
return time;
}
}
If I understood correctly, you want to store the 3D positions(ArrayList of PVectors) for each frame, right ?
If so, you should be able to simply store PVectors and reference them later.
Here's a basic sketch to illustrate this:
import processing.opengl.*;
import SimpleOpenNI.*;
SimpleOpenNI kinect;
ArrayList<ArrayList<PVector>> frames = new ArrayList<ArrayList<PVector>>();
ArrayList<PVector> frame;
boolean isRecording = true;
boolean isRecFrame;
void setup() {
size(1024, 768, OPENGL);
kinect = new SimpleOpenNI(this);
kinect.enableDepth();
stroke(255);
}
void draw() {
background(0);
translate(width/2, height/2, 500);
rotateX(PI);
translate(0,0,-1000);
kinect.update();
if(isRecording){
isRecFrame = (frameCount % 20 == 0);//record every 20 frames
int[] depthMap = kinect.depthMap();
int steps = 5; // to speed up the drawing, draw every N point
int index;
PVector realWorldPoint;
if(isRecFrame) frame = new ArrayList<PVector>();
for(int y=0;y < kinect.depthHeight();y+=steps)
{
for(int x=0;x < kinect.depthWidth();x+=steps)
{
index = x + y * kinect.depthWidth();
if(depthMap[index] > 0)
{
realWorldPoint = kinect.depthMapRealWorld()[index];
point(realWorldPoint.x,realWorldPoint.y,realWorldPoint.z);
if(isRecFrame) frame.add(realWorldPoint.get());
}
}
}
if(isRecFrame) frames.add(frame);
}else{//playback
ArrayList<PVector> currentFrame = frames.get(frameCount%frames.size());//playback is faster than recording now for testing purposes - add a decent frame counter here at some point
for(PVector p : currentFrame) point(p.x,p.y,p.z);
}
}
void keyPressed(){
if(key == ' ') isRecording = !isRecording;
}
Use the SPACE key to toggle between recording and playback.
The main thing to note is I'm storing a copy of the real world position for each depth pixel (frame.add(realWorldPoint.get());). Another thing to keep in mind is that currently you're storing these coordinates in memory which at some point will fill. If you only store a limited number of frames that should be fine, if not you might want to save to the points to disk. This way you can reuse recordings with other sketches. A basic way would be to sore them in a csv file:
void saveCSV(ArrayList<PVector> pts){
String csv = "x,y,z\n";
for(PVector p : pts) csv += p.x + "," + p.y + "," + p.z + "\n";
saveStrings("frame_"+frameCount+".csv",csv.split("\n"));
}
Another would be to use a more suitable format for point clouds, like PLY.
Saving an ASCII PLY is fairly straight forward:
void savePLY(ArrayList<PVector> pts){
String ply = "ply\n";
ply += "format ascii 1.0\n";
ply += "element vertex " + pts.size() + "\n";
ply += "property float x\n";
ply += "property float y\n";
ply += "property float z\n";
ply += "end_header\n";
for(PVector p : pts)ply += p.x + " " + p.y + " " + p.z + "\n";
saveStrings("frame_"+frameCount+".ply",ply.split("\n"));
}
You can later open/explore/process these files with tools like MeshLab.

Resources