Why is the result of this PVector math different than the primitive? How can I make them the same?

edited April 2018 in Questions about Code

These two blocks of code yield radically different results. For the sake of simplicity I'm just working with the 1 dimension. Why is that?

void PVector(){
a.add(0, 98);
v.add(a.mult(1.0/frameRate));
l.add(v.mult(1.0/frameRate)); // edit <- when this happens, it's multiplying the whole vector and saving it as if it was v = v * 1/x;
a.mult(0);
}

void Primitive(){
a += 98;
v += a * (1/frameRate);
l += v * (1/frameRate);
a = 0;
}

You would think that the PVector l.y would be the same as the primitive float l, but it's not. Run it for a few hundred frames and they are miles apart. The primitive value advances at the correct speed, the PVector value advances very, very slowly.

You can see that after just 30 frames PVector l.y = 11 vs float l = 162. PVector version is just adding by the value of acceleration *(1/frameRate). It's not accumulating to v.

1 0.792847 0.792847
2 1.5435343 2.213922
3 2.2012308 4.1423683
4 2.7871804 6.5059586
5 3.3176618 9.250198
6 3.806187 12.342217
7 4.2596936 15.747873
8 4.683733 19.441088
9 5.0847764 23.412453
10 5.466639 27.649351
11 5.832577 32.14402
12 6.1839776 36.88176
13 6.5261645 41.88294
14 6.857661 47.11915
15 7.179004 52.57995
16 7.4931245 58.278584
17 7.800785 64.210884
18 8.10281 70.375946
19 8.400051 76.774864
20 8.692898 83.404854
21 8.981894 90.266136
22 9.267535 97.35933
23 9.550255 104.68519
24 9.831322 112.257385
25 10.109912 120.06045
26 10.386936 128.1054
27 10.660829 136.36436
28 10.931881 144.83878
29 11.201469 153.54774
30 11.471621 162.52364

This gives the correct output (same as primitive):

void PVector(){
a.add(0, 98);
v.add(a.mult(1.0/frameRate));
PVector temp = new PVector();
temp.add(v);
l.add(temp.mult(1.0/frameRate));
a.mult(0);
}

This gives the wrong output:

void PVector(){
a.add(0, 98);
v.add(a.mult(1.0/frameRate));
PVector temp = new PVector();
temp = v;
l.add(temp.mult(1.0/frameRate));
a.mult(0);
}

Does PVectorA = PVectorB mean that any changes that happen to PVectorA also apply to PVectorB? Also, can someone explain why the original with no temp pvector didn't work? It's really confusing.

Answers

  • Answer ✓

    l.add(v.mult(1.0/frameRate)); != l += v * (1/frameRate);.

    In the 1st statement, the PVector object stored in variable v is being mutated via its method mult()! :-SS

    In the 2nd statement, primitive variable v isn't mutated. L-)
    It is merely accessed in the expression which is assigned to primitive variable l. :-B

    Given the PVector object stored in variable a has already done its job by now, you can set() that w/ the values of v instead, so v's PVector is preserved: *-:)
    l.add(a.set(v).mult(1/frameRate));

  • Answer ✓

    Does PVectorA = PVectorB mean that any changes that happen to PVectorA also apply to PVectorB?

    Yes! B/c after the = assignment operation, both variables will point to the same object; thus they're alias now. ~O)

  • edited April 2018

    Given the PVector object stored in variable a has already done its job by now, you can set() that w/ the values of v instead, so v's PVector is preserved:

    Oh got it, so basically like using a as the temporary vector. set makes a copy. Essentially set is the equivalent of =.

    primitive:

    a = b;

    vector:

    a.set(b);

  • edited April 2018 Answer ✓

    Yup! PVector::set() method assigns the 3 float fields x, y & z from the passed argument to the PVector which had invoked that method. :-B

    https://Processing.org/reference/PVector_set_.html

  • Answer ✓

    At least two things going on here, I think

    v.add(a.mult(1.0/frameRate));
    

    This modifies a.

    v += a * (1/frameRate);
    

    This doesn't.

    If you don't want to modify a then use the static method Pvector.mult(p, n); which will return a new pvector.

    temp = v;
    

    This effectively aliases v as temp. Any changes to v will be reflected in temp. And vice versa.

  • edited April 2018

    If you don't want to modify a then use the static method Pvector.mult(p, n); which will return a new pvector.

    Does this mean:

    temp.mult(v, 1/x);

    creates a new PVector with the name temp and value of v * (1/x)? Does this static method work with the other pvector functions?

    Oh wait, you mean it just returns that value itself instead of modifying the vector but that is not stored to be used later. I got it.

    So it should be

    l.add(PVector.mult(v,1/frameRate));

  • Without testing it, yes.

    The reference on this is terrible because it doesn't show the return values. The javadoc is better iirc.

  • edited April 2018

    If you wish the most performance outta Java, you should avoid creating unnecessary objects. $-)

    Those PVector static methods instantiates a new PVector object, unless we pass an existing 1 as its last target parameter: https://Processing.org/reference/PVector_mult_.html

    So rather than l.add(PVector.mult(v, 1/frameRate));, which creates an unnecessary PVector, pass the a's PVector as its target argument: l.add(PVector.mult(v, 1/frameRate, a));

    Thus, the result of the multiplication of v w/ 1/frameRate is now stored in a's PVector instead. \m/

    Pretty much equivalent of l.add(a.set(v).mult(1/frameRate));, but in reversed order. ;)

  • But passing in a means that a is modified, which you don't want. (It's hard to tell from the code but I'm guessing a has global scope. It's your usual acceleration / velocity / position stuff)

  • Um, a += 98; could be wrong, the addition part. Acceleration due to gravity?

  • edited April 2018

    Oh, the PVectors are in a nested class, local to the function.

    I did have a question about gravity though. Why is it that when you simulate gravity, when things get really close it turns into a gravity cannon and the objects shoot out at near infinite speed? Shouldn't it just get stuck in the gravity well of the other object? I also am having the problem as two objects are circling eachother very closely they accelerate themselves in a direction, it's like they propel eachother from nothing, clearly violating some laws of motion.

  • edited April 2018

    Not that hard to figure out the PVector object stored in variable a didn't need to "remember" its state when its function is invoked.

    The last statement a.mult(0); made it crystal clear he wanted a cleared by the end of it. :P

    Pretty much a was merely a cache for temporarily store the value 98 * 1/frameRate to add() that to PVector v. :-B

    Actually he coulda done away w/ a and just use: v.add(98/frameRate); :-$

  • edited April 2018

    As for the slingshot effect you're seeing, we covered it here recently:

    https://forum.processing.org/two/discussion/27474/better-way-to-make-gravity-work

  • edited April 2018

    Yeah, I've experimented with constraining either the distance or limit() the acceleration. While it "works" to damp the effect, it's kind of more like a hack. I think OP in that thread was right as to the cause that it's due to frame rates as we are approximating the motion to gravity. When acceleration throws a large value the next frame will be too far away from the center to get another huge value in the opposite direction. so it ends up being random which frame lands closest. There's got to be a better way, or some way to mitigate it besides clamping values. I also wonder if the wandering phenomenon is due to clamping values or the slingshot effect.

    Gravity assist for satellites uses the relative motion of the planet body. So it's more like grabbing onto the back of a truck and then letting go and less of a gravity cannon like we're seeing in this kind of frame based simulation.

  • You can calculate more values than you show...

  • edited April 2018

    Yeah, I've tried that a bit but couldn't get it to work. If anything it made the problem worse. Possibly because of timestep issues calculating within for loop. I need to know exactly how long it takes each loop to run, but it happens much faster than 1ms. It seems like it should increase the accuracy, but maybe I'm thinking about the problem wrong.

  • edited April 2018

    Maybe I could try a different method of integration. Does anyone know how to change it for verlet integration?

    I changed it to calculate the average velocity and average acceleration, which I guess is some kind of verlet integration. It still doesn't work. In fact, even if I limit the distance to the radius1 + radius2 meaning they can not get closer than touching, it still has a magnifying oscillation. Each time the spheres pass through each other, they get thrown a little further away until they push each other to infinity distance from each other. Without a distance constrain, as soon as they run into each other they speed to infinity and disappear... There has to be something wrong here.

Sign In or Register to comment.