🐒 B4XTurtle - Examples for teachers and parents

Status
Not open for further replies.

Erel

Administrator
Staff member
Licensed User
The purpose of this thread is to build a collection of examples that will help you, the teacher or parent, learn how to use and teach with B4XTurtle library.

If you have any question, feedback or request then please post it here: https://www.b4x.com/android/forum/threads/🐒-πŸ‘©β€πŸ«-πŸ‘¨β€πŸ«-b4xturtle-examples-for-teachers-discuss-here.115896/

The attached projects include a suggested template. It is clean and simple and looks like this:



Some of the examples are based on: https://personal.utdallas.edu/~veerasam/logo/

The examples are not listed in any special order.
Feel free to use these examples in any way you like.

B4XTurtle is an internal library.
 

Attachments

Last edited:

Erel

Administrator
Staff member
Licensed User
Drawing a square:



Implementation #1:

B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    Turtle.MoveForward(100).TurnLeft(90)
    Turtle.MoveForward(100).TurnLeft(90)
    Turtle.MoveForward(100).TurnLeft(90)
    Turtle.MoveForward(100).TurnLeft(90)
End Sub
Implementation #2:
With a For loop
B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    For i = 1 To 4
        Turtle.MoveForward(100).TurnLeft(90)
    Next
End Sub
Implementation #3:
Reusable sub where the edge size is given as a parameter.


B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    DrawSquare(100)
    'and another smaller green square
    Turtle.SetPenColor(xui.Color_Green)
    DrawSquare(50)
End Sub

Sub DrawSquare (EdgeSize As Float)
    For i = 1 To 4
        Turtle.MoveForward(EdgeSize).TurnLeft(90)
    Next
End Sub
 
Last edited:

Erel

Administrator
Staff member
Licensed User
Drawing a circle:



Implementation #1:
B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    For i = 1 To 360
        Turtle.MoveForward(1).TurnLeft(1)
    Next
End Sub
Implementation #2:
A filled circle:


B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    For i = 1 To 360
        Turtle.MoveForward(1).TurnLeft(1)
    Next
    Turtle.PenUp.TurnLeft(90).MoveForward(20).SetPenColor(xui.Color_Yellow).Fill
End Sub
Implementation #3:
A circle with a specific radius:



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    Dim radius As Float = 100
    DrawCircle(radius)
    Turtle.PenUp.MoveBackward(radius).PenDown
    Turtle.SetPenColor(0xFFFF00B6) 'right click - color picker
    DrawSquare(radius * 2)
End Sub

Sub DrawCircle (Radius As Float)
    Dim DistancePerIteration As Float = 2 * cPI * Radius / 360
    For i = 1 To 360
        Turtle.MoveForward(DistancePerIteration).TurnLeft(1)
    Next
End Sub

Sub DrawSquare (EdgeSize As Float)
    For i = 1 To 4
        Turtle.MoveForward(EdgeSize).TurnLeft(90)
    Next
End Sub
 

Erel

Administrator
Staff member
Licensed User
Drawing a star:


Implementation #1:

B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    DrawStar (100)
End Sub

Sub DrawStar (EdgeSize As Float)
    For i = 1 To 5
        Turtle.MoveForward(EdgeSize).TurnRight(144)   
    Next
End Sub
Implementation #2:
With random colors.



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(1)
    DrawStar (100)
End Sub

Sub DrawStar (EdgeSize As Float)
    For i = 1 To 5
        Turtle.SetPenColor(Turtle.RandomColor)
        Turtle.MoveForward(EdgeSize).TurnRight(144)   
    Next
End Sub
Implementation #3:
With recursion.



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(5)
    DrawStar (200)
End Sub

Sub DrawStar (EdgeSize As Float)
    If EdgeSize < 50 Then Return
    For i = 1 To 5
        Turtle.SetPenColor(Turtle.RandomColor)
        Turtle.MoveForward(EdgeSize).TurnRight(144)   
        DrawStar(EdgeSize / 4)
    Next
End Sub
 

Erel

Administrator
Staff member
Licensed User
Full circles:



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(2).SetSpeedFactor(10)
    Dim size As Float = 150
    For i = 1 To 8
        Turtle.SetPenColor(Turtle.RandomColor).Arc(360, size).Fill
        size = size - 20
    Next
End Sub
Note that the Fill command is a bit slow on mobile devices and the result is not perfect because of antialiasing effects.
 
Last edited:

Erel

Administrator
Staff member
Licensed User
Drawing a flower:



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(2).SetSpeedFactor(10)
    Dim turn As Float = 0
    Do While turn < 360
        Turtle.SetPenColor(Turtle.RandomColor)
        Dim angle As Float = 0
        Do While angle < 180
            Turtle.MoveForward(30).TurnRight(angle)
            angle = angle + 5
        Loop
        Turtle.Home
        turn = turn + 60
        Turtle.TurnRight(turn)
    Loop
End Sub
 

Erel

Administrator
Staff member
Licensed User
Squares spiral:



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(2).SetSpeedFactor(30)
    Dim size As Float = 200
    For i = 1 To 36
        Turtle.SetPenColor(Turtle.RandomColor)
        DrawSquare (size)
        Turtle.TurnRight(10)
        size = size - 5
    Next
End Sub

Sub DrawSquare (EdgeSize As Float)
    For i = 1 To 4
        Turtle.MoveForward(EdgeSize).TurnLeft(90)
    Next
End Sub
 

Erel

Administrator
Staff member
Licensed User
Handling the touch event.

Implementation #1:



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(2).SetSpeedFactor(1)
End Sub

Sub Turtle_Touch (Args As TurtleTouchArgs)
    Turtle.MoveTo(Args.X, Args.Y)
End Sub
Implementation #2:

This time we call Turtle.Stop in the Touch event. Turtle.Stop is a special command. It clears the actions queue so the turtle immediately stops moving and then with a call to MoveTo starts moving towards the touch point.





B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(2).SetSpeedFactor(1)
End Sub

Sub Turtle_Touch (Args As TurtleTouchArgs)
    Turtle.Stop
    Turtle.MoveTo(Args.X, Args.Y)
End Sub
 

Erel

Administrator
Staff member
Licensed User
Global variables and If condition:

1. Add this line to Process_Globals:
B4X:
Private PrevX, PrevY As Float
2.
B4X:
Sub Turtle_Touch (Args As TurtleTouchArgs)
    Turtle.SetX(Args.X).SetY(Args.Y)
    If Args.Down Then
        PrevX = Args.X
        PrevY = Args.Y
        Turtle.MoveForward(2)
    Else If Args.Up Then
        Turtle.MoveTo(PrevX, PrevY)
    End If
End Sub
Result:

 

Erel

Administrator
Staff member
Licensed User
Tree:



Add to Process_Globals:
B4X:
Private Angle1 As Float = 15
    Private Angle2 As Float = 30
    Private Factor1 As Float = 0.9
    Private Factor2 As Float = 0.8
And:
B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(2).SetSpeedFactor(30)
    Turtle.SetY(Turtle.Height - 50).TurnLeft(90)
    Tree(12, 60)
End Sub

Sub Tree (Level As Float, Size As Float)
    If Level > 0 Then
        Turtle.SetPenSize(1 + Level / 2)
        If Level < 3 Then 
            Turtle.SetPenColor(0xFF087A00)
        Else
            Turtle.SetPenColor(xui.Color_Black)
        End If
        Turtle.MoveForward(Size).TurnLeft(Angle1)
        Tree(Level - 1, Size * Factor1)
        Turtle.TurnRight(Angle1).TurnRight(Angle2)
        Tree(Level - 1, Size * Factor2)
        Turtle.TurnLeft(Angle2)
        Turtle.PenUp.MoveBackward(Size).PenDown
    End If
End Sub
 

Erel

Administrator
Staff member
Licensed User
Moving the turtle with the phone accelerometer.

1588081900378.png


This is a B4A example that uses the phone sensor to control the turtle.

The core code is:
B4X:
Sub Accelerometer_SensorChanged (Values() As Float)
    Turtle.Stop
    Dim angle As Float = ATan2D(Values(1), -Values(0))
    Turtle.SetAngle(angle)
    Turtle.MoveForward(50)
End Sub
This is not the "best" implementation possible howerver I wanted to keep the code simple.

The B4A project is attached.
 

Attachments

Erel

Administrator
Staff member
Licensed User
Analog clock:



Several interesting things about this example:
  1. It uses two layers, one for the clock layout and one for the hands. Only the second layer is redrawn every second.
  2. It uses a timer that ticks every second.
  3. Turtle is set to be in "rabbit mode" - there are no animations.
  4. The turtle itself is hidden with SetTurtleVisible (False).

Code:

1. Add to Process_Globals:
B4X:
Private Timer1 As Timer
2.:
B4X:
Sub Turtle_Start
    If Timer1.IsInitialized = False Then
        Timer1.Initialize("Timer1", 1000)
    End If
    Turtle.RabbitMode.SetTurtleVisible(False)
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(3)
    Turtle.SetNumberOfLayers(2)
    Turtle.SetCurrentLayer(0).ClearScreen
    Turtle.TurnRight(90).PenUp.MoveForward(110).PenDown.TurnLeft(90)
    DrawCircle(110)
    For i = 1 To 12
        Turtle.Home
        Turtle.SetAngle(HoursToAngle(i))
        Turtle.SetPenColor(xui.Color_Blue)
        Turtle.PenUp.MoveForward(100).PenDown.MoveForward(10).PenUp.MoveBackward(30)
        Turtle.SetPenColor(xui.Color_Black).DrawText(i)
    Next
    Timer1.Enabled = True
End Sub

Sub Timer1_Tick
    Turtle.SetCurrentLayer(1) 'second layer
    Turtle.ClearScreen
    Turtle.Home
    Dim hours As Int = DateTime.GetHour(DateTime.Now)
    Dim minutes As Int = DateTime.GetMinute(DateTime.Now)
    Dim seconds As Int = DateTime.GetSecond(DateTime.Now)
    Turtle.PenDown
    Turtle.SetPenSize(6).SetPenColor(0xFF6D6D6D)
    Turtle.SetAngle(HoursToAngle(hours + minutes / 60)).MoveForward(60)
    Turtle.SetPenSize(4).SetPenColor(0xFF4B4B4B).Home
    Turtle.SetAngle(MinutesOrSecondsToAngle(minutes + seconds / 60)).MoveForward(100)
    Turtle.SetPenSize(2).SetPenColor(0xFFBB0000).Home
    Turtle.SetAngle(MinutesOrSecondsToAngle(seconds)).MoveForward(100)
End Sub

Sub HoursToAngle (Hours As Float) As Float
    Return Hours / 12 * 360 - 90
End Sub

Sub MinutesOrSecondsToAngle (N As Float) As Float
    Return N / 60 * 360 - 90
End Sub

Sub DrawCircle (Radius As Float)
    Dim DistancePerIteration As Float = 2 * cPI * Radius / 360
    For i = 1 To 360
        Turtle.MoveForward(DistancePerIteration).TurnLeft(1)
    Next
End Sub

Sub MainForm_Resize (Width As Double, Height As Double)
    If Timer1.IsInitialized = False Then Return 'Resize event can be raised before Turtle_Start. We want to ignore those events.
    Turtle.Stop.ClearScreen.Home
    Turtle_Start
End Sub
Depends on B4XTurtle 1.03+.
 

Erel

Administrator
Staff member
Licensed User
Rabbit mode and the Get methods. This is an important example.

Lets start with a spiral and a square:


The code:
B4X:
Turtle.SetPenColor(xui.Color_Blue).SetPenSize(3).SetSpeedFactor(20)
Turtle.SetX(100).SetY(100)
For i = 1 To 4
    Turtle.MoveForward(200).TurnRight(90)
Next
Turtle.MoveForward(100).SetPenColor(xui.Color_Green)
For i = 1 To 2000
    Turtle.MoveForward(3 - i / 1000)
    Turtle.TurnRight(1)
Next
Now we want to limit the spiral drawing to the square interior.
So we add code that raises or puts down the pen based on the values returned from Turtle.GetX and Turtle.GetY:
B4X:
For i = 1 To 2000
    Dim x As Float = Turtle.GetX
    Dim y As Float = Turtle.GetY
    If x >= 100 And x <= 300 And y >= 100 And y <= 300 Then
        Turtle.PenDown
    Else
        Turtle.PenUp
    End If
    Turtle.MoveForward(3 - i / 1000)
    Turtle.TurnRight(1)
Next
It doesn't work. The full spiral is drawn, exactly as above.

So we add a call to Log inside the loop that will print the coordinates in the logs tab:
B4X:
Log("x = " & x & ", y = " & y)
Surprisingly it prints:



These are the "home" coordinates.

Why GetX and GetY return the wrong values???

The answer is that the actions are not executed immediately, they are added to a queue and the queue is then processed. This allows the drawings to be animated.
So all of this code will be executed before the turtle moves a single pixel. This is why GetX and GetY return the home coordinates.

The solution is to switch to rabbit mode. In rabbit mode the actions are executed immediately.



The complete code:
B4X:
Sub Turtle_Start
    Turtle.RabbitMode
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(3).SetSpeedFactor(20)
    Turtle.SetX(100).SetY(100)
    For i = 1 To 4
        Turtle.MoveForward(200).TurnRight(90)
    Next
    Turtle.MoveForward(100).SetPenColor(xui.Color_Green)
    For i = 1 To 2000
        Dim x As Float = Turtle.GetX
        Dim y As Float = Turtle.GetY
        Log("x = " & x & ", y = " & y)
        If x >= 100 And x <= 300 And y >= 100 And y <= 300 Then
            Turtle.PenDown
        Else
            Turtle.PenUp
        End If
        Turtle.MoveForward(3 - i / 1000)
        Turtle.TurnRight(1)
    Next
End Sub
Note that it is important that RabbitMode will be the very first call.

If the call to RabbitMode is not the first call, it will only switch to rabbit mode when the action is processed. This will happen after the code is executed and again the Get methods will not return the expected values.
The way to switch to rabbit mode and to be able to get the current values is by waiting for the Done event:
B4X:
Sub DrawSpiralInsideSquare
    If Turtle.IsMoving Then 'important check. If IsMoving is False then the Done event will not be raised.
        Wait For Turtle_Done
    End If
    Turtle.RabbitMode
    Turtle.MoveForward(100).SetPenColor(xui.Color_Green)
    For i = 1 To 2000
        Dim x As Float = Turtle.GetX
        Dim y As Float = Turtle.GetY
        If x >= 100 And x <= 300 And y >= 100 And y <= 300 Then
            Turtle.PenDown
        Else
            Turtle.PenUp
        End If
        Turtle.MoveForward(3 - i / 1000)
        Turtle.TurnRight(1)
    Next
End Sub
 

Erel

Administrator
Staff member
Licensed User
Drawing bitmaps:



B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(3).SetSpeedFactor(1)
    Dim smiley As B4XBitmap = xui.LoadBitmapResize(File.DirAssets, "smiley.png", 100, 100, True)
    Turtle.PenUp
    For i = 0 To 360
        Turtle.MoveForward(2).TurnLeft(1)
        If i Mod 60 = 0 Then Turtle.DrawBitmap(smiley)
    Next
End Sub
Add the attached file to the Files tab.
 

Attachments

Erel

Administrator
Staff member
Licensed User
PushState / PopState

We want to draw this nice circle:



We start with this code:
B4X:
Sub Turtle_Start
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(3).SetSpeedFactor(1)
    For i = 1 To 360
        Turtle.MoveForward(2).TurnLeft(1)
        If i Mod 60 = 0 Then DrawGreenCircle
    Next
End Sub

Sub DrawGreenCircle
    Turtle.SetPenColor(xui.Color_Green)
    For i = 1 To 360
        Turtle.MoveForward(0.5).TurnLeft(1)
    Next
End Sub
Worth explaining line #5, the Mod operator (modulo) is a very important operator. It returns the remainder of i divided by 60. It will be 0 when 'i' equals to: 0, 60, 120, 180 and so on.
The result is that we call DrawGreenCircle every 60 degrees. (it would have been a bit more elegant, at least for myself, if the for loop range was from 0 to 359 but it is not important).

We run the code and we get:



There is a big problem. Ideally, a sub (method) shouldn't have side effects. We want to call it, let it do its job, and continue with our job. This is not the case here as it changed the pen color.
BTW, this is one of the reasons that you should use at least as possible global variables. A sub that modifies global variables is a sub that has side effects and is therefore a sub that adds complexity to your program.

The solution with B4XTurtle is simple.

We change the sub code to:
B4X:
Sub DrawGreenCircle
    Turtle.PushState
    Turtle.SetPenColor(xui.Color_Green)
    For i = 1 To 360
        Turtle.MoveForward(0.5).TurnLeft(1)
    Next
    Turtle.PopState
End Sub
When it starts it "pushes" the current turtle state to an internal stack. Right before it ends it pops the newly added state and returns the turtle state to the previous one.
A stack is a Last In First Out (LIFO) data collection. The last state that was added is the first one to be removed. This is different than a queue which is a First In First Out (FIFO) data collection.
 
Last edited:

Erel

Administrator
Staff member
Licensed User
Multilayers



By default there is a single drawing layer, however B4XTurtle supports multiple layers. In this example the background is drawn on layer #0 and the time string is drawn on the second layer which is layer #1. Indices in B4X always start from 0.
SetNumberOfLayers defines the number of layers.
SetCurrentLayer sets the current layer.

I've used a custom font here. Source: https://www.dafont.com/ds-digital.font
The code to load custom fonts is platform specific. This is the B4J code. It is quite similar in the other platforms.
B4X:
Sub Turtle_Start
    Turtle.SetFontAndAlignment(xui.CreateFont(fx.LoadFont(File.DirAssets, "DS-DIGI.TTF", 40), 40), "CENTER")
    Turtle.SetNumberOfLayers(2)
    Turtle.SetCurrentLayer(0) 'first layer = 0
    Turtle.SetPenColor(xui.Color_Blue).SetPenSize(5).SetSpeedFactor(2)
    Turtle.SetX(40).SetY(125)
    For i = 1 To 2
        Turtle.MoveForward(120)
        RoundCorner
        Turtle.MoveForward(50)
        RoundCorner
    Next
    Turtle.PenUp.TurnLeft(90).MoveForward(20).SetPenColor(0xFF82E4FF).Fill
    Turtle.SetCurrentLayer(1) 'switch to second layer
    If Timer1.IsInitialized = False Then
        Timer1.Initialize("Timer1", 1000)
        Timer1.Enabled = True
    End If
    Turtle.SetTurtleVisible(False)
End Sub

Sub RoundCorner
    For i = 1 To 90
        Turtle.MoveForward(0.2).TurnLeft(1)
    Next
End Sub

Sub Timer1_Tick
    Turtle.ClearScreen
    Turtle.SetPenColor(xui.Color_Black)
    Turtle.SetX(100).SetY(90).DrawText(DateTime.Time(DateTime.Now))
End Sub
Add under Process_Globals
B4X:
Private Timer1 As Timer
 

Erel

Administrator
Staff member
Licensed User
Lists and For loops



List is a simple and important data structure. You can add and remove elements from a list. The elements can be of any type.
B4X:
Sub Turtle_Start
    Turtle.SetSpeedFactor(3)
    Dim values As List
    values.Initialize
    values.Add(100)
    values.Add(200)
    values.Add(120)
    DrawPie(values)
End Sub
There are two common ways to iterate over the list items:

1. For Each:
B4X:
For Each value As Float In Values
       'do something here
Next
2. For
B4X:
For i = 0 To Values.Size - 1 'first time index is always 0
        Dim value As Float = Values.Get(i)
        'do something here
Next
For Each is safer as you cannot get the index wrong. As a general rule you should use For Each unless you need to access the index.

If you are familiar with arrays, then a list is a dynamic array. It can grow and shrink as needed.
In B4X, arrays can be converted to lists automatically. This allows us to replace the above code with:
B4X:
DrawPie (Array(100, 200, 50, 80, 120))
Complete code:
B4X:
Sub Turtle_Start
    Turtle.SetSpeedFactor(3)
    Dim values As List
    values.Initialize
    values.Add(100)
    values.Add(200)
    values.Add(50)
    values.Add(80)
    values.Add(120)
    DrawPie(values)
End Sub

Sub DrawPie (Values As List)
    Dim radius As Float = 100
    Dim TotalValues As Float
    'For Each - best option when we don't need the index
    For Each value As Float In Values
        TotalValues = TotalValues + value
    Next
    Dim TotalAngles As Float
    'regular For Next
    For i = 0 To Values.Size - 1 'first time index is always 0
        Dim value As Float = Values.Get(i)
        Dim angle As Float = Floor(value / TotalValues * 360)
        If i = Values.Size - 1 Then
            'to avoid rounding errors the last element completes the circle.
            angle = Ceil(360 - TotalAngles)
        End If
        TotalAngles = TotalAngles + angle
        DrawSlice(angle, radius)
    Next
End Sub

Sub DrawSlice (angle As Float, radius As Float)
    Turtle.MoveForward(radius).TurnLeft(90)
    For a = 1 To angle
        Turtle.MoveForward(2 * cPI * radius / 360).TurnLeft(1)
    Next
    Turtle.TurnLeft(90).MoveForward(100).TurnLeft(180).PenUp
    Turtle.TurnRight(angle / 2).MoveForward(radius / 2).SetPenColor(Turtle.RandomColor).Fill
    Turtle.SetPenColor(xui.Color_Black).MoveBackward(radius / 2).TurnLeft(angle / 2).PenDown
End Sub
 
Status
Not open for further replies.
Top