Problem
How to make closed loop and curved polyline ?
It makes sharp turn at X.
Code:
'===============================================
' TEST DRIVE
'
'Problem :
'- How to make closed loop and curved polyline ?
' It makes sharp turn at X.
'===============================================
'------------------
' INITIALIZATION
'------------------
#win32
include "polyline.n7"
set window "Test Drive", 160, 120,false,5
set redraw off
'color definition
white = [255,255,255]
gray = [128,128,128,50]
black = [0,0,0]
black_alpha = [0,0,0,24]
red = [168,0,0]
green = [0,255,0]
'-------------
' MAIN LOOP
'-------------
while not keydown(KEY_ESCAPE, true)
' Title
set caret width(primary)/2, 6; center "Test Drive"
set caret width(primary)/2, height(primary)-20; center "Sharp turn at X"
' Racing Track
set color gray ; draw ellipse 50-1,60, 32,32,1;draw ellipse 110+2,60, 32,32,1
set color black ; draw ellipse 50-1,60, 21,21,1;draw ellipse 110+2,60, 21,21,1
set color white ; set caret 20-8,60-6; wln "x"
' Draw circle that moves along path.
set color black_alpha; cls 'clear background screen
set color green; draw ellipse pos[0], pos[1], radius, radius, 1
Hi, yes it's treating each lap as a separate path, which I guess is how it's designed to work. In the absence of a change to the code (which I can't think of), the impact can be lessened if we join the path in one of the straighter parts, rather than a curve. For example:
(03-05-2024, 08:42 AM)kevin Wrote: Hi, yes it's treating each lap as a separate path, which I guess is how it's designed to work. In the absence of a change to the code (which I can't think of), the impact can be lessened if we join the path in one of the straighter parts, rather than a curve. ...
Your solution solved my problem, thank you, Kevin
You are genius.
(03-05-2024, 08:42 AM)kevin Wrote: Hi, yes it's treating each lap as a separate path, which I guess is how it's designed to work. In the absence of a change to the code (which I can't think of), the impact can be lessened if we join the path in one of the straighter parts, rather than a curve. ...
Your solution solved my problem, thank you, Kevin
You are genius.
Glad you could work around it! But I'll modify the library to support closed paths when I get the time.
03-08-2024, 08:21 PM (This post was last modified: 03-08-2024, 08:24 PM by Marcus.)
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
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"
' 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
' 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'.
03-09-2024, 02:53 AM (This post was last modified: 03-09-2024, 03:14 AM by 1micha.elok.)
(03-08-2024, 08:21 PM)Marcus Wrote: I've made some changes to the polyline library so that you can create closed paths.
... writing code while watching a film on tv
... use 'atan2(dir[1], dir[0])' to get the angle ...
Last night I watched Gran Tourismo : A True Story, a video game player who became a professional real race car driver. Who knows that for the rest of my life, I could be a professional real race car driver too if I could make a car racing game ...just kidding...
With the changes in the polyline library especially the ability to get the angle 'atan2(dir[1], dir[0])' , you give me an idea to make car's dashboard for the car race game. The left dashboard for the angle direction, and the right dashboard for the car's speed. click the image to zoom in
Code:
'===============================================
' TEST DRIVE
'
'Required : NEW POLYLINE LIBRARY
'===============================================
'------------------
' INITIALIZATION
'------------------
set console oFF
include "polyline.n7"
set window "Test Drive", 160, 120,false,5
set redraw off
'color definition
white = [255,255,255]
gray = [128,128,128,50]
black = [0,0,0]
black_alpha = [0,0,0,24]
red = [255,0,0]
green = [0,255,0]
' Create a polyline
closed_path = true
path = PolyLine(points,closed_path)
'-------------
' MAIN LOOP
'-------------
while not keydown(KEY_ESCAPE, true)
' Title
set caret width(primary)/2, 6; center "Test Drive"
if closed_path = true then; cp="true";else;cp="false";endif
set caret width(primary)/2, height(primary)-20; center "closed "+cp
' Racing Track
set color gray ; draw ellipse 50-1,60, 32,32,1;draw ellipse 110+2,60, 32,32,1
set color black ; draw ellipse 50-1,60, 21,21,1;draw ellipse 110+2,60, 21,21,1
set color white ; set caret 20-8,60-6; wln "x"
'Car's Dashboard
'Left Dashboard : Angle
angle = atan2(dir[1], dir[0])
set color red; draw line 49,60,18*cos(angle)+49,18*sin(angle)+60
'Right Dashboard : move_pixel
speedx = 18*cos(rad(360-30*move_pixel))+112
speedy = 18*sin(rad(360-30*move_pixel))+60
draw line 112,60, speedx, speedy
if move_pixel < 0 then; move_pixel = 6 ;else;move_pixel = move_pixel - 0.1;endif
' Draw circle that moves along path.
set color black_alpha; cls 'clear background screen
set color green; draw ellipse pos[0], pos[1], radius, radius, 1
redraw
fwait 10
wend
Perhaps, this dashboard and this race track could improve this game visually https://naalaa.com/forum/thread-92-post-539.html#pid539 along with improving the road ( to make a playable curved road ) ... a lot of homeworks
By the way, happy weekend and hope you and your family enjoy the gymnastics competition this weekend too
(03-08-2024, 08:21 PM)Marcus Wrote: I've made some changes to the polyline library so that you can create closed paths.
... writing code while watching a film on tv
... use 'atan2(dir[1], dir[0])' to get the angle ...
Last night I watched Gran Tourismo : A True Story, a video game player who became a professional real race car driver. Who knows that for the rest of my life, I could be a professional real race car driver too if I could make a car racing game ...just kidding...
With the changes in the polyline library especially the ability to get the angle 'atan2(dir[1], dir[0])' , you give me an idea to make car's dashboard for the car race game. The left dashboard for the angle direction, and the right dashboard for the car's speed.
click the image to zoom in
Code:
'===============================================
' TEST DRIVE
'
'Required : NEW POLYLINE LIBRARY
'===============================================
'------------------
' INITIALIZATION
'------------------
set console oFF
include "polyline.n7"
set window "Test Drive", 160, 120,false,5
set redraw off
'color definition
white = [255,255,255]
gray = [128,128,128,50]
black = [0,0,0]
black_alpha = [0,0,0,24]
red = [255,0,0]
green = [0,255,0]
' Create a polyline
closed_path = true
path = PolyLine(points,closed_path)
'-------------
' MAIN LOOP
'-------------
while not keydown(KEY_ESCAPE, true)
' Title
set caret width(primary)/2, 6; center "Test Drive"
if closed_path = true then; cp="true";else;cp="false";endif
set caret width(primary)/2, height(primary)-20; center "closed "+cp
' Racing Track
set color gray ; draw ellipse 50-1,60, 32,32,1;draw ellipse 110+2,60, 32,32,1
set color black ; draw ellipse 50-1,60, 21,21,1;draw ellipse 110+2,60, 21,21,1
set color white ; set caret 20-8,60-6; wln "x"
'Car's Dashboard
'Left Dashboard : Angle
angle = atan2(dir[1], dir[0])
set color red; draw line 49,60,18*cos(angle)+49,18*sin(angle)+60
'Right Dashboard : move_pixel
speedx = 18*cos(rad(360-30*move_pixel))+112
speedy = 18*sin(rad(360-30*move_pixel))+60
draw line 112,60, speedx, speedy
if move_pixel < 0 then; move_pixel = 6 ;else;move_pixel = move_pixel - 0.1;endif
' Draw circle that moves along path.
set color black_alpha; cls 'clear background screen
set color green; draw ellipse pos[0], pos[1], radius, radius, 1
redraw
fwait 10
wend
Perhaps, this dashboard and this race track could improve this game visually https://naalaa.com/forum/thread-92-post-539.html#pid539 along with improving the road ( to make a playable curved road ) ... a lot of homeworks
By the way, happy weekend and hope you and your family enjoy the gymnastics competition this weekend too
I once wrote a Mario Kart clone (just a test, more of an engine, I guess) in n5 or n6: https://youtu.be/Xr_UhCX9ta0?si=YPMi7_CBVAJVJRKm&t=212 For that game I actually did use something like the polyline library for the computer players' movements. I "recorded" (saved a position into an array every second or so) my self playing the game a couple of times. Then I let the computer played opponents follow these paths. The enemies could still collide with the eachother and the player, but they always tried to get back on their paths. It worked quite well
Here's a zip with the new version (posted above) of the library. The third game example now uses images and rotates the enemy sprites after the path direction.