I've made some changes to the polyline library so that you can create closed paths. You now create a path with 'PolyLine(points, closed)'. Set 'closed' to true or false. You can also get the direction (normalized derivative) at any distance on the curve with 'GetDirection'.
I hope it works, I haven't tested it much - writing code while watching a film on tv
closed.n7
polyline.n7
Edit If you want to draw an image with rotation based on the curve direction, you should be able to use 'atan2(dir[1], dir[0])' to get the angle if 'dir' was returned by 'GetDirection'.
I hope it works, I haven't tested it much - writing code while watching a film on tv
closed.n7
Code:
include "polyline.n7"
set window "test", 640, 480
set redraw off
points = [[50, 200], [200, 100], [500, 200], [320, 400], [64, 300]]
path = PolyLine(points, true)
d = 0
while not keydown(KEY_ESCAPE, true)
d = d + 1
set color 0, 0, 0
cls
' draw control points and lines.
set color 128, 128, 128
for i = 0 to sizeof(points) - 1
j = (i + 1)%sizeof(points)
draw ellipse points[i][0], points[i][1], 4, 4, true
draw line points[i][0], points[i][1], points[j][0], points[j][1]
next
set color 255, 255, 255
' when the curve is looped, d is wrapped and a valid pos is always returned.
pos = path.GetPoint(d, true)
' you can use GetDirection to get the normalized derivative of the curve at distance d.
dir = path.GetDirection(d, true)
draw line pos[0] - dir[0]*16, pos[1] - dir[1]*16, pos[0], pos[1]
redraw
fwait 60
wend
polyline.n7
Code:
' polyline.n7
' -----------
' By Marcus.
' PolyLine
' --------
' Create a polyline from 'points', which is assumed to be a 2d array of the form [[x0, y0], [x1,
' y1], .. [xn, yn]].
function PolyLine(points, closed)
assert sizeof(points) > 1, "PolyLine: too few points"
pp = []
pp.closed = closed
pp.segs = []
pp.length = 0
pp.eval = dim(2)
pp.deval = dim(2)
' Convert points to segments with some more data.
if closed
for i = 0 to sizeof(points) - 1
dx = points[(i + 1)%sizeof(points)][0] - points[i][0]
dy = points[(i + 1)%sizeof(points)][1] - points[i][1]
d = sqr(dx*dx + dy*dy)
pp.segs[i] = [
x0: points[i][0], y0: points[i][1],
x3: points[(i + 1)%sizeof(points)][0], y3: points[(i + 1)%sizeof(points)][1],
dx: dx/d, dy: dy/d, d: d]
pp.length = pp.length + d
next
else
for i = 0 to sizeof(points) - 2
dx = points[i + 1][0] - points[i][0]
dy = points[i + 1][1] - points[i][1]
d = sqr(dx*dx + dy*dy)
pp.segs[i] = [
x0: points[i][0], y0: points[i][1],
x3: points[i + 1][0], y3: points[i + 1][1],
dx: dx/d, dy: dy/d, d: d]
pp.length = pp.length + d
next
endif
' Build bezier curve data.
for i = 0 to sizeof(pp.segs) - 1 BuildBezierSegment(pp.segs, i, closed)
' GetPoint
' --------
' Return the point, [x, y], where the traveled distance along the polyline is 'dist'. If the
' path is not closed and 'dist' is not within the length of the polyline 'unset' is returned.
' Set 'smooth' to 'true' to get the point from a composite bezier curve that passes through all
' the original points.
pp.GetPoint = function(dist, smooth)
if this.closed dist = dist%this.length
elseif dist < 0 return unset
d = 0
for i = 0 to sizeof(this.segs) - 1
if dist < d + this.segs[i].d
if smooth
pt = this.segs[i]
p = (dist - d)/this.segs[i].d
b0 = (1 - p)^3
b1 = 3*(1 - p)^2*p
b2 = 3*(1 - p)*p^2
b3 = p^3
this.eval[0] = pt.x0*b0 + pt.x1*b1 + pt.x2*b2 + pt.x3*b3
this.eval[1] = pt.y0*b0 + pt.y1*b1 + pt.y2*b2 + pt.y3*b3
else
this.eval[0] = this.segs[i].x0 + this.segs[i].dx*(dist - d)
this.eval[1] = this.segs[i].y0 + this.segs[i].dy*(dist - d)
endif
return this.eval
else d = d + this.segs[i].d
next
return unset
endfunc
' GetDirection
' ------------
' Return the normalized derivative, [dx, dy], where the traveled distance along the polyline is
' 'dist'. If the path is not closed and 'dist' is not within the length of the polyline 'unset'
' is returned. Set 'smooth' to 'true' to get the derivative from a composite bezier curve that
' passes through all the original points.
pp.GetDirection = function(dist, smooth)
if this.closed dist = dist%this.length
elseif dist < 0 return unset
d = 0
for i = 0 to sizeof(this.segs) - 1
if dist < d + this.segs[i].d
pt = this.segs[i]
if smooth
p = (dist - d)/this.segs[i].d
b0 = 3*(1 - p)^2
b1 = 6*(1 - p)*p
b2 = 3*p*p
this.deval[0] = b0*(pt.x1 - pt.x0) + b1*(pt.x2 - pt.x1) + b2*(pt.x3 - pt.x2)
this.deval[1] = b0*(pt.y1 - pt.y0) + b1*(pt.y2 - pt.y1) + b2*(pt.y3 - pt.y2)
else
this.deval[0] = pt.dx
this.deval[1] = pt.dy
endif
k = 1/sqr(this.deval[0]*this.deval[0] + this.deval[1]*this.deval[1])
this.deval[0] = this.deval[0]*k
this.deval[1] = this.deval[1]*k
return this.deval
else d = d + this.segs[i].d
next
return unset
endfunc
' GetControlPoint
' ---------------
pp.GetControlPoint = function(index)
if index >= 0 and index <= sizeof(this.segs)
if index = sizeof(this.segs) return [this.segs[index - 1].x3, this.segs[index - 1].y3]
else return [this.segs[index].x0, this.segs[index].y0]
else
return unset
endif
endfunc
' ModifyControlPoint
' ------------------
' Change the coordinates of a control point.
pp.ModifyControlPoint = function(index, x, y)
if this.closed
assert index >= 0 and index < sizeof(this.segs), "PolyLine.ModifyControlPoint: index out of range"
seg = this.segs[index]
seg.x0 = x; seg.y0 = y;
seg.dx = seg.x3 - x; seg.dy = seg.y3 - y
seg.d = sqr(seg.dx*seg.dx + seg.dy*seg.dy)
seg = this.segs[(index - 1)%sizeof(this.segs)]
seg.x3 = x; seg.y3 = y
seg.dx = x - seg.x0; seg.dy = y - seg.y0
seg.d = sqr(seg.dx*seg.dx + seg.dy*seg.dy)
else
assert index >= 0 and index <= sizeof(this.segs), "PolyLine.ModifyControlPoint: index out of range"
if index < sizeof(this.segs)
seg = this.segs[index]
seg.x0 = x; seg.y0 = y
seg.dx = seg.x3 - x; seg.dy = seg.y3 - y
seg.d = sqr(seg.dx*seg.dx + seg.dy*seg.dy)
endif
if index > 0
seg = this.segs[index - 1]
seg.x3 = x; seg.y3 = y
seg.dx = x - seg.x0; seg.dy = y - seg.y0
seg.d = sqr(seg.dx*seg.dx + seg.dy*seg.dy)
endif
endif
for i = index - 2 to index + 2 BuildBezierSegment(this.segs, i, this.closed)
this.length = 0
for i = 0 to sizeof(this.segs) - 1 this.length = this.length + this.segs[i].d
endfunc
' GetLength
' ---------
pp.GetLength = function()
return this.length
endfunc
return pp
' BuildBezierSegment
' ------------------
function BuildBezierSegment(segs, i, closed)
if closed i = i%sizeof(segs)
elseif i < 0 or i >= sizeof(segs) return
dx = 1*(segs[i].x3 - segs[i].x0)
dy = 1*(segs[i].y3 - segs[i].y0)
if closed
dx = dx + segs[i].x0 - segs[(i - 1)%sizeof(segs)].x0
dy = dy + segs[i].y0 - segs[(i - 1)%sizeof(segs)].y0
elseif i > 0
dx = dx + segs[i].x0 - segs[i - 1].x0
dy = dy + segs[i].y0 - segs[i - 1].y0
endif
k = 0.333*segs[i].d/sqr(dx*dx + dy*dy)
segs[i].x1 = segs[i].x0 + k*dx
segs[i].y1 = segs[i].y0 + k*dy
dx = 1*(segs[i].x3 - segs[i].x0)
dy = 1*(segs[i].y3 - segs[i].y0)
if closed
dx = dx + segs[(i + 1)%sizeof(segs)].x3 - segs[i].x3
dy = dy + segs[(i + 1)%sizeof(segs)].y3 - segs[i].y3
elseif i < sizeof(segs) - 1
dx = dx + segs[i + 1].x3 - segs[i].x3
dy = dy + segs[i + 1].y3 - segs[i].y3
endif
k = 0.333*segs[i].d/sqr(dx*dx + dy*dy)
segs[i].x2 = segs[i].x3 - k*dx
segs[i].y2 = segs[i].y3 - k*dy
endfunc
endfunc
Edit If you want to draw an image with rotation based on the curve direction, you should be able to use 'atan2(dir[1], dir[0])' to get the angle if 'dir' was returned by 'GetDirection'.