Petr Schreiber
17-03-2019, 19:50
A friend of mine approached me with a question, how would I model mesh of tobogan.
Normal person would answer "extrude profile along curve in Blender", but coding it is more interesting task, right?
Friend needs it done in Python, but thinBASIC has better built in graphics, so I prototype it in TB.
The project started with generation of tobogan profile, the "U" shape.
How to model profile?
The profile could be approached as arc, part of circle. As circle can be generated via SIN/COS, we will take these helpers to build the part of the mesh.
Some step needs to be decided. As thinBASIC does not allow redim of UDT arrays yet, the step is hardcoded to 16, will update it in future thinBASIC versions to something more tasteful:
uses "math"
type Point3D
x as float32
y as float32
z as float32
end type
type ToboganProfile
point(16) as point3D ' -- Will be converted to dynamic UDT array once TB supports it
point_count as int32
function _create(ellipsis_width as float32, ellipsis_height as float32, arc_angle as float32)
me.point_count = ubound(me.point)
float32 angle
float32 min_angle = degToRad(-arc_angle/2) ' Conversion of degrees to radians is offered by math module
float32 max_angle = degToRad( arc_angle/2)
float32 angle_step= (max_angle - min_angle) / (me.point_count-1)
int32 i
for i = 1 to me.point_count
angle = min_angle + (i-1) * angle_step
me.point(i).x = sin(angle) * ellipsis_width/2
me.point(i).y = -cos(angle) * ellipsis_height/2
me.point(i).y += ellipsis_height/2 ' shift it up to 0,0,0
next
end function
end type
Now we can get list of points from the ToboganProfile. In order to simplify rendering, we can add dedicated function to connect the points of the U profile:
function render()
int32 i
tbgl_beginPoly %GL_LINE_STRIP ' This primitive allows to just supply list of vertices and they will get connected
for i = 1 to me.point_count
tbgl_vertex me.point(i).x, me.point(i).y, me.point(i).z
next
tbgl_endPoly
tbgl_pushStateProtect %TBGL_DEPTH ' Ignoring depth allows to draw over the previously rendered line strip
tbgl_pushColor 255, 0, 0 ' PushColor allows to enable drawing color just until PopColor is called
tbgl_pushPointSize 5 ' Again, limited scope for point size change
tbgl_beginPoly %GL_POINTS
for i = 1 to me.point_count
tbgl_vertex me.point(i).x, me.point(i).y, me.point(i).z
next
tbgl_endPoly
tbgl_popPointSize ' Returning the states back to previous state
tbgl_popColor
tbgl_popStateProtect
end function
Grid with 1x1 cells would come in handy, to see the profile size
We can build a Grid2D type to help us draw grid easily:
type grid2d
top_x as float32
top_y as float32
grid_step as float32
function _create(top_x as float32, top_y as float32, optional grid_step as float32 = 1)
me.top_x = top_x
me.top_y = top_y
me.grid_step = grid_step
end function
function render()
tbgl_pushColor 128, 128, 128
tbgl_line(-me.top_x, 0, me.top_x, 0)
tbgl_line(0, -me.top_y, 0, me.top_y)
tbgl_popColor
float32 i
tbgl_pushColor 64, 64, 64
for i = -me.top_x to me.top_x
tbgl_line(i, -me.top_y, i, me.top_y)
next
for i = -me.top_y to me.top_y
tbgl_line(-me.top_x, i, me.top_x, i)
next
tbgl_popColor
end function
end type
Once we have these helpers, let's draw it to the screen
uses "tbgl", "console"
#include "ToboganProfile.tbasicu"
#include "Grid2d.tbasicu"
function tbMain()
' -- Create and show window
uint32 hWnd = TBGL_createWindowEx("Tobogan profile - press ESC to quit", 512, 512, 32, %TBGL_WS_WINDOWED or %TBGL_WS_CLOSEBOX)
tbgl_showWindow
dim grid as grid2d(10, 10) ' Create new grid
dim profileA as toboganProfile(2, 1, 180) ' Create wide profile, 180°
dim profileB as toboganProfile(1, 1, 270) ' Create profile with 270° U shape
float32 frameRate
' -- Print the calculated points
uint32 i
printl "Profile A" in 15
for i = 1 to profileA.point_count
printl format$(profileA.point(i).x, "#.000"), format$(profileA.point(i).y, "#.000")
next
printl
printl "Profile B" in 15
for i = 1 to profileB.point_count
printl format$(profileB.point(i).x, "#.000"), format$(profileB.point(i).y, "#.000")
next
' -- Main loop
while tbgl_isWindow(hWnd)
FrameRate = tbgl_getFrameRate
tbgl_clearFrame
' Set the camera position
tbgl_camera 0, 0, 5, 0, 0, 0
' Render all, in order dependent fashion
tbgl_pushStateProtect %TBGL_DEPTH
grid.render()
profileA.render()
profileB.render()
tbgl_popStateProtect
tbgl_drawFrame
' -- ESCAPE key to exit application
if tbgl_getWindowKeyState(hWnd, %VK_ESCAPE) then exit while
wend
tbgl_destroyWindow
end function
The result of the drawing can be seen in the attachment. You can also download the complete source code for this phase.
Petr
Normal person would answer "extrude profile along curve in Blender", but coding it is more interesting task, right?
Friend needs it done in Python, but thinBASIC has better built in graphics, so I prototype it in TB.
The project started with generation of tobogan profile, the "U" shape.
How to model profile?
The profile could be approached as arc, part of circle. As circle can be generated via SIN/COS, we will take these helpers to build the part of the mesh.
Some step needs to be decided. As thinBASIC does not allow redim of UDT arrays yet, the step is hardcoded to 16, will update it in future thinBASIC versions to something more tasteful:
uses "math"
type Point3D
x as float32
y as float32
z as float32
end type
type ToboganProfile
point(16) as point3D ' -- Will be converted to dynamic UDT array once TB supports it
point_count as int32
function _create(ellipsis_width as float32, ellipsis_height as float32, arc_angle as float32)
me.point_count = ubound(me.point)
float32 angle
float32 min_angle = degToRad(-arc_angle/2) ' Conversion of degrees to radians is offered by math module
float32 max_angle = degToRad( arc_angle/2)
float32 angle_step= (max_angle - min_angle) / (me.point_count-1)
int32 i
for i = 1 to me.point_count
angle = min_angle + (i-1) * angle_step
me.point(i).x = sin(angle) * ellipsis_width/2
me.point(i).y = -cos(angle) * ellipsis_height/2
me.point(i).y += ellipsis_height/2 ' shift it up to 0,0,0
next
end function
end type
Now we can get list of points from the ToboganProfile. In order to simplify rendering, we can add dedicated function to connect the points of the U profile:
function render()
int32 i
tbgl_beginPoly %GL_LINE_STRIP ' This primitive allows to just supply list of vertices and they will get connected
for i = 1 to me.point_count
tbgl_vertex me.point(i).x, me.point(i).y, me.point(i).z
next
tbgl_endPoly
tbgl_pushStateProtect %TBGL_DEPTH ' Ignoring depth allows to draw over the previously rendered line strip
tbgl_pushColor 255, 0, 0 ' PushColor allows to enable drawing color just until PopColor is called
tbgl_pushPointSize 5 ' Again, limited scope for point size change
tbgl_beginPoly %GL_POINTS
for i = 1 to me.point_count
tbgl_vertex me.point(i).x, me.point(i).y, me.point(i).z
next
tbgl_endPoly
tbgl_popPointSize ' Returning the states back to previous state
tbgl_popColor
tbgl_popStateProtect
end function
Grid with 1x1 cells would come in handy, to see the profile size
We can build a Grid2D type to help us draw grid easily:
type grid2d
top_x as float32
top_y as float32
grid_step as float32
function _create(top_x as float32, top_y as float32, optional grid_step as float32 = 1)
me.top_x = top_x
me.top_y = top_y
me.grid_step = grid_step
end function
function render()
tbgl_pushColor 128, 128, 128
tbgl_line(-me.top_x, 0, me.top_x, 0)
tbgl_line(0, -me.top_y, 0, me.top_y)
tbgl_popColor
float32 i
tbgl_pushColor 64, 64, 64
for i = -me.top_x to me.top_x
tbgl_line(i, -me.top_y, i, me.top_y)
next
for i = -me.top_y to me.top_y
tbgl_line(-me.top_x, i, me.top_x, i)
next
tbgl_popColor
end function
end type
Once we have these helpers, let's draw it to the screen
uses "tbgl", "console"
#include "ToboganProfile.tbasicu"
#include "Grid2d.tbasicu"
function tbMain()
' -- Create and show window
uint32 hWnd = TBGL_createWindowEx("Tobogan profile - press ESC to quit", 512, 512, 32, %TBGL_WS_WINDOWED or %TBGL_WS_CLOSEBOX)
tbgl_showWindow
dim grid as grid2d(10, 10) ' Create new grid
dim profileA as toboganProfile(2, 1, 180) ' Create wide profile, 180°
dim profileB as toboganProfile(1, 1, 270) ' Create profile with 270° U shape
float32 frameRate
' -- Print the calculated points
uint32 i
printl "Profile A" in 15
for i = 1 to profileA.point_count
printl format$(profileA.point(i).x, "#.000"), format$(profileA.point(i).y, "#.000")
next
printl
printl "Profile B" in 15
for i = 1 to profileB.point_count
printl format$(profileB.point(i).x, "#.000"), format$(profileB.point(i).y, "#.000")
next
' -- Main loop
while tbgl_isWindow(hWnd)
FrameRate = tbgl_getFrameRate
tbgl_clearFrame
' Set the camera position
tbgl_camera 0, 0, 5, 0, 0, 0
' Render all, in order dependent fashion
tbgl_pushStateProtect %TBGL_DEPTH
grid.render()
profileA.render()
profileB.render()
tbgl_popStateProtect
tbgl_drawFrame
' -- ESCAPE key to exit application
if tbgl_getWindowKeyState(hWnd, %VK_ESCAPE) then exit while
wend
tbgl_destroyWindow
end function
The result of the drawing can be seen in the attachment. You can also download the complete source code for this phase.
Petr