Modelling a specific sort of curve
Author 
Message 
Insectoid

Posted: Fri Apr 04, 2014 1:24 pm Post subject: Modelling a specific sort of curve 


I'm starting to build a game very similar to Steambirds. I'm having trouble coming up with a way to recreate the movement planning for the aircraft. I tried using Bezier curves, but they don't quite fit the problem (or I can't manipulate them correctly) and seem like overkill for this task. Anyone got any ideas? 





Sponsor Sponsor



Zren

Posted: Fri Apr 04, 2014 4:35 pm Post subject: RE:Modelling a specific sort of curve 


Constant rotation rate + linear movement. Draw Arc + Draw Line. That might be even harder that beziers though.
Steambirds is deffinitely using a bezier though. 





Insectoid

Posted: Fri Apr 04, 2014 5:08 pm Post subject: RE:Modelling a specific sort of curve 


I think I might have a solution.
Draw the path as a chain of 3 links (4 points) of the same length and with the same angle between them. Clicking & dragging the last point moves the whole thing to satisfy those conditions. These 4 points describe the bezier curve of the flight path. Figuring out a solver for this will be a pain, but I'm confident it will work. 





Insectoid

Posted: Fri Apr 04, 2014 7:52 pm Post subject: RE:Modelling a specific sort of curve 


I've been playing around with it a little and came up with this solution (it's a bit messy). Ended up using a trigonometric bezier to model the path. Click & drag to move the bezier around. I can't figure out a way to make the 2 lines that define the bezier the same length.
Turing:  type Point : record
x : real
y : real
end record
function slope (p : Point ) : real %calculates the slope of the line from (0, 0) to p
result arcsind (p.y/ sqrt (p.x** 2 + p.y** 2))
end slope
function slope2 (x : real, y : real) : real
result arcsind (y/ sqrt (x** 2+y** 2))
end slope2
var p0 : Point %Not really p0. Just used as a guide to show the 'original' direction
var p1 : Point %midpoint of the bezier
var p2 : Point %endpoint of the bezier
p0.x := 200 %some random initial values.
p0.y := 200
p1.x := 75
p1.y := 75
p2.x := 150
p2.y := 150
var offsetX := 0 %offset for drawing. Nonzero values kinda mess with the controls
var offsetY := 0
var keys : array char of boolean
View.Set ("offscreenonly")
%this draws the bezier given coordinates. t = time
proc drawBezier (x0 : real, y0 : real, x1 : real, y1 : real, x2 : real, y2: real, t : real)
var x : real
var y: real
x := (1t )** 2*x0 + 2* (1t )*t*x1+t** 2*x2
y := (1t )** 2*y0 + 2* (1t )*t*y1+t** 2*y2
Draw.Dot (round (x )+offsetX, round (y )+offsetY, green)
end drawBezier
%var angle : real
var mx, my, mb : int
loop
Mouse.Where (mx, my, mb )
if mb = 1 then
p2.y := my
p2.x := mx
p1.x := (p2.x + p0.x ) / 4 %these are the two lines I'm having trouble with. I need length((0, 0), p1) to match length (p1, p2). When slope (p2) = slope (p0), the bezier should be linear.
p1.y := (p2.y + p0.y ) / 4
end if
Draw.Line (0+offsetX, 0+offsetY, round (p0.x )+offsetX, round (p0.y )+offsetY, black)
Draw.Line (0+offsetX, 0+offsetY, round (p1.x )+offsetX, round (p1.y )+offsetY, red)
Draw.Line (round (p1.x )+offsetX, round (p1.y )+offsetY, round (p2.x )+offsetX, round (p2.y )+offsetY, red)
for i : 0.. 60
drawBezier (0, 0, p1.x, p1.y, p2.x, p2.y, 1. 0/ 60*i )
end for
put Math.Distance (0, 0, p1.x, p1.y )
put Math.Distance (p1.x, p1.y, p2.x, p2.y )
%Draw.Line (round (p3.x), round (p3.y), round (p4.x), round (p4.y), red)
%Draw.Line (0, 0, round (p2.x), round (p2.y), black)
View.Update
delay (10)
cls
end loop 






Raknarg

Posted: Fri Apr 04, 2014 8:12 pm Post subject: RE:Modelling a specific sort of curve 


Just out of curiosity, why are you using Turing for this project? 





Insectoid

Posted: Fri Apr 04, 2014 8:39 pm Post subject: RE:Modelling a specific sort of curve 


It lets you write small graphical prototypes really quickly (and I can't get Allegro to function properly). Eventually, when I get a functional prototype, I might rewrite it in something more iOS/Android friendly and really polish it up, but I'm not planning that far ahead. 





Raknarg

Posted: Fri Apr 04, 2014 8:45 pm Post subject: RE:Modelling a specific sort of curve 


I used to think so too, but once I got used to Processing, it really changed my mind. Simplicity but with the power of Java.
Not saying you should change what you've already started, though. Just for future reference 





Dreadnought

Posted: Fri Apr 04, 2014 11:07 pm Post subject: Re: Modelling a specific sort of curve 


Insectoid wrote:
I can't figure out a way to make the 2 lines that define the bezier the same length.
Here's how I thought of doing it. If you force the angles between some line to be the same, you can get lines of the same length.
Let L0, L1, L2 be the lines from (0,0) to p0, p1, p2 respectively.
Force the angle between L0 and L1 to be equal to the angle from L1 to L2. This gives an isosceles triangle with vertices p0, p1 and p2 and lets us solve for the length with the cosine law.
Here's what I got (just modified the way you compute p1).
Turing:  type Point :
record
x : real
y : real
end record
function slope (p : Point ) : real %calculates the slope of the line from (0, 0) to p
result arcsind (p.y / sqrt (p.x ** 2 + p.y ** 2))
end slope
% Distance from origin to p squared
fcn norm2 (p : Point ) : real
result p.x * p.x + p.y * p.y
end norm2
proc drawBezier (x0 : real, y0 : real, x1 : real, y1 : real, x2 : real, y2 : real, t : real)
var x : real
var y : real
x := (1  t ) ** 2 * x0 + 2 * (1  t ) * t * x1 + t ** 2 * x2
y := (1  t ) ** 2 * y0 + 2 * (1  t ) * t * y1 + t ** 2 * y2
Draw.Dot (round (x ), round (y ), green)
end drawBezier
var p0 : Point %Not really p0. Just used as a guide to show the 'original' direction
var p1 : Point %midpoint of the bezier
var p2 : Point %endpoint of the bezier
p0.x := 200 %some random initial values.
p0.y := 200
const a0 : real := slope (p0 )
p1.x := 75
p1.y := 75
p2.x := 150
p2.y := 150
var mx, my, mb : int
loop
Mouse.Where (mx, my, mb )
if mb = 1 then
p2.y := my
p2.x := mx
% Please excuse the terrible naming, a0, a1, a2 correspond to slope() for p0, p1, p2
var a2 : real := slope (p2 )
var a : real := (a0  a2 ) / 2 % angle between lines from origin to p0 and p1 (equal to angle between lines from origin to p1 and p2)
var len : real := sqrt (norm2 (p2 ) / (2 * (1  cosd (180  2 * a ))))
var a1 : real := a2 + a
p1.x := len * cosd (a1 )
p1.y := len * sind (a1 )
end if
Draw.Line (0, 0, round (p0.x ), round (p0.y ), black)
Draw.Line (0, 0 , round (p1.x ), round (p1.y ), red)
Draw.Line (round (p1.x ), round (p1.y ), round (p2.x ), round (p2.y ), red)
for i : 0 .. 60
drawBezier (0, 0, p1.x, p1.y, p2.x, p2.y, 1. 0 / 60 * i )
end for
View.Update
delay (10)
cls
end loop







Sponsor Sponsor



Insectoid

Posted: Sat Apr 05, 2014 1:48 pm Post subject: RE:Modelling a specific sort of curve 


That works pretty well, thanks. I've added a little more functionality to it, and came up with this. Same deal, click & drag the blue dot to move it. Press enter to advance the bezier, sort of.
There is an issue when p0 is pointing righttoleft; the slope gets reflected around the y axis (you'll see what I mean). Pretty sure this has something to do with the CAST rule of trig, but I can't figure it out right now. It's a long time since I did any trig.
Also, if p2 = origin (the opposite end of the bezier), the program will crash due to division by zero in the slope function. This is normal and will be fixed by limiting the values of p2 later on.
Turing:  type Point :
record
x : real
y : real
end record
function slope (p : Point ) : real %calculates the slope of the line from (0, 0) to p
var out : real := arcsind (p.y / sqrt (p.x ** 2 + p.y ** 2))
result out
end slope
function subtractPoint (a : Point, b : Point ) : Point
var out : Point
out.x := a.x  b.x
out.y := a.y  b.y
result out
end subtractPoint
% Distance from origin to p squared
fcn norm2 (p : Point ) : real
result p.x * p.x + p.y * p.y
end norm2
proc drawBezier (x0 : real, y0 : real, x1 : real, y1 : real, x2 : real, y2 : real, t : real)
var x : real
var y : real
x := (1  t ) ** 2 * x0 + 2 * (1  t ) * t * x1 + t ** 2 * x2
y := (1  t ) ** 2 * y0 + 2 * (1  t ) * t * y1 + t ** 2 * y2
Draw.Dot (round (x ), round (y ), green)
end drawBezier
var p0 : Point %Not really p0. Just used as a guide to show the 'original' direction
var p1 : Point %midpoint of the bezier
var p2 : Point %endpoint of the bezier
p0.x := 200 %some random initial values.
p0.y := 200
var a0 : real := slope (p0 )
p1.x := 75
p1.y := 75
p2.x := 150
p2.y := 150
var offsetX : real := 0
var offsetY : real := 0
var mx, my, mb : int
var keys : array char of boolean
var keyPressed : boolean := false
View.Set ("offscreenonly")
var s : real
var track : boolean := false
loop
Mouse.Where (mx, my, mb )
Input.KeyDown (keys )
mx  = round (offsetX )
my  = round (offsetY )
if keys (KEY_ENTER ) and keyPressed = false then
keyPressed := true
offsetX + = p2.x
offsetY + = p2.y
p0.x := p2.x  p1.x
p0.y := p2.y  p1.y
p2.x := p0.x
p2.y := p0.y
p1.x := p2.x/ 2
p1.y := p2.y / 2
a0 := slope (p0 )
% p1.x := 75 * cosd (a1)
% p1.y := 75 * sind (a1)
elsif not keys (KEY_ENTER ) then
keyPressed := false
end if
if mb = 1 and mx < p2.x + 5 and mx > p2.x  5 and my < p2.y + 5 and my > p2.y  5 then
track := true
elsif mb = 0 then
track := false
end if
if track = true then
p2.y := my
p2.x := mx
% Please excuse the terrible naming, a0, a1, a2 correspond to slope() for p0, p1, p2
var a2 : real := slope (p2 )
var a : real := (a0  a2 ) / 2 % angle between lines from origin to p0 and p1 (equal to angle between lines from origin to p1 and p2)
var len : real := sqrt (norm2 (p2 ) / (2 * (1  cosd (180  2 * a ))))
var a1 : real := a2 + a
p1.x := len * cosd (a1 )
p1.y := len * sind (a1 )
end if
%Draw.Line (round (offsetX), round (offsetY), round (p0.x+offsetX), round (p0.y+offsetY), black)
Draw.Line (round (offsetX ), round (offsetY ), round (p1.x+offsetX ), round (p1.y+offsetY ), red)
Draw.Line (round (p1.x+offsetX ), round (p1.y+offsetY ), round (p2.x+offsetX ), round (p2.y+offsetY ), red)
Draw.FillOval (round (p2.x + offsetX ), round (p2.y + offsetY ), 5, 5, blue)
for i : 0 .. 60
drawBezier (offsetX, offsetY, p1.x+offsetX, p1.y+offsetY, p2.x+offsetX, p2.y+offsetY, 1. 0 / 60 * i )
end for
View.Update
delay (10)
cls
end loop 






Dreadnought

Posted: Sun Apr 06, 2014 12:44 am Post subject: Re: Modelling a specific sort of curve 


Insectoid wrote:
There is an issue when p0 is pointing righttoleft; the slope gets reflected around the y axis (you'll see what I mean). Pretty sure this has something to do with the CAST rule of trig, but I can't figure it out right now. It's a long time since I did any trig.
You could try changing the computation of a2 to something like this
Turing:  var a2 : real := slope (p2)
put dot (p2, p0)
if (p2.x < 0) then
% The angle is greater than 90 deg (or less than 90 deg)
% We need to adjust the value based on whether p2 is above or below the line from (offsetX, offsetY) to p0
if (p2.x * p0.y / p0.x < p2.y) then
a2 := 180  a2
else
a2 := 180  a2
end if
end if 
Just causes a problem if a2 = a0 (division by zero when computing len) 






