module turtle
module lib/turtle.mu
import "turtle"
turtle provides simple Logo-style turtle graphics using SDL2.
The implementation intentionally uses only Mu's existing integer/pointer FFI surface. Heading math is integer CORDIC, so no f32/f64 FFI support is needed.
Imports
Functions
background#
Undocumented.
Source lib/turtle.mu:334
fn background(t, r, g, b) {
t["bg"] = [r, g, b]
_render(t)
return nil
}backward#
Undocumented.
Source lib/turtle.mu:297
fn backward(t, distance) {
return forward(t, -distance)
}clear#
clear removes all drawn segments without moving the turtle.
Source lib/turtle.mu:362
fn clear(t) {
t["segments"] = []
_render(t)
return nil
}close#
Undocumented.
Source lib/turtle.mu:399
fn close(t) {
if t["renderer"] != nil {
_call(t["api"], "SDL_DestroyRenderer", "void", ["ptr"], [t["renderer"]])
t["renderer"] = nil
}
if t["window"] != nil {
_call(t["api"], "SDL_DestroyWindow", "void", ["ptr"], [t["window"]])
t["window"] = nil
}
_call(t["api"], "SDL_Quit", "void", [], [])
t["closed"] = true
return nil
}done#
done keeps the window responsive until the user closes it.
Source lib/turtle.mu:389
fn done(t) {
while poll(t) {
// Redraw while idle so expose/resize events never leave a stale canvas.
_render(t)
_call(t["api"], "SDL_Delay", "void", ["u32"], [16])
}
close(t)
return nil
}forward#
forward moves the turtle by distance pixels, drawing when the pen is down.
Source lib/turtle.mu:270
fn forward(t, distance) {
if !poll(t) {
return false
}
old_x := t["x"]
old_y := t["y"]
next := _project(old_x, old_y, t["heading"], distance)
t["x"] = next[0]
t["y"] = next[1]
if t["pen"] {
c := t["color"]
append(t["segments"], [
_pixel(old_x), _pixel(old_y),
_pixel(next[0]), _pixel(next[1]),
c[0], c[1], c[2],
])
}
_render(t)
if t["delay"] > 0 {
_call(t["api"], "SDL_Delay", "void", ["u32"], [t["delay"]])
}
return true
}hide#
Undocumented.
Source lib/turtle.mu:346
fn hide(t) {
t["visible"] = false
_render(t)
return nil
}home#
home returns to the centre and points north, drawing the journey if needed.
Source lib/turtle.mu:369
fn home(t) {
old_heading := t["heading"]
target_x := (t["width"] / 2) << 16
target_y := (t["height"] / 2) << 16
if t["pen"] {
c := t["color"]
append(t["segments"], [
_pixel(t["x"]), _pixel(t["y"]),
_pixel(target_x), _pixel(target_y),
c[0], c[1], c[2],
])
}
t["x"] = target_x
t["y"] = target_y
t["heading"] = 0
_render(t)
return old_heading
}left#
Undocumented.
Source lib/turtle.mu:307
fn left(t, degrees) {
t["heading"] = math.normalise_degrees(t["heading"] - degrees)
_render(t)
return poll(t)
}open#
open creates a turtle canvas. The turtle starts in the centre, facing north.
Source lib/turtle.mu:192
fn open(width, height, title...) {
lib := _open_sdl()
if is_error(lib) {
return lib
}
api := _bind_api(lib)
if is_error(api) {
return api
}
rc := _call(api, "SDL_Init", "i32", ["u32"], [_SDL_INIT_VIDEO])
if rc != 0 {
return error("turtle: SDL_Init failed")
}
caption := "Mu Turtle"
if len(title) > 0 {
caption = title[0]
}
window := _call(
api,
"SDL_CreateWindow",
"ptr",
["cstr", "i32", "i32", "i32", "i32", "u32"],
[
caption,
_SDL_WINDOWPOS_CENTERED,
_SDL_WINDOWPOS_CENTERED,
width,
height,
0,
],
)
if window == nil {
_call(api, "SDL_Quit", "void", [], [])
return error("turtle: SDL_CreateWindow failed")
}
renderer := _call(
api,
"SDL_CreateRenderer",
"ptr",
["ptr", "i32", "u32"],
[window, -1, 0],
)
if renderer == nil {
_call(api, "SDL_DestroyWindow", "void", ["ptr"], [window])
_call(api, "SDL_Quit", "void", [], [])
return error("turtle: SDL_CreateRenderer failed")
}
t := {
"lib": lib,
"api": api,
"window": window,
"renderer": renderer,
"event": buf(_EVENT_SIZE),
"width": width,
"height": height,
"x": (width / 2) << 16,
"y": (height / 2) << 16,
"heading": 0,
"pen": true,
"color": [0, 0, 0],
"bg": [255, 255, 255],
"segments": [],
"visible": true,
"delay": 0,
"closed": false,
}
_render(t)
return t
}pen_color#
Undocumented.
Source lib/turtle.mu:329
fn pen_color(t, r, g, b) {
t["color"] = [r, g, b]
return nil
}pen_down#
Undocumented.
Source lib/turtle.mu:324
fn pen_down(t) {
t["pen"] = true
return nil
}pen_up#
Undocumented.
Source lib/turtle.mu:319
fn pen_up(t) {
t["pen"] = false
return nil
}poll#
poll processes pending window events and returns false once the window closes.
Source lib/turtle.mu:176
fn poll(t) {
if t["closed"] {
return false
}
event := t["event"]
while _call(t["api"], "SDL_PollEvent", "i32", ["ptr"], [event]) != 0 {
if _event_type(event) == _SDL_QUIT {
t["closed"] = true
return false
}
}
return true
}right#
Undocumented.
Source lib/turtle.mu:301
fn right(t, degrees) {
t["heading"] = math.normalise_degrees(t["heading"] + degrees)
_render(t)
return poll(t)
}set_heading#
Undocumented.
Source lib/turtle.mu:313
fn set_heading(t, degrees) {
t["heading"] = math.normalise_degrees(degrees)
_render(t)
return poll(t)
}show#
Undocumented.
Source lib/turtle.mu:340
fn show(t) {
t["visible"] = true
_render(t)
return nil
}speed#
speed sets a delay in milliseconds after each movement.
Source lib/turtle.mu:353
fn speed(t, milliseconds) {
if milliseconds < 0 {
milliseconds = 0
}
t["delay"] = milliseconds
return nil
}Internal helpers
Underscore-prefixed names are implementation detail. They are listed so the module's source reads without surprises, not as API — they may change at any time.
| _EVENT_SIZE | — |
| _Q16 | — |
| _SDL_INIT_VIDEO | — |
| _SDL_QUIT | — |
| _SDL_WINDOWPOS_CENTERED | — |
| _bind(lib, name) | — |
| _bind_api(lib) | — |
| _call(api, name, ret, arg_types, args) | — |
| _draw_turtle(t) | — |
| _event_type(event) | — |
| _line(t, x1, y1, x2, y2) | — |
| _open_sdl() | — |
| _pixel(q16) | — |
| _project(x, y, heading, distance) | — |
| _render(t) | — |
| _sdl_paths() | — |
| _set_color(t, r, g, b, a) | — |