Hi all,
This is kind of a complex subject (mathematically), so I would prefer a more conceptual answer.
Basically, I currently have bezier path code working in AGK. It works perfectly and does exactly what I tell it; but that's the issue, I can't know what to tell it.
Ok, so I want to be able to place down "control points" in realtime, and draw a smooth flowing curve between each one. This can be achieved by hand by manipulating the tangents of the curves, but I need some way to make this automatic. Here, pretty much exactly
like this.
What kind of code would I use to work out where the tangent points need to be to give a smooth flow like that?
I know I haven't explained myself very well, but my brain's tired and I hope someone can work out what I'm trying to say and help me
Thanks
----------------------------------------------------------------------------------------
Update [13/03/14]: I solved the issue myself
Here's the code: DON'T USE THIS CODE NOW. USE THE UPDATED VERSION AT THE BOTTOM OF THIS POST.
function calculatepointoncurve(t#) //t# is the percentage of the way along the line in a range of 0-1
//[x,y]=(1-t)3P0+3(1-t)2tP1+3(1-t)t2P2+t3P3
if curvepointcount>1
totalt#=t#*(curvepointcount-1)
i=trunc(totalt#)
realt#=totalt#-i
p1posx#=pointonlinex(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i+1].posx,curvepoint[i+1].posy,0.3333)
p1posy#=pointonliney(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i+1].posx,curvepoint[i+1].posy,0.3333)
p2posx#=pointonlinex(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i].posx,curvepoint[i].posy,0.3333)
p2posy#=pointonliney(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i].posx,curvepoint[i].posy,0.3333)
if i=0
p0posx#=curvepoint[i].posx
p0posy#=curvepoint[i].posy
else
tempx#=pointonlinex(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i-1].posx,curvepoint[i-1].posy,0.3333)
tempy#=pointonliney(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i-1].posx,curvepoint[i-1].posy,0.3333)
p0posx#=pointonlinex(tempx#,tempy#,p1posx#,p1posy#,0.5)
p0posy#=pointonliney(tempx#,tempy#,p1posx#,p1posy#,0.5)
endif
if i=curvepointcount-2
p3posx#=curvepoint[i+1].posx
p3posy#=curvepoint[i+1].posy
else
tempx#=pointonlinex(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i+2].posx,curvepoint[i+2].posy,0.3333)
tempy#=pointonliney(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i+2].posx,curvepoint[i+2].posy,0.3333)
p3posx#=pointonlinex(p2posx#,p2posy#,tempx#,tempy#,0.5)
p3posy#=pointonliney(p2posx#,p2posy#,tempx#,tempy#,0.5)
endif
//begin calculations (conveniently adapted from http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/):
u#=1-realt#
tt#=realt#*realt#
uu#=u#*u#
uuu#=uu#*u#
ttt#=tt#*realt#
lastcurvepointx#=uuu#*p0posx# //first term
lastcurvepointy#=uuu#*p0posy# //first term
lastcurvepointx#=lastcurvepointx#+3*uu#*realt#*p1posx# //second term
lastcurvepointy#=lastcurvepointy#+3*uu#*realt#*p1posy# //second term
lastcurvepointx#=lastcurvepointx#+3*u#*tt#*p2posx# //third term
lastcurvepointy#=lastcurvepointy#+3*u#*tt#*p2posy# //third term
lastcurvepointx#=lastcurvepointx#+ttt#*p3posx# //fourth term
lastcurvepointy#=lastcurvepointy#+ttt#*p3posy# //fourth term
else
if curvepointcount>0
lastcurvepointx#=curvepoint[0].posx
lastcurvepointy#=curvepoint[0].posy
else
message("Tried to calculate a curve with no points! You dooshbag!")
end
endif
endif
endfunction
curvepoint is defined as such:
type curvepointinfo
posx as integer
posy as integer
endtype
dim curvepoint[0] as curvepointinfo
More info on code usage in
this post.
----------------------------------------------------------------------------------------
Update [16/03/14]:
AGAIN, DON'T USE THIS CODE ANY MORE - THERE'S A BETTER VERSION HERE More changes to the code. Well, not so much changes as I didn't realise I hadn't included some of the functions used by my bezier code....so here is the latest version. Also included now is a function that finds to closest point on the curve to a specific point elsewhere. You should define 4 globals:
global distanceiterations=50
global closestdistance#=0.0
global closestpointx#=0.0
global closestpointy#=0.0
The distance-to-curve function uses a pretty basic brute-force method, so it could probably be faster, but you can change
distanceiterations to adjust how accurate (and how slow) the function is at finding the closest point.
function calculatepointoncurve(t#) //t# is the percentage of the way along the line in a range of 0-1
if curvepointcount>1
//print("start timing")
//pasttime#=timer()
totalt#=t#*(curvepointcount-1)
i=trunc(totalt#)
realt#=totalt#-i
p1posx#=pointonlinex(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i+1].posx,curvepoint[i+1].posy,0.3333)
p1posy#=pointonliney(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i+1].posx,curvepoint[i+1].posy,0.3333)
p2posx#=pointonlinex(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i].posx,curvepoint[i].posy,0.3333)
p2posy#=pointonliney(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i].posx,curvepoint[i].posy,0.3333)
//time#=timer()
//print("time 0-1: "+str((time#-pasttime#)*1000))
//pasttime#=time#
if i=0
p0posx#=curvepoint[i].posx
p0posy#=curvepoint[i].posy
else
tempx#=pointonlinex(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i-1].posx,curvepoint[i-1].posy,0.3333)
tempy#=pointonliney(curvepoint[i].posx,curvepoint[i].posy,curvepoint[i-1].posx,curvepoint[i-1].posy,0.3333)
p0posx#=pointonlinex(tempx#,tempy#,p1posx#,p1posy#,0.5)
p0posy#=pointonliney(tempx#,tempy#,p1posx#,p1posy#,0.5)
endif
//time#=timer()
//print("time 1-2: "+str((time#-pasttime#)*1000))
//pasttime#=time#
if i=curvepointcount-2
p3posx#=curvepoint[i+1].posx
p3posy#=curvepoint[i+1].posy
else
tempx#=pointonlinex(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i+2].posx,curvepoint[i+2].posy,0.3333)
tempy#=pointonliney(curvepoint[i+1].posx,curvepoint[i+1].posy,curvepoint[i+2].posx,curvepoint[i+2].posy,0.3333)
p3posx#=pointonlinex(p2posx#,p2posy#,tempx#,tempy#,0.5)
p3posy#=pointonliney(p2posx#,p2posy#,tempx#,tempy#,0.5)
endif
//time#=timer()
//print("time 2-3: "+str((time#-pasttime#)*1000))
//pasttime#=time#
//begin calculations (conveniently adapted from http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/):
u#=1-realt#
tt#=realt#*realt#
uu#=u#*u#
uuu#=uu#*u#
ttt#=tt#*realt#
lastcurvepointx#=uuu#*p0posx# //first term
lastcurvepointy#=uuu#*p0posy# //first term
lastcurvepointx#=lastcurvepointx#+3*uu#*realt#*p1posx# //second term
lastcurvepointy#=lastcurvepointy#+3*uu#*realt#*p1posy# //second term
lastcurvepointx#=lastcurvepointx#+3*u#*tt#*p2posx# //third term
lastcurvepointy#=lastcurvepointy#+3*u#*tt#*p2posy# //third term
lastcurvepointx#=lastcurvepointx#+ttt#*p3posx# //fourth term
lastcurvepointy#=lastcurvepointy#+ttt#*p3posy# //fourth term
//time#=timer()
//print("time 3-4: "+str((time#-pasttime#)*1000))
//pasttime#=time#
//print("end timing")
//sync()
//waitkey()
else
if curvepointcount>0
lastcurvepointx#=curvepoint[0].posx
lastcurvepointy#=curvepoint[0].posy
else
message("Tried to calculate a curve with no points! You douchebag!")
end
endif
endif
endfunction
function calculateclosestpointtocurve(x#,y#) //x# and y# are the point from which we're searching
closestdistance#=1000000 //hack, but basically could never be larger than this anyway
if curvepointcount>0
closestpointx#=0
closestpointy#=0
add#=1.0/distanceiterations
t#=0
for d=0 to distanceiterations-1
calculatepointoncurve(t#)
//distance#=ufp_distance2(x#,y#,lastcurvepointx#,lastcurvepointy#)
distance#=sqrt((x#-lastcurvepointx#)*(x#-lastcurvepointx#)+(y#-lastcurvepointy#)*(y#-lastcurvepointy#))
if distance#<closestdistance#
closestdistance#=distance#
closestpointx#=lastcurvepointx#
closestpointy#=lastcurvepointy#
endif
t#=t#+add#
next d
endif
endfunction
function pointonlinex(x1#,y1#,x2#,y2#,percent#)
px#=x1#+((x2#-x1#)*percent#)
endfunction px#
function pointonliney(x1#,y1#,x2#,y2#,percent#)
py#=y1#+((y2#-y1#)*percent#)
endfunction py#
This post still applies for setting up and using the code.