PDA

View Full Version : Curve a line to reach desired distance from its base?


Clox
05-27-2008, 08:00 AM
I have a line, with its base at a specified point. It rotates to always point at the mouseCursor.
Also, when the distance of the cursor compared to the line-base is smaller than the line-length I want the line to curve so that its tip is on the cursor.

I have almost gotten this to work..
When the curveValue of the line is 1, it is fully curved meaning it goes around 360 degrees, and when its at 0 it's perfectly straight.

So I have tried setting the curve like this:


curveValue=(1-mouseDistance/lineLength);
if (curve<0)
curve=0;


This almost does the trick but not quite. It only touches the cursor when the the distance is zero or equal to the lineLength.
You can see the result here. (http://img80.imageshack.us/my.php?image=curvelineie1.swf)
And as you can see I'm not using curveTo, I'm using lineTo's with a set amount of segments. I set it to 5 so you can see what I mean.

If anyone can tell me how to set the curveValue so that the tip always is at the cursor when distance is smaller than lineLength it would be greatly appreciated.

Thanks in advance =)

amarghosh
05-27-2008, 08:24 AM
its hard to tell without seeing some code

lordofduct
05-27-2008, 08:27 AM
I don't know how you're drawing the curve. But I can tell you this much...

if the magnitude of curvature is a constant change from 0 to 1, the distance from the end point to initial point does not change at a constant rate. It actually a trigonmetric issue.

Try doing this, as it will probably shed some light on some things, and I personally love the feeling when I discover little things like this:

set up a small program that draws your line from curvature 0 to curvature 1 at a constant rate. Each time you update the curve, trace the distance between the start and end point. Graph those points and take a look at the curve (where x is the lerp value, and y is the distance). It should look familiar.

Or if your good with trig, look at your code for drawing the curve. You should see the rate of change in their some where.





To give you a hint, think of curves as a segment in a larger circle. The radius of said circle is going from infinitely massive (at curvature 0), and at 1 it is lineLength / 2*PI... or 1 in the significant condition.

helpNeeded=true
05-27-2008, 08:33 AM
seems like your curveValue controls angles, but you set it based on linear dimension. so I guess it might work better with a sine in formula somewhere... e.g., curveValue seems to be not close enough to 1 most of the time, so try something like:curveValue=(1-mouseDistance/lineLength);
curveValue = Math.sin (Math.PI * 0.5 * curveValue);

Clox
05-27-2008, 04:45 PM
if the magnitude of curvature is a constant change from 0 to 1, the distance from the end point to initial point does not change at a constant rate. It actually a trigonmetric issue.

Try doing this, as it will probably shed some light on some things, and I personally love the feeling when I discover little things like this:

set up a small program that draws your line from curvature 0 to curvature 1 at a constant rate. Each time you update the curve, trace the distance between the start and end point.

Yea, I was aware of this. I knew why it didn't give me the result i wanted but i didnt know how to fix it..

seems like your curveValue controls angles, but you set it based on linear dimension. so I guess it might work better with a sine in formula somewhere... e.g., curveValue seems to be not close enough to 1 most of the time, so try something like:curveValue=(1-mouseDistance/lineLength);
curveValue = Math.sin (Math.PI * 0.5 * curveValue);
I tried what you suggested but the result was about the same as before..


After having posted this, I played around a lot with it, trying various things.
the way it is now, the curveValue goes from 0 to 1 in a constant change compared to the distance divided by length. I thought maybe I could get a better result if i added a third point in the middle of the line, and changing it to something else than 0.5, so that it would give a different result.

I let the inner half of the line have a curve from 1 to 0.6 and the outer half 0.6 to to 0.
This gave the desired effect for the inner half but not the outer. Tested squareRooting the outer half and it's practically perfect now =)

So it looks like this now:


if (mouseDistance>=lineLenght/2) {
curveValue=Math.sqrt(1-(mouseDistance-lineLength/2)/(lineLength/2))*.6;
} else {
curveValue=((1-(mouseDistance)/(lineLength/2))*.4+.6);
}
if (!curveValue>0) {
curveValue=0;
}


here (http://img59.imageshack.us/my.php?image=rotlinejx3.swf)'s the new version

I dont know why it works, but at least it does =)
So if anyone could shed some more light on this that would be cool =)
And I wouldn't mind a more accurate result of this though it's good enough for what I need it for.

And thanks for the answers so far. Really appreciated =)

lordofduct
05-27-2008, 07:10 PM
helpneeded was kinda close... but no cigar.

remember my hint? Well I was referring to tangent which starts at 1 and shoots to infinity at a rate of sin/cos. So you can use either sin or cos to find your answer... but that's not quite it.

You're going in the inverse... what is the inverse of sin and cos? arcSin and arcCos. You can use either or. Depending on if you use 1 -, or just take the value as is and limit it in the 0 to 1 range, will decide which one you should use. If you google some images of arcSin and arcCos you'll notice some similarities with that graph I said you should try and draw.



As long as what I assume your math is looking like (if you used any trig to draw this arc), this should work. If it doesn't, then you probably drew your arc in a weird manner.

amarghosh
05-28-2008, 10:12 AM
CurveTo (http://www.cdacbangalore.in/~amarghosh/curveTo/)

As Clox seemed reluctant towards posting some code, i thought it's some code worth writing. Thanks Clox for the question because it was a good challenge and i was *really* happy when i finally got it. i had to revise most of my high school trigonometry lessons again....
right click on the movie or click here to view source (http://www.cdacbangalore.in/~amarghosh/curveTo/srcview/index.html)

Clox
06-02-2008, 11:47 AM
CurveTo (http://www.cdacbangalore.in/~amarghosh/curveTo/)

As Clox seemed reluctant towards posting some code, i thought it's some code worth writing. Thanks Clox for the question because it was a good challenge and i was *really* happy when i finally got it. i had to revise most of my high school trigonometry lessons again....
right click on the movie or click here to view source (http://www.cdacbangalore.in/~amarghosh/curveTo/srcview/index.html)

Hey, that is nice.
Sorry i haven't replied until now.

Maybe I should have posted some code but it was too messy and i guess i as too lazy to clean it up =P

I will take a look at your sourcecode and hopefully that should help =)

Clox
06-02-2008, 05:11 PM
Hey, amargosh..
i've adapted your code to what i need, and its working really good, there's just one thing i cant seem to change.

instead of having 5 lines, i want it to be custom.
but i dont get what im missing...

I've changed obvious places like loops ending at 5 and 5*barLength and such.

But I dont know what to do about this part...


var k:Number = Math.sqrt(1 + 4 * (1 + distance / barLength));
var root1:Number = (1 - k) / 4;
var theta1:Number = Math.acos(root1);
var phi:Number = 2 * (Math.PI - theta1);


I've tried experimenting with it but cant seem to get it right.
I hope you (or anyone else) can help me.

Thanks in advance, and thanks for all the help so far =)

amarghosh
06-03-2008, 05:31 AM
That part was obtained by applying some trigonometry and solving the resultant quadratic equation. i will try to come up with a general equation for that later but till then i will give u the concept i used to reach the solution

1. sum of all angles of a convex polygon with n sides is (n - 2) * pi;
2. in this particular case, n = 6 (5 equal sides and the line joining mouse pointer and initial point). hence sum of angles is 4 * pi
EDIT: click and view the picture to get a better idea of what i am talking about;
27603
3. line joining origin and mouse be of length d and remaining segments be of length t
4. the two angles made by d with t will be equal. call them phi;
5. remaining four angles will be equal. call them theta.
so we have
4 * theta + 2 * phi = 4 * pi
or
2 * theta + phi = 2 * pi;


now drop perpendiculars from the four corners (where angle is theta) to the line d.
we can see that d = 2 * (a + b) + t;
where a = t * cos (phi);
and b = t * cos (alpha);
and it can be proved that alpha = theta + phi - pi;
hence:
d = 2(t * cos(phi) + t * cos(theta + phi - pi) ) + t;
substituting 2 * (pi - theta) for phi using trig formulas for cos(A + B), cos(pi - A) and cos(2 * A), we can reduce this to:

4 * cos^2(theta) - 2 * cos (theta) - (1 + d/t) = 0;
which is a quadratic equation of the form
ax^2 + bx + c = 0;
with roots
[-b + sqrt(b^2 - 4 * a * c)] / 2 * a
and
[-b - sqrt(b^2 - 4 * a * c)] / 2 * a
applying this to our equation we get

cos(theta) = [1 - sqrt(1 + 4 * (1 + d/t))] / 4
//[1 + sqrt(1 + 4 * (1 + d/t))] / 4 was neglected by trial and error;
put
k = sqrt(1 + 4*(1 + d/t))
theta = Math.acos((1 - k) / 4);
phi = 2 * (pi - theta);



general equation connecting theta and phi with number of segments n would be:
(n - 1) * theta + 2 * phi = (n - 1) * pi;

u can use this equation and follow a similar approach to find the general equation theta and phi.

lordofduct
06-03-2008, 07:53 AM
::does a little jig::

Clox
06-24-2008, 08:47 AM
Hey, amarghosh. Sorry I haven't replied again until now. I've been busy working for a client, and lots of other stuff.

I tried your approach and it felt like I did everything correctly, I tried it several times, and thought about it for hours but I just couldn't solve it.
Obviously my math-skills aren't as good as yours (which is why im studying more math right now, heh)
I feel awful for asking for asking for more help, do you (or anyone) think you could help me just a little more to get this right?

I really appreciate all the help...

amarghosh
06-24-2008, 09:09 AM
actually i tried to generalize that equation for any value of n.
but i got a **dirty** summation equation in trigonometry which i couldn't solve even after spending a whole day... :(
it was something like this:
if n is even:
d = 2*t* Zigma[k = 0 to (n - 2) / 2] cos alpha_k;
if n is odd:
d = t + 2*t * Zigma[k = 0 to (n - 3) / 2] cos alpha_k;
where Zigma refers to summation symbol and
alpha_k = (2*k + 1 - n)*(pi - theta)/2

i am sorry to give up :(

Clox
06-24-2008, 12:12 PM
I see..
Couldn't imagine this thing would be so complicated =S

I really appreciate all the help, amarghosh =)

I hope someone can solve this...

lordofduct
06-24-2008, 02:18 PM
[edit]
that hack doesn't work as nicely as I had hoped... tired... ugh

basically though the entire arc is Math.acos(dis / maxLength) * 2

you just have to curve a vector through how ever many points you want on that. The downside is dealing with that infinite tangent... and I'm to tired to differentiate that to make it easier for you.


I leave it up though so you can see it... the variables 'li' and 'ang' need to be fixed that is all.

here is my vector2 class: www.lordofduct.com/storage/Vector2.as


import com.lordofduct.math.Vector2;

var test:Sprite = new Sprite();
addChild(test);
test.x = 100;
test.y = 100;

var maxLen:Number = 100;
var n:Number = 5;

stage.addEventListener(Event.ENTER_FRAME, update);

function update(e:Event):void
{
var len:Number = new Vector2(test.mouseX, test.mouseY).length;
if (len > maxLen - .001) len = maxLen - .001;
len /= maxLen;

var angle:Number =Math.atan2(stage.mouseY - test.y, stage.mouseX - test.x);
angle *= 180 / Math.PI;
test.rotation = angle;

var arc:Number = Math.acos(len) * 2;
var sa:Number = arc / n;

var gr:Graphics = test.graphics;
gr.clear();
gr.lineStyle(2, 0x000000);

var bv:Vector2 = Vector2.rotateTo(Vector2.UnitX, -arc);

for (var i:int = 1; i <= n; i++)
{

var v:Vector2 = Vector2.rotateBy(bv, sa * i);
v.normalize();

var ang:Number = sa * i;
var li:Number = Math.sin(ang) / Math.sin(arc) * Math.cos(arc / 2) * maxLen;

gr.lineTo(v.i * li, v.j * li);
}
}


in action as is:
http://www.lordofduct.com/FlashTest/curveTo/curveTo.html

Clox
07-01-2008, 09:40 AM
[edit]
that hack doesn't work as nicely as I had hoped... tired... ugh

basically though the entire arc is Math.acos(dis / maxLength) * 2

you just have to curve a vector through how ever many points you want on that. The downside is dealing with that infinite tangent... and I'm to tired to differentiate that to make it easier for you.


I leave it up though so you can see it... the variables 'li' and 'ang' need to be fixed that is all.


what do you mean? those variables have to be fixed?
i just tried your code and it seems to be working perfectly...
what am i missing...?

thanks a bunch :)

lordofduct
07-01-2008, 11:08 AM
what do you mean? those variables have to be fixed?
i just tried your code and it seems to be working perfectly...
what am i missing...?

thanks a bunch :)
Oh it works, it works pretty good.

But notice that the arclength of the curve is not constant... it gets longer as you come closer to the center. Making it a hack... it isn't proper and if any math gurus came in here, they'd probably look at me like schmuck.

Clox
07-01-2008, 11:18 AM
oh, i see..
i notice its also quite a bit slower than the one i made (more accurate though), im guessing thats because you do 2 sins and 1 con for each segment? is that really necessary since the relative rotation for each segment is the same..?

lordofduct
07-01-2008, 12:53 PM
yeah that Math.sin(arc) and Math.cos(arc / 2) can be solved prior to the loop... that's just left there because it's a bit hacked together. I was just throwing it together in an actionPanel real fast for you.

Clox
07-25-2008, 01:54 PM
I ended up using your method lordofduct, worked well enough and i for my case where total precision wasn't really that important. thanks a lot =)