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 & HelpPrograms › wrap-around interpolation
Page Index Toggle Pages: 1
wrap-around interpolation (Read 729 times)
wrap-around interpolation
Aug 16th, 2007, 4:25pm
 
I'm trying to make something accelerate and the slow down to a given rotation. The usual trick I use is:

followX += (targetX-followX)*fraction;

Where fraction would be something like 0.5 and it would make the follower speed up towards the target and slow down.

This trick doesn't work with rotation because you have a point where two angles close together can be at -3 and +3 radians. This results in the equation making it take the long way round.

Take this example:
Code:

float meX = 100;
float followX = 100;
float step = 15;
void setup(){
size(400, 200);
rectMode(CENTER);
frameRate(24);
}
void draw(){
background(255);
fill(255,0,0);
rect(meX, 100, 20, 20);
fill(0);
rect(followX, 100, 20, 20);
if(keyPressed){
switch(key){
case '-':
meX -= step;
if(meX < 0) meX += width;
break;
case '+':
case '=':
meX += step;
if(meX > width-1) meX -= width;
break;
}
}
followX += (meX-followX)*0.2;
}

I'd like the black square to utilise the same trick the red square does of wrapping around to the other side instead of taking the long way of going from one side to the other. But I want the same smooth movement I get from the following equation:

followX += (meX-followX)*0.2;

How can I do this?
Re: wrap-around interpolation
Reply #1 - Aug 16th, 2007, 5:29pm
 
Code:

float meX = 100;
float followX = 100;
float step = 15;
void setup(){
size(400, 200);
rectMode(CENTER);
frameRate(24);
}
float globalX = 0;
void draw(){
translate( globalX, 0 );
background(255);
fill(255,0,0);
rect(meX, 100, 20, 20);
fill(0);
rect(followX, 100, 20, 20);
if(keyPressed){
switch(key){
case '-':
meX -= step;
if(meX < -globalX) globalX += width;
break;
case '+':
case '=':
meX += step;
if(meX > width-1-globalX) globalX -= width;
break;
}
}
followX += (meX-followX)*0.2;
}


F
Re: wrap-around interpolation
Reply #2 - Aug 16th, 2007, 5:29pm
 
Are you writing an Asteroids clone?  I only ask cuz I've been there, done that.  Cheesy

Let me reword your last statement like this:

followX = lerp(followX, meX, 0.2);

Then all you need is a "wrapped" version of lerp() that "changes direction" when the target value is more than half the span of your modular coordinate system.  So change it to:

followX = wraplerp(followX, meX, 0.2, width);

and add this somewhere:

float wraplerp(float a, float b, float t, float w) {
 a += (abs(b-a) > w/2f) ? ((a < b) ? w : -w) : 0;
 return lerp(a, b, t);
}
Re: wrap-around interpolation
Reply #3 - Aug 16th, 2007, 6:24pm
 
Ah - this is in Flash.

I'm asking the question here because I trust you guys with math better than Flash-heads. And in Processing I rarely get a "is it me or is Processing being crap?" And that's been the latter with Flash all week (delayed initialisation of objects a speciality!)

Wink

It's like asteroids but underwater, I flip to a different physics model when you leap out of water - quite fun really. I've also taken the Bresenham algorithm and used it to calculate the vector to push out of pixelated scenery. I know that creates some odd collisions with one particle - but tie those together with springs and it all gets very realistic!

I try to upload the physics engine for sharing when I'm done - I keep dickering with the thing and adding widgets.

I've gone with davbol's route - pushing and popping angles confuses me a bit.

Here's what I was after:
Code:

float theta = 2;
float target = 2;
float step = 0.08;
void setup(){
size(200, 200);
}
void draw(){
background(255);
float x = 100+cos(theta)*100;
float y = 100+sin(theta)*100;
stroke(255,0,0);
line(100, 100, x, y);
x = 100+cos(target)*100;
y = 100+sin(target)*100;
stroke(0);
line(100, 100, x, y);
if(keyPressed){
switch(key){
case '-':
target -= step;
if(target < -PI) target += TWO_PI;
break;
case '+':
case '=':
target += step;
if(target > PI) target -= TWO_PI;
break;
}
}
theta = wrapLerp(theta, target, 0.2);
}
float wrapLerp(float a, float b, float t){
a += (abs(b-a) > PI) ? ((a < b) ? TWO_PI : -TWO_PI) : 0;
return lerp(a, b, t);
}


Edit:

Sorry - thanks
Re: wrap-around interpolation
Reply #4 - Aug 16th, 2007, 8:58pm
 
Okay - sorry 'bout this, my work problem is solved but I still have a bugbear about the mechanics of this. I was going to paste it up on seltar's snippets for future reference but I wanted to be double sure it copes with all circumstances.

davbols method works great on a 0-width scale, and it happily does rotation as well. But on say a -100 to 300 scale, things go awry.
Code:

float targetX = 100;
float followX = 100;
float step = 15;
float minX = -100;
float maxX = 300;
float w;
void setup(){
size(400, 200);
rectMode(CENTER);
frameRate(24);
w = abs(minX - maxX);
println(w);
}
void draw(){
translate(100,0);
background(255);
fill(255,0,0);
rect(targetX, 100, 20, 20);
fill(0);
rect(followX, 100, 20, 20);
if(keyPressed){
switch(key){
case '-':
targetX -= step;
if(targetX < minX) targetX += w;
break;
case '+':
case '=':
targetX += step;
if(targetX > maxX) targetX -= w;
break;
}
}
followX = wrapLerp(followX, targetX, 0.2, 50, 350);
}
float wrapLerp(float a, float b, float t, float minX, float maxX) {
float w = abs(minX-maxX);
a += (abs(b-a) > w*0.5f) ? ((a < b) ? w : -w) : 0;
return lerp(a, b, t);
}

When it flips over either edge the follower lerps to the wrong side of the target. If I pee about with minX some more is misbehaves some more.

Not quite sure what I've missed yet.
Re: wrap-around interpolation
Reply #5 - Aug 16th, 2007, 10:15pm
 
It's not clear to me what you're trying to accomplish -- it appears you want your 'follow' to wrap in a 300-unit coordinate system (50,350) while your 'target' wraps in a 400-unit (0,width) coordinate system?  What does that mean?  What happens in those other 100 coordinate units?  Seems like a really bizarre use!  Cheesy

At any rate, the fix *might* be simple:  All it's doing now is transforming a's world coordinate frame into b's local coordinate frame, so that a is always +/- w/2 from b.  But it does assume that they're at least both using the same origin (0).  Since in your case they're not, you'd need to further correct for that transformation, here's one easy way:

a -= minX; // put a's origin back at 0
a += ...// the existing half-span origin transform
a += minX; // put a's origin back where it was
return lerp(...

Similarly, if your usage gets weird enough to slide b off 0 origin then you may have to correct b also, it's hard to guess.  The question to always ask is:  how do i get a's coordinate in terms of a b-centered coordinate frame?  (and then put it back afterwards)  In such a coordinate frame, the shortest distance from b to a will always be in the right direction.

And if as you show, b's coordinate system is 400 units wide while a's is only 300 units wide, then you'll potentially even want to add a scaling factor in there (400/300ths) or you'd be lerp'ing across the wrong range.  Sorry, that's about as clear as I can be without understanding the use.  Suggested approach:

putAandBinACommonCoordinateSystem();
wraplerp(a,b,t, commonCoordinateSystemModuloBase);
putAandBbackInTheirOwnCoordinateSystems();
Re: wrap-around interpolation
Reply #6 - Aug 17th, 2007, 10:49am
 
Ah ahahahahahah

hah

Code:

followX = wrapLerp(followX, targetX, 0.2, minX, maxX);
// not
followX = wrapLerp(followX, targetX, 0.2, 50, 350);

// note to self - stop using magic numbers


*smacks palm into forehead

doh!
Page Index Toggle Pages: 1