Thread Rating:
  • 1 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
PolyLine library
#17
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 Smile

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'.


Attached Files
.n7   polyline.n7 (Size: 7.5 KB / Downloads: 3)
.n7   closed.n7 (Size: 960 bytes / Downloads: 3)
Reply


Messages In This Thread
PolyLine library - by Marcus - 02-29-2024, 04:25 PM
RE: PolyLine library - by aliensoldier - 02-29-2024, 04:54 PM
RE: PolyLine library - by kevin - 03-01-2024, 11:32 AM
RE: PolyLine library - by Marcus - 03-01-2024, 02:40 PM
RE: PolyLine library - by kevin - 03-01-2024, 03:02 PM
RE: PolyLine library - by Marcus - 03-01-2024, 06:32 PM
RE: PolyLine library - by kevin - 03-01-2024, 08:05 PM
RE: PolyLine library - by Marcus - 03-02-2024, 01:23 PM
RE: PolyLine library - by johnno56 - 03-01-2024, 09:36 PM
RE: PolyLine library - by Marcus - 03-01-2024, 10:03 PM
RE: PolyLine library - by kevin - 03-02-2024, 04:48 PM
RE: PolyLine library - by Marcus - 03-02-2024, 06:20 PM
RE: PolyLine library - by 1micha.elok - 03-05-2024, 06:53 AM
RE: PolyLine library - by kevin - 03-05-2024, 08:42 AM
RE: PolyLine library - by 1micha.elok - 03-05-2024, 11:15 AM
RE: PolyLine library - by Marcus - 03-05-2024, 08:52 PM
RE: PolyLine library - by Marcus - 03-08-2024, 08:21 PM
RE: PolyLine library - by 1micha.elok - 03-09-2024, 02:53 AM
RE: PolyLine library - by Marcus - 03-09-2024, 07:43 AM
RE: PolyLine library - by Marcus - 03-10-2024, 10:10 AM
RE: PolyLine library - by 1micha.elok - 03-11-2024, 12:52 AM

Forum Jump:


Users browsing this thread: 2 Guest(s)