The library only needs a pointer to a line function, so it works with any graphics backend.
Have fun and feel free to post your improvements & art

Edit (21.11.2024):
- fixed a small mistake in the code
- added a module version (last code)

Edit (22.11.2024):
- Update 1.01: added file handling and improved some aspects of the library, includes and examples
Images:


Library:
Code: Select all
EnableExplicit
;--------------------------------------------------------------------------------------
;Project: Turtle Library
;File: turtle.pb
;Version: 1.01
;Platform: Linux, Windows, Mac
;Compiler: PureBasic 6.12 LTS (x64) - C Backend
;--------------------------------------------------------------------------------------
; MIT License
; Copyright 2024 © Mijikai
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;--------------------------------------------------------------------------------------
;Component: lzma
;LZMA SDK is written and placed in the public domain by Igor Pavlov.
;Some code in LZMA SDK is based on public domain code from other developers:
;1) PPMd var.H (2001): Dmitry Shkarin
;2) SHA-256: Wei Dai (Crypto++ library)
;--------------------------------------------------------------------------------------
UseLZMAPacker()
Prototype.i turtleLine(X1.d,Y1.d,X2.d,Y2.d,Color.l)
Structure TURTLE_HEADER_STRUCT
magic.l
width.l
height.l
title.a[64]
bytes.q
packed.q
EndStructure
Structure TURTLE_FILE_STRUCT
title.s
width.l
height.l
length.i
commands.s
EndStructure
Structure TURTLE_POSITION_STRUCT
x.d
y.d
EndStructure
Structure TURTLE_STRUCT
*vtable
render.l
color.l
origin.TURTLE_POSITION_STRUCT
start.TURTLE_POSITION_STRUCT
stop.TURTLE_POSITION_STRUCT
direction.d
length.d
*draw.turtleLine
EndStructure
#TURTLE_VERSION = 101
;--------------------------------------------------------------------------------------
ProcedureDLL.i turtleInterface(Version.i)
Protected *turtle.TURTLE_STRUCT
With *turtle
If Version = #Null
Version = #TURTLE_VERSION
EndIf
If Version = #TURTLE_VERSION
*turtle = AllocateStructure(TURTLE_STRUCT)
If *turtle
\vtable = ?vtable
\color = $FFFFFFFF
EndIf
EndIf
ProcedureReturn *turtle
EndWith
EndProcedure
;--------------------------------------------------------------------------------------
Procedure.i turtleLine(*turtle.TURTLE_STRUCT,*Function)
With *turtle
\draw = *Function
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleColor(*turtle.TURTLE_STRUCT,Color.l)
With *turtle
\color = Color
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleOrigin(*turtle.TURTLE_STRUCT,X.l,Y.l,Center.b)
With *turtle
If Center
\origin\x = X / 2.0
\origin\y = Y / 2.0
Else
\origin\x = X
\origin\y = Y
EndIf
\start = \origin
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleStart(*turtle.TURTLE_STRUCT)
With *turtle
\render = #True
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleStop(*turtle.TURTLE_STRUCT)
With *turtle
\render = #False
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleOffset(*turtle.TURTLE_STRUCT,X.d,Y.d)
With *turtle
\start\x = X
\start\y = Y
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleReset(*turtle.TURTLE_STRUCT)
With *turtle
\start = \origin
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleLength(*turtle.TURTLE_STRUCT,Length.d)
With *turtle
\length = Length
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleAngle(*turtle.TURTLE_STRUCT,Angle.d)
With *turtle
\direction = Radian(Angle)
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleLeft(*turtle.TURTLE_STRUCT,Angle.d)
With *turtle
\direction - Radian(Angle)
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleRight(*turtle.TURTLE_STRUCT,Angle.d)
With *turtle
\direction + Radian(Angle)
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleTarget(*turtle.TURTLE_STRUCT,X.d,Y.d)
With *turtle
\direction = ATan2((X - \start\x),(Y - \start\y))
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleMove(*turtle.TURTLE_STRUCT)
With *turtle
\stop\x = \start\x + (Cos(\direction) * \length)
\stop\y = \start\y + (Sin(\direction) * \length)
If \render
\draw(\start\x,\start\y,\stop\x,\stop\y,\color)
EndIf
\start = \stop
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtlePosition(*turtle.TURTLE_STRUCT,*X.Double,*Y.Double)
With *turtle
*X\d = \start\x
*Y\d = \start\y
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleRun(*turtle.TURTLE_STRUCT,*Code.Ascii,*X.Double,*Y.Double)
Protected.d x,y
Protected.i offset,a,b,c,d
Protected.Ascii *pos
Protected.s parameter
With *turtle
While *Code\a
offset = 0
*pos = *Code + 2
If *pos\a = '('
Repeat
*pos + 2
If *pos\a = ','
offset + 1
EndIf
Until *pos\a = ')'
Else
*pos = *Code
EndIf
Select *Code\a
Case '$';Color
If offset = 3
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
a = Val(StringField(parameter,1,","))
b = Val(StringField(parameter,2,","))
c = Val(StringField(parameter,3,","))
d = Val(StringField(parameter,4,","))
turtleColor(*turtle,RGBA(a,b,c,d))
EndIf
Case '*';Origin
If offset = 2
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
x = ValD(StringField(parameter,1,","))
y = ValD(StringField(parameter,2,","))
a = Val(StringField(parameter,3,","))
turtleOrigin(*turtle,x,y,a)
EndIf
Case '-';Start
turtleStart(*turtle)
Case '.';Stop
turtleStop(*turtle)
Case '#';Offset
If offset = 1
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
x = ValD(StringField(parameter,1,","))
y = ValD(StringField(parameter,2,","))
turtleOffset(*turtle,x,y)
EndIf
Case '!';Reset
turtleReset(*turtle)
Case '_'
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleLength(*turtle,x)
EndIf
Case '~';Angle
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleAngle(*turtle,x)
EndIf
Case '>';Right
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleRight(*turtle,x)
EndIf
Case '<';Left
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleLeft(*turtle,x)
EndIf
Case '@';Target
If offset = 1
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
x = ValD(StringField(parameter,1,","))
y = ValD(StringField(parameter,2,","))
turtleTarget(*turtle,x,y)
EndIf
Case '+'
turtleMove(*turtle)
Case '?'
*X\d = \start\x
*Y\d = \start\y
EndSelect
*Code = *pos + 2
Wend
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleCatch(*turtle.TURTLE_STRUCT,*Buffer.TURTLE_HEADER_STRUCT)
Protected.TURTLE_FILE_STRUCT *data
Protected.i packed,bytes
Protected *ascii
If *Buffer
If *Buffer\magic = $73636774
If *Buffer\width > 0 And *Buffer\height > 0 And *Buffer\bytes > 0
packed = Bool(*Buffer\bytes <> *Buffer\packed)
*ascii = AllocateMemory(*Buffer\bytes)
If *ascii
If packed
bytes = UncompressMemory(*Buffer + SizeOf(TURTLE_HEADER_STRUCT),*Buffer\packed,*ascii,*Buffer\bytes,#PB_PackerPlugin_Lzma)
Else
CopyMemory(*Buffer + SizeOf(TURTLE_HEADER_STRUCT),*ascii,*Buffer\bytes)
bytes = *Buffer\bytes
EndIf
If *Buffer\bytes = bytes
*data = AllocateStructure(TURTLE_FILE_STRUCT)
If *data
*data\title = PeekS(@*Buffer\title[0],64,#PB_Ascii)
*data\width = *Buffer\width
*data\height = *Buffer\height
*data\length = bytes
*data\commands = PeekS(*ascii,bytes,#PB_Ascii)
EndIf
EndIf
FreeMemory(*ascii)
EndIf
EndIf
EndIf
EndIf
ProcedureReturn *data
EndProcedure
Procedure.i turtleLoad(*turtle.TURTLE_STRUCT,File.s)
Protected.String *string
Protected.i bytes,handle,packed,result
Protected.TURTLE_HEADER_STRUCT header
Protected *ascii
Protected.TURTLE_FILE_STRUCT *data
If File
If LCase(Right(File,5)) <> ".tgcf"
File + ".tgcf"
EndIf
handle = ReadFile(#PB_Any,File)
If IsFile(handle)
bytes = Lof(handle)
If bytes > SizeOf(TURTLE_HEADER_STRUCT)
If ReadData(handle,@header,SizeOf(TURTLE_HEADER_STRUCT)) = SizeOf(TURTLE_HEADER_STRUCT)
If header\magic = $73636774
bytes - SizeOf(TURTLE_HEADER_STRUCT)
If header\width > 0 And header\height > 0 And header\bytes > 0
If header\packed = bytes
packed = Bool(header\bytes <> header\packed)
If packed
*ascii = AllocateMemory(header\bytes + header\packed)
Else
*ascii = AllocateMemory(header\bytes)
EndIf
If *ascii
If packed
result = Bool(ReadData(handle,*ascii + header\bytes,header\packed) = header\packed)
If result
bytes = UncompressMemory(*ascii + header\bytes,header\packed,*ascii,header\bytes,#PB_PackerPlugin_Lzma)
EndIf
Else
result = Bool(ReadData(handle,*ascii,header\bytes) = header\bytes)
EndIf
If result And header\bytes = bytes
*data = AllocateStructure(TURTLE_FILE_STRUCT)
If *data
*data\title = PeekS(@header\title[0],64,#PB_Ascii)
*data\width = header\width
*data\height = header\height
*data\length = bytes
*data\commands = PeekS(*ascii,bytes,#PB_Ascii)
EndIf
EndIf
FreeMemory(*ascii)
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
CloseFile(handle)
EndIf
EndIf
ProcedureReturn *data
EndProcedure
Procedure.i turtleFree(*turtle.TURTLE_STRUCT,*File.TURTLE_FILE_STRUCT)
FreeStructure(*File)
ProcedureReturn #Null
EndProcedure
Procedure.i turtleSave(*turtle.TURTLE_STRUCT,File.s,Override.b,Title.s,Width.i,Height.i,*Commands)
Protected.String *string
Protected.i bytes,handle,result
Protected.TURTLE_HEADER_STRUCT *header
Protected *ascii
If File And *Commands
If LCase(Right(File,5)) <> ".tgcf"
File + ".tgcf"
EndIf
If Override = #False
If FileSize(File) >= 0
ProcedureReturn #False
EndIf
EndIf
bytes = MemoryStringLength(*Commands)
If bytes
*string = @*Commands
*ascii = Ascii(*string\s)
If *ascii
*header = AllocateMemory(SizeOf(TURTLE_HEADER_STRUCT) + bytes)
If *header
*header\bytes = bytes
*header\packed = CompressMemory(*ascii,bytes,*header + SizeOf(TURTLE_HEADER_STRUCT),bytes,#PB_PackerPlugin_Lzma,9)
If *header\packed = 0
CopyMemory(*ascii,*header + SizeOf(TURTLE_HEADER_STRUCT),bytes)
*header\packed = *header\bytes
EndIf
*header\magic = $73636774
PokeS(@*header\title[0],Title,64,#PB_Ascii)
If Width And Height
*header\width = Width
*header\height = Height
Else
*header\width = 800
*header\height = 600
EndIf
handle = CreateFile(#PB_Any,File)
If IsFile(handle)
bytes = *header\packed + SizeOf(TURTLE_HEADER_STRUCT)
If WriteData(handle,*header,bytes) = bytes
result = Bool(FlushFileBuffers(handle) <> 0)
EndIf
CloseFile(handle)
If result = #False
DeleteFile(File)
EndIf
EndIf
FreeMemory(*header)
EndIf
FreeMemory(*ascii)
EndIf
EndIf
EndIf
ProcedureReturn result
EndProcedure
Procedure.i turtleRelease(*turtle.TURTLE_STRUCT)
FreeStructure(*turtle)
ProcedureReturn #Null
EndProcedure
;--------------------------------------------------------------------------------------
DataSection
vtable:
Data.i @turtleLine()
Data.i @turtleColor()
Data.i @turtleOrigin()
Data.i @turtleStart()
Data.i @turtleStop()
Data.i @turtleOffset()
Data.i @turtleReset()
Data.i @turtleLength()
Data.i @turtleAngle()
Data.i @turtleLeft()
Data.i @turtleRight()
Data.i @turtleTarget()
Data.i @turtleMove()
Data.i @turtlePosition()
Data.i @turtleRun()
Data.i @turtleCatch()
Data.i @turtleLoad()
Data.i @turtleFree()
Data.i @turtleSave()
Data.i @turtleRelease()
EndDataSection
Include:
Code: Select all
EnableExplicit
;--------------------------------------------------------------------------------------
;Project: Turtle Library
;File: turtle.pbi
;Version: 1.01
;Platform: Linux, Windows, Mac
;Compiler: PureBasic 6.12 LTS (x64) - C Backend
;--------------------------------------------------------------------------------------
Import "turtle.so"
turtleInterface.i(Version.i = #Null)
EndImport
#TURTLE_VERSION = 101
Interface TURTLE
Line.i(*Function)
Color.i(Color.l)
Origin.i(X.l,Y.l,Center.b = #False)
Start.i()
Stop.i()
Offset.i(X.d,Y.d)
Reset.i()
Length.i(Length.d)
Angle.i(Angle.d)
Left.i(Angle.d)
Right.i(Angle.d)
Target.i(X.d,Y.d)
Move.i()
Position.i(*X,*Y)
Run.i(*Code,*X = 0,*Y = 0)
Catch.i(*Buffer)
Load.i(File.s)
Free.i(*File)
Save.i(File.s,Override.b,Title.s,Width.i,Height.i,*Commands)
Release.i()
EndInterface
Structure TURTLE_FILE
title.s
width.l
height.l
length.i
commands.s
EndStructure
Include (helper):
Code: Select all
EnableExplicit
;--------------------------------------------------------------------------------------
;Project: Turtle Library
;File: turtle_output.pbi
;Version: 1.01
;Platform: Linux, Windows, Mac
;Compiler: PureBasic 6.12 LTS (x64) - C Backend
;--------------------------------------------------------------------------------------
Procedure.i turtleLine(X1.d,Y1.d,X2.d,Y2.d,Color.l)
ProcedureReturn LineXY(X1,Y1,X2,Y2,Color)
EndProcedure
Procedure.i turtleWindow(*Turtle.TURTLE,Title.s,Width.i,Height.i,Once.b,*Render,*Parameter = #Null,*Event = #Null)
Protected.i task,exit
If InitSprite() And *Render
If OpenWindow(0,0,0,Width,Height,Title,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
If OpenWindowedScreen(WindowID(0),0,0,WindowWidth(0),WindowHeight(0))
SetFrameRate(60)
If CreateSprite(0,WindowWidth(0),WindowHeight(0))
*Turtle\Line(@turtleLine())
*Turtle\Origin(WindowWidth(0),WindowHeight(0),#True)
If Once
If StartDrawing(SpriteOutput(0))
DrawingMode(#PB_2DDrawing_AllChannels)
If CallFunctionFast(*Render,*Turtle,*Parameter)
exit = #True
EndIf
StopDrawing()
EndIf
EndIf
Repeat
Repeat
task = WindowEvent()
Select task
Case #PB_Event_None
Break
Case #PB_Event_CloseWindow
exit = #True
Default
If *Event
CallFunctionFast(*Event,task)
EndIf
EndSelect
ForEver
ClearScreen(0)
If Once = #False
If StartDrawing(SpriteOutput(0))
DrawingMode(#PB_2DDrawing_AllChannels)
If CallFunctionFast(*Render,*Turtle,*Parameter)
exit = #True
EndIf
StopDrawing()
EndIf
EndIf
DisplaySprite(0,0,0)
FlipBuffers()
Until exit
FreeSprite(0)
EndIf
CloseScreen()
EndIf
CloseWindow(0)
EndIf
EndIf
ProcedureReturn #Null
EndProcedure
Example:
Code: Select all
EnableExplicit
;--------------------------------------------------------------------------------------
;Project: Turtle Library
;File: turtle_example.pb
;Version: 1.01
;Platform: Linux, Windows, Mac
;Compiler: PureBasic 6.12 LTS (x64) - C Backend
;--------------------------------------------------------------------------------------
XIncludeFile "turtle.pbi"
XIncludeFile "turtle_output.pbi"
Procedure.i Render(*turtle.TURTLE,*Parameter)
Protected.i x,y
With *turtle
\Offset(520,158)
\Start()
\Length(100)
\Color(RGBA(255,60,60,255))
For y = 1 To 20
For x = 1 To 20
\Right(30)
\Move()
Next
\Left(20)
\Move()
Next
ProcedureReturn #Null
EndWith
EndProcedure
; Procedure.i Render(*turtle.TURTLE,*Parameter);<- string command version
; Protected.i x,y
; Protected.s c1,c2,c3
; With *turtle
; c1 = "#(520,158)-_(100)$(255,60,60,255)"
; c2 = ">(30)+"
; c3 = "<(20)+"
; \Run(@c1)
; For y = 1 To 20
; For x = 1 To 20
; \Run(@c2)
; Next
; \Run(@c3)
; Next
; ProcedureReturn #Null
; EndWith
; EndProcedure
Procedure.i Main()
Protected *turtle.TURTLE
*turtle = turtleInterface()
If *turtle
turtleWindow(*turtle,"Turtle Graphics",800,600,#True,@Render())
*turtle\Release()
EndIf
ProcedureReturn #Null
EndProcedure
End Main()
The whole thing as single Module (some functions have been adjusted):
Code: Select all
EnableExplicit
;--------------------------------------------------------------------------------------
;Project: Turtle Module
;File: turtle_module.pbi
;Version: 1.01
;Platform: Linux, Windows, Mac
;Compiler: PureBasic 6.12 LTS (x64) - C Backend
;--------------------------------------------------------------------------------------
; MIT License
; Copyright 2024 © Mijikai
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in all
; copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.
;--------------------------------------------------------------------------------------
;Component: lzma
;LZMA SDK is written and placed in the public domain by Igor Pavlov.
;Some code in LZMA SDK is based on public domain code from other developers:
;1) PPMd var.H (2001): Dmitry Shkarin
;2) SHA-256: Wei Dai (Crypto++ library)
;--------------------------------------------------------------------------------------
DeclareModule turtle
Interface TURTLE
Line.i(*Function)
Color.i(Color.l)
Origin.i(X.l,Y.l,Center.b = #False)
Start.i()
Stop.i()
Offset.i(X.d,Y.d)
Reset.i()
Length.i(Length.d)
Angle.i(Angle.d)
Left.i(Angle.d)
Right.i(Angle.d)
Target.i(X.d,Y.d)
Move.i()
Position.i(*X,*Y)
Run.i(*Code,*X = 0,*Y = 0)
Catch.i(*Buffer)
Load.i(File.s)
Free.i(*File)
Save.i(File.s,Override.b,Title.s,Width.i,Height.i,*Commands)
Release.i()
EndInterface
Structure TURTLE_FILE
title.s
width.l
height.l
length.i
commands.s
EndStructure
Declare.i Create()
Declare.i Window(*Turtle,Title.s,Width.i,Height.i,Once.b,*Render,*Parameter = #Null,*Event = #Null)
Declare.i Clear()
EndDeclareModule
Module turtle
UseLZMAPacker()
Prototype.i turtleLine(X1.d,Y1.d,X2.d,Y2.d,Color.l)
Structure TURTLE_HEADER_STRUCT
magic.l
width.l
height.l
title.a[64]
bytes.q
packed.q
EndStructure
Structure TURTLE_FILE_STRUCT
title.s
width.l
height.l
length.i
commands.s
EndStructure
Structure TURTLE_POSITION_STRUCT
x.d
y.d
EndStructure
Structure TURTLE_STRUCT
*vtable
render.l
color.l
origin.TURTLE_POSITION_STRUCT
start.TURTLE_POSITION_STRUCT
stop.TURTLE_POSITION_STRUCT
direction.d
length.d
*draw.turtleLine
EndStructure
#TURTLE_VERSION = 101
;--------------------------------------------------------------------------------------
Procedure.i Create()
Protected *turtle.TURTLE_STRUCT
With *turtle
*turtle = AllocateStructure(TURTLE_STRUCT)
If *turtle
\vtable = ?vtable
\color = $FFFFFFFF
EndIf
ProcedureReturn *turtle
EndWith
EndProcedure
;--------------------------------------------------------------------------------------
Procedure.i turtleLine(*turtle.TURTLE_STRUCT,*Function)
With *turtle
\draw = *Function
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleColor(*turtle.TURTLE_STRUCT,Color.l)
With *turtle
\color = Color
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleOrigin(*turtle.TURTLE_STRUCT,X.l,Y.l,Center.b)
With *turtle
If Center
\origin\x = X / 2.0
\origin\y = Y / 2.0
Else
\origin\x = X
\origin\y = Y
EndIf
\start = \origin
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleStart(*turtle.TURTLE_STRUCT)
With *turtle
\render = #True
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleStop(*turtle.TURTLE_STRUCT)
With *turtle
\render = #False
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleOffset(*turtle.TURTLE_STRUCT,X.d,Y.d)
With *turtle
\start\x = X
\start\y = Y
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleReset(*turtle.TURTLE_STRUCT)
With *turtle
\start = \origin
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleLength(*turtle.TURTLE_STRUCT,Length.d)
With *turtle
\length = Length
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleAngle(*turtle.TURTLE_STRUCT,Angle.d)
With *turtle
\direction = Radian(Angle)
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleLeft(*turtle.TURTLE_STRUCT,Angle.d)
With *turtle
\direction - Radian(Angle)
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleRight(*turtle.TURTLE_STRUCT,Angle.d)
With *turtle
\direction + Radian(Angle)
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleTarget(*turtle.TURTLE_STRUCT,X.d,Y.d)
With *turtle
\direction = ATan2((X - \start\x),(Y - \start\y))
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleMove(*turtle.TURTLE_STRUCT)
With *turtle
\stop\x = \start\x + (Cos(\direction) * \length)
\stop\y = \start\y + (Sin(\direction) * \length)
If \render
\draw(\start\x,\start\y,\stop\x,\stop\y,\color)
EndIf
\start = \stop
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtlePosition(*turtle.TURTLE_STRUCT,*X.Double,*Y.Double)
With *turtle
*X\d = \start\x
*Y\d = \start\y
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleRun(*turtle.TURTLE_STRUCT,*Code.Ascii,*X.Double,*Y.Double)
Protected.d x,y
Protected.i offset,a,b,c,d
Protected.Ascii *pos
Protected.s parameter
With *turtle
While *Code\a
offset = 0
*pos = *Code + 2
If *pos\a = '('
Repeat
*pos + 2
If *pos\a = ','
offset + 1
EndIf
Until *pos\a = ')'
Else
*pos = *Code
EndIf
Select *Code\a
Case '$';Color
If offset = 3
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
a = Val(StringField(parameter,1,","))
b = Val(StringField(parameter,2,","))
c = Val(StringField(parameter,3,","))
d = Val(StringField(parameter,4,","))
turtleColor(*turtle,RGBA(a,b,c,d))
EndIf
Case '*';Origin
If offset = 2
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
x = ValD(StringField(parameter,1,","))
y = ValD(StringField(parameter,2,","))
a = Val(StringField(parameter,3,","))
turtleOrigin(*turtle,x,y,a)
EndIf
Case '-';Start
turtleStart(*turtle)
Case '.';Stop
turtleStop(*turtle)
Case '#';Offset
If offset = 1
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
x = ValD(StringField(parameter,1,","))
y = ValD(StringField(parameter,2,","))
turtleOffset(*turtle,x,y)
EndIf
Case '!';Reset
turtleReset(*turtle)
Case '_'
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleLength(*turtle,x)
EndIf
Case '~';Angle
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleAngle(*turtle,x)
EndIf
Case '>';Right
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleRight(*turtle,x)
EndIf
Case '<';Left
If *Code <> *pos
x = ValD(PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode))
turtleLeft(*turtle,x)
EndIf
Case '@';Target
If offset = 1
parameter = PeekS(*Code + 4,((*pos - *Code) >> 1) - 2,#PB_Unicode)
x = ValD(StringField(parameter,1,","))
y = ValD(StringField(parameter,2,","))
turtleTarget(*turtle,x,y)
EndIf
Case '+'
turtleMove(*turtle)
Case '?'
*X\d = \start\x
*Y\d = \start\y
EndSelect
*Code = *pos + 2
Wend
ProcedureReturn #Null
EndWith
EndProcedure
Procedure.i turtleCatch(*turtle.TURTLE_STRUCT,*Buffer.TURTLE_HEADER_STRUCT)
Protected.TURTLE_FILE_STRUCT *data
Protected.i packed,bytes
Protected *ascii
If *Buffer
If *Buffer\magic = $73636774
If *Buffer\width > 0 And *Buffer\height > 0 And *Buffer\bytes > 0
packed = Bool(*Buffer\bytes <> *Buffer\packed)
*ascii = AllocateMemory(*Buffer\bytes)
If *ascii
If packed
bytes = UncompressMemory(*Buffer + SizeOf(TURTLE_HEADER_STRUCT),*Buffer\packed,*ascii,*Buffer\bytes,#PB_PackerPlugin_Lzma)
Else
CopyMemory(*Buffer + SizeOf(TURTLE_HEADER_STRUCT),*ascii,*Buffer\bytes)
bytes = *Buffer\bytes
EndIf
If *Buffer\bytes = bytes
*data = AllocateStructure(TURTLE_FILE_STRUCT)
If *data
*data\title = PeekS(@*Buffer\title[0],64,#PB_Ascii)
*data\width = *Buffer\width
*data\height = *Buffer\height
*data\length = bytes
*data\commands = PeekS(*ascii,bytes,#PB_Ascii)
EndIf
EndIf
FreeMemory(*ascii)
EndIf
EndIf
EndIf
EndIf
ProcedureReturn *data
EndProcedure
Procedure.i turtleLoad(*turtle.TURTLE_STRUCT,File.s)
Protected.String *string
Protected.i bytes,handle,packed,result
Protected.TURTLE_HEADER_STRUCT header
Protected *ascii
Protected.TURTLE_FILE_STRUCT *data
If File
If LCase(Right(File,5)) <> ".tgcf"
File + ".tgcf"
EndIf
handle = ReadFile(#PB_Any,File)
If IsFile(handle)
bytes = Lof(handle)
If bytes > SizeOf(TURTLE_HEADER_STRUCT)
If ReadData(handle,@header,SizeOf(TURTLE_HEADER_STRUCT)) = SizeOf(TURTLE_HEADER_STRUCT)
If header\magic = $73636774
bytes - SizeOf(TURTLE_HEADER_STRUCT)
If header\width > 0 And header\height > 0 And header\bytes > 0
If header\packed = bytes
packed = Bool(header\bytes <> header\packed)
If packed
*ascii = AllocateMemory(header\bytes + header\packed)
Else
*ascii = AllocateMemory(header\bytes)
EndIf
If *ascii
If packed
result = Bool(ReadData(handle,*ascii + header\bytes,header\packed) = header\packed)
If result
bytes = UncompressMemory(*ascii + header\bytes,header\packed,*ascii,header\bytes,#PB_PackerPlugin_Lzma)
EndIf
Else
result = Bool(ReadData(handle,*ascii,header\bytes) = header\bytes)
EndIf
If result And header\bytes = bytes
*data = AllocateStructure(TURTLE_FILE_STRUCT)
If *data
*data\title = PeekS(@header\title[0],64,#PB_Ascii)
*data\width = header\width
*data\height = header\height
*data\length = bytes
*data\commands = PeekS(*ascii,bytes,#PB_Ascii)
EndIf
EndIf
FreeMemory(*ascii)
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
CloseFile(handle)
EndIf
EndIf
ProcedureReturn *data
EndProcedure
Procedure.i turtleFree(*turtle.TURTLE_STRUCT,*File.TURTLE_FILE_STRUCT)
FreeStructure(*File)
ProcedureReturn #Null
EndProcedure
Procedure.i turtleSave(*turtle.TURTLE_STRUCT,File.s,Override.b,Title.s,Width.i,Height.i,*Commands)
Protected.String *string
Protected.i bytes,handle,result
Protected.TURTLE_HEADER_STRUCT *header
Protected *ascii
If File And *Commands
If LCase(Right(File,5)) <> ".tgcf"
File + ".tgcf"
EndIf
If Override = #False
If FileSize(File) >= 0
ProcedureReturn #False
EndIf
EndIf
bytes = MemoryStringLength(*Commands)
If bytes
*string = @*Commands
*ascii = Ascii(*string\s)
If *ascii
*header = AllocateMemory(SizeOf(TURTLE_HEADER_STRUCT) + bytes)
If *header
*header\bytes = bytes
*header\packed = CompressMemory(*ascii,bytes,*header + SizeOf(TURTLE_HEADER_STRUCT),bytes,#PB_PackerPlugin_Lzma,9)
If *header\packed = 0
CopyMemory(*ascii,*header + SizeOf(TURTLE_HEADER_STRUCT),bytes)
*header\packed = *header\bytes
EndIf
*header\magic = $73636774
PokeS(@*header\title[0],Title,64,#PB_Ascii)
If Width And Height
*header\width = Width
*header\height = Height
Else
*header\width = 800
*header\height = 600
EndIf
handle = CreateFile(#PB_Any,File)
If IsFile(handle)
bytes = *header\packed + SizeOf(TURTLE_HEADER_STRUCT)
If WriteData(handle,*header,bytes) = bytes
result = Bool(FlushFileBuffers(handle) <> 0)
EndIf
CloseFile(handle)
If result = #False
DeleteFile(File)
EndIf
EndIf
FreeMemory(*header)
EndIf
FreeMemory(*ascii)
EndIf
EndIf
EndIf
ProcedureReturn result
EndProcedure
Procedure.i turtleRelease(*turtle.TURTLE_STRUCT)
FreeStructure(*turtle)
ProcedureReturn #Null
EndProcedure
;--------------------------------------------------------------------------------------
Procedure.i turtleDraw(X1.d,Y1.d,X2.d,Y2.d,Color.l)
ProcedureReturn LineXY(X1,Y1,X2,Y2,Color)
EndProcedure
Procedure.i Window(*Turtle,Title.s,Width.i,Height.i,Once.b,*Render,*Parameter = #Null,*Event = #Null)
Protected.i task,exit
If *Turtle
If InitSprite() And *Render
If OpenWindow(0,0,0,Width,Height,Title,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
If OpenWindowedScreen(WindowID(0),0,0,WindowWidth(0),WindowHeight(0))
SetFrameRate(60)
If CreateSprite(0,WindowWidth(0),WindowHeight(0))
turtleLine(*Turtle,@turtleDraw())
turtleOrigin(*Turtle,WindowWidth(0),WindowHeight(0),#True)
If Once
If StartDrawing(SpriteOutput(0))
DrawingMode(#PB_2DDrawing_AllChannels)
If CallFunctionFast(*Render,*Turtle,*Parameter)
exit = #True
EndIf
StopDrawing()
EndIf
EndIf
Repeat
Repeat
task = WindowEvent()
Select task
Case #PB_Event_None
Break
Case #PB_Event_CloseWindow
exit = #True
Default
If *Event
CallFunctionFast(*Event,task)
EndIf
EndSelect
ForEver
ClearScreen(0)
If Once = #False
If StartDrawing(SpriteOutput(0))
DrawingMode(#PB_2DDrawing_AllChannels)
If CallFunctionFast(*Render,*Turtle,*Parameter)
exit = #True
EndIf
StopDrawing()
EndIf
EndIf
DisplaySprite(0,0,0)
FlipBuffers()
Until exit
FreeSprite(0)
EndIf
CloseScreen()
EndIf
CloseWindow(0)
EndIf
EndIf
EndIf
ProcedureReturn #Null
EndProcedure
Procedure.i Clear()
ProcedureReturn Box(0,0,WindowWidth(0),WindowHeight(0),RGBA(0,0,0,0))
EndProcedure
;--------------------------------------------------------------------------------------
DataSection
vtable:
Data.i @turtleLine()
Data.i @turtleColor()
Data.i @turtleOrigin()
Data.i @turtleStart()
Data.i @turtleStop()
Data.i @turtleOffset()
Data.i @turtleReset()
Data.i @turtleLength()
Data.i @turtleAngle()
Data.i @turtleLeft()
Data.i @turtleRight()
Data.i @turtleTarget()
Data.i @turtleMove()
Data.i @turtlePosition()
Data.i @turtleRun()
Data.i @turtleCatch()
Data.i @turtleLoad()
Data.i @turtleFree()
Data.i @turtleSave()
Data.i @turtleRelease()
EndDataSection
EndModule
Procedure.i Render(*turtle.turtle::TURTLE,*Parameter);Example 1
Protected.i x
Static.i t,s
With *turtle
\Start()
\Angle(270)
If t % 2 = 0
\Color(RGBA(Random(127) + 128,Random(127) + 128,Random(127) + 128,255))
\Length(50 + s)
\Offset(480 + s / 4,360 + s / 2)
s + 4
If s > 160
turtle::Clear()
s = 0
EndIf
EndIf
For x = 1 To 9
\Right(100)
\Move()
\Left(140)
\Move()
Next
t + 1
ProcedureReturn #Null
EndWith
EndProcedure
; Procedure.i Render(*turtle.turtle::TURTLE,*Parameter);Example 2
; Static.i x,y,i
; Static.s Dim s(80),c
; With *turtle
; If i
; If y % 2 = 0 And x < 80
; c = s(x)
; \Run(@c)
; x + 1
; If y % 16 = 0
; \Color(RGBA(Random(127) + 128,Random(127) + 128,Random(127) + 128,255))
; EndIf
; ElseIf x = 80
; turtle::Clear()
; x = 0
; y = 0
; EndIf
; y + 1
; Else
; s(i) = "#(420,340)-_(200)~(270)"
; i + 1
; For x = 1 To 9
; s(i) = ">(100)+<(140)+"
; i + 1
; For y = 1 To 7
; s(i) = ">(200)+"
; i + 1
; Next
; Next
; x = 0
; y = 0
; EndIf
; ProcedureReturn #Null
; EndWith
; EndProcedure
Procedure.i Main()
Protected.turtle::TURTLE *t
*t = turtle::Create()
If *t
turtle::Window(*t,"Turtle Graphics",800,600,#False,@Render())
*t\Release()
EndIf
EndProcedure
End Main()