We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Normally I would assume that it makes a shallow copy, I can think of no good reason to make a deep copy, but I have this programming bug and it is making me question my intuition.
(I actually have versions of this code in a few different threads on this forum as I am just picking up this language.)
I have a box and I want to make it fly around the screen in 3 dimensions and rotate while it does it.
(assuming we can get it to work) ignore the counter-intuitive rotations and the fact that the transition between the initial rotation and the addition of the movement is not smooth what so ever, please just focus on the code relevant to lines 48-66.
I want to let the box fly off of the screen, but I don't want to let it go to far, which is why, before I increment its position, I run it through the regulate_boundary_intervention() function.
The problem is that the x and y properties of the location vector aren't being changed, even though you can clearly see that their increments are being calculated by the function correctly. For some reason, each run of the function seems to be resetting the l[] array, which I can only fathom would be the case if PVector is making a deep copy on me.
I also understand that I need to increase the size of my boundary if I want to allow the box to go off-screen. Right now I just want it to move correctly.
int w = 1500;
int h = 800;
PVector location = new PVector(w/2,h/2,0);
PVector last_location=location.get();
float move_scale=50;
PVector rotation = new PVector(0,0,0);
float rotation_scale=0.25;
PVector noises = new PVector(0,0,0);
PVector final_min = new PVector(0,0,-h);
PVector final_max = new PVector(w,h,h/2);
int count=0;
int dance_queue = 60*10;
color box_color = color(40,40,40);
float box_size = ((w+h)/2)/5;
boolean axes=false;
void setup()
{
size(1500,800,P3D);
background(255,255,255);
fill(box_color);
noStroke();
seed_noises();
axes=true;
}
void draw()
{
background(255);
directionalLight(100, 100, 100, 0.5, 0.65, -0.5);
directionalLight(100, 100, 100, 0.5, 0, -1);
float a = 0.01;
noises.add(a,a,a);
if(count<dance_queue)
{
float s = count * rotation_scale*2;
float scale = radians(s);
rotation.x=map(newNoise(noises.x,0,0),-1,1,-scale, scale);
rotation.y=map(newNoise(noises.y,0,0),-1,1,-scale, scale);
count++;
}
else
{
last_location=location.get();
float[] l = location.array();
float[] n = noises.array();
for(int i=0;i<3;i++)
{
float natural_movement = map(newNoise(n[i], 0, 0),-1,1,-move_scale,move_scale);
l[i] = regulate_boundary_intervention(natural_movement,i,a); //point the cube back in the right direction if necessary.
if(i==0)
{
println(l[i]);
}
}
println(l[0]);
location=new PVector(l[0],l[1],l[2]);
rotation.x=map(location.x-last_location.x, location.x-move_scale, location.x+move_scale, -rotation_scale, rotation_scale);
rotation.y=map(location.y-last_location.y, location.y-move_scale, location.y+move_scale, -rotation_scale, rotation_scale);
}
translate(location.x,location.y,location.z);
rotateX(rotation.y);
rotateY(rotation.x);
noStroke();
box(box_size);
if(axes) draw_axes();
}
void seed_noises()
{
int seed=(int) random(6,30);
noises.x=random(1000)*seed-4;
noises.y=random(1000)*seed-2;
noises.z=random(1000)*seed;
}
float regulate_boundary_intervention(float desired_movement, int i, float noise_incr)
{
float[] loc = location.array();
float[] fMin = final_min.array();
float[] fMax = final_max.array();
float new_location = loc[i]+desired_movement;
boolean location_approved = ((new_location>fMin[i]) && (new_location<fMax[i]));
if (location_approved)
{
return new_location;
}
float[] ns = noises.array();
while(!location_approved)
{
ns[i]+=noise_incr;
desired_movement = map(newNoise(ns[i], 0, 0),-1,1,-move_scale,move_scale);
new_location=loc[i]+desired_movement;
location_approved = ((new_location>fMin[i]) && (new_location<fMax[i]));
}
noises=new PVector(ns[0],ns[1],ns[2]);
return new_location;
}
void draw_axes()
{
stroke(0,255,0);
line(-w/2,0,w/2,0);
stroke(255,0,0);
line(0,-h/2,0,h/2);
}
//================================================================
//Chinchbug's Implementation of Ken Perlin's Improved Noise Algorithm
//See <a href="http://mrl.nyu.edu/~perlin/paper445.pdf" target="_blank" rel="nofollow">http://mrl.nyu.edu/~perlin/paper445.pdf</a>
//================================================================
int p[];
//================================================================
float newNoise(float x, float y, float z) {
//returns value between -1 and +1
if (p == null) setupPermutationTable();
int X = floor(x) & 255;
int Y = floor(y) & 255;
int Z = floor(z) & 255;
x -= floor(x);
y -= floor(y);
z -= floor(z);
float u = fade(x);
float v = fade(y);
float w = fade(z);
int A = p[X]+Y;
int AA = p[A]+Z;
int AB = p[A+1]+Z;
int B = p[X+1]+Y;
int BA = p[B]+Z;
int BB = p[B+1]+Z;
return lerp2(w, lerp2(v, lerp2(u, grad(p[AA], x, y, z), grad(p[BA], x-1, y, z)),
lerp2(u, grad(p[AB], x, y-1, z), grad(p[BB], x-1, y-1, z))),
lerp2(v, lerp2(u, grad(p[AA+1], x, y, z-1), grad(p[BA+1], x-1, y, z-1)),
lerp2(u, grad(p[AB+1], x, y-1, z-1), grad(p[BB+1], x-1, y-1, z-1))));
}
//================================================================
float fade(float t) {
return ((t*6 - 15)*t + 10)*t*t*t;
}
//================================================================
float lerp2(float t, float a, float b) {
return (b - a)*t + a;
}
//================================================================
float grad(int hash, float x, float y, float z) {
int h = hash & 15;
float u = h<8 ? x : y;
float v = h<4 ? y : h==12||h==14 ? x : z;
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
//================================================================
void setupPermutationTable() {
int permutation[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194,
233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190,
6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35,
11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168,
68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111,
229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102,
143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18,
169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186,
3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82,
85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183,
170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167,
43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178,
185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12,
191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214,
31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150,
254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78,
66, 215, 61, 156, 180 };
p = new int[512];
for (int i=0; i < 256 ; i++)
p[i] = p[i+256] = permutation[i];
}
Answers
tldr
https://github.com/processing/processing/blob/master/core/src/processing/core/PVector.java#L1004
I really don't get why they waste the memory of having an array in the PVector class...
I read that, but it doesn't answer my question although, now that I read it again it definitely implies deep copy.
changed line 85 from location.array() to location.get().array() and it worked.
and my response to your statement is that its very convenient when you want to perform similar operations on all 3 vector dimensions, so you can just loop through an array.
Yeah but I think it would be better to have it like:
location.array(arrayToFill);
If you have a lot of PVectors to deal with then it's better to avoid get() on runtime. Creating a lot of new objects on runtime can slow things down.
"I really don't get why they waste the memory of having an array in the PVector class"
If array() isn't called, it is only a null reference. Still some memory, but not much. But indeed, providing an array to fill is another option, often used in the JRE libraries.
This is how we calculate how much memory an object uses up. Let's use PVector class:
float
fields. So it's 3 * 4 bytes = 12 bytes.float[]
field, which isnull
99,9% of times.float[]
field would be removed, a PVector would come down to 24 bytes each!I questioned the need for this array back in 2011, see here
I think I also raised it as an issue and was informed that Processing core used internally for speed reasons. Don't know whether that is still the case with Processing 2 & 3
That'd be true if method array() would be commonly used. This post is the 1st time I see some1 even knowing about its existence! @-)
That attempt optimization is simply instantiate a
float[]
the 1st time array() is called.Then reuse that same instance for further array() calls.
That is an excellent idea for data structure classes like IntList, Table, etc.
But not for something as basic as a PVector which is supposed to be instantiated lotsa times. >:P
As I've explained above, it's 32 bytes down to 24 bytes for each PVector if that
float[]
is finally extirpated! :-q@clankill3r's idea to pass an existing
float[]
to method array() is the best optimization for class PVector!Something like this won't even break things?
Yea, that's more like it. Though both overloaded array() methods should return
float[]
:Isn't it a waste of cpu cycles to check if the array has the correct length? Shouldn't it be up to the user to ensure the length... hmmm
I think this makes more sense:
Yes it is. And I cringe when I do it. However, array()'s usage is so rare and transferring x, y, z to it isn't needed all the time, that the added checks won't decrease the performance that much! In the end, It's irrelevant whether checks are present or not, as long as they remove that useless
float[]
field! [-O<