Merge branch 'worktree-agent-a788866b56165817f' into dev-loop

This commit is contained in:
Joseph Ferano 2026-09-13 18:01:35 +07:00
commit 91ec235395
31 changed files with 906 additions and 698 deletions

View File

@ -5292,3 +5292,62 @@ route neither covers, and says so rather than claiming to be a design.
`test/programs/generics.flan` runs one written body at two key types; `test/programs/generic-map-reject.flan` is
the other half, a call site at `f64` refused against the clause.
## The begin/end pairs are macros now, and the pair is all they can promise
`vendor/raylib/modes.flan` — a second file in the raylib package with no `declare-c` in it, split out on
`vector.flan`'s reasoning exactly: `raylib.flan` is the package's statement about C and the file the header check
reads signatures out of, and nothing here can be made wrong by raylib changing because nothing here names C.
Five macros, one per pair the package binds: `with-drawing`, `with-mode-2d`, `with-mode-3d`, `with-texture-mode`,
`with-scissor-mode`. Each expands to `(do (begin-… args) body… (end-…))` — the calls the author used to type, in the
order they used to type them, with the argument evaluated once where it always was. No `let` and no `gensym`
anywhere: nothing binds a name, so there is no name a caller's could collide with, and introducing one in a
deliberately non-hygienic macro that does not need it is a step backwards. NEXT.md called these "three-line macros
once the expander lands" when it rejected `unwind-protect`, and they are.
They arrive qualified, the rule every declaration in a package follows: the importer writes `(rl/with-drawing …)`
and a bare `(with-drawing …)` is an unknown name. The expansions name `begin-drawing` and friends unqualified and
the expander qualifies them on the way out — `test/programs/pkgs/mac/mac.flan` is that rule's worked example, and
these are its first non-test use.
**What the macro removes is not what a reader assumes it removes.** It removes *the `End*` is missing, or is the
wrong one, or drifted away from its `Begin*` during an edit* — which is the whole of the class that actually bites,
and `EndMode2D` closing a `BeginMode3D` type-checks fine and corrupts the matrix stack. It does **not** remove a
body that leaves through the unwind path: a `return`, or an `invoke-restart` reaching an enclosing `restart-case`,
skips the rest of the `do` and the `End*` with it, exactly as it skipped a hand-written one.
`defer` is the obvious fix and is not available. It is a compile-time construct — the forms are copied into the
function's exit paths — so it is refused inside a loop body, and a begin/end pair lives inside the game loop
essentially always. The refusal was checked rather than assumed:
```
defer is not allowed inside a loop body — a defer is copied into every exit path of the function, so it
always registers and always runs at function exit.
```
Were it permitted there it would be wrong in the worse direction: one `EndDrawing` at function exit for N
`BeginDrawing`s, which is the silent-wrongness class the refusal exists for. A macro expanding to a `defer` would
therefore compile at a function body's top level and be refused in the one place anybody writes it. So the
discipline `sand.flan` already wrote down stays the discipline — **keep the restart boundary outside the pair**, so
choosing `continue` for a frame abandons the update and still reaches the drawing. spec-conditions §5 is the rule
behind that: a transfer runs the intervening frames' `defer`s and moves control, and there are none here to run.
`sand.flan`'s comment says so at the loop, and the macro's header says so at the definition.
**35 call sites converted** across `examples/` and `sand.flan`: 26 `with-drawing`, 5 `with-texture-mode`, 3
`with-mode-3d`, 1 `with-mode-2d`. Every begin/end pair in the corpus except one — `examples/core-scissor-test.flan`
turns its scissor on and off from a flag, so its `Begin` and its `End` sit in two separate `when`s with the drawing
between them and there is no body to wrap. That site is left hand-written, and it is the honest illustration of the
macro's limit rather than an oversight: a conditional pair is a shape a bracketing macro cannot express without
duplicating the body.
`sand.flan` converted its draw pair and stays at parity — `lisp/sand.lisp` writes `rl:with-drawing` there, which is
what makes it a port of the reference version rather than an addition to it. Its header note "no user-written
macros" now reads "no macros of its own", because `rl/with-drawing` is the binding package's, as in the Lisp.
`with-scissor-mode` is the one no example exercises, so `test/programs/rl-with.flan` does: all five expanded in a
`frame` function deliberately **unreachable from `main`**, which is what lets the case run with no libraylib at all
`reach.ml` answers the link from the checked program, and `check` still walks every line. Nothing there draws,
because nothing headless can. `rl-with-reject.flan` is the arity half: a macro cannot signal while it expands, so
the prelude's idiom is to answer a symbol that is not a name and reads as the sentence the caller needs
(`with-mode-2d-takes-a-camera-and-a-body`), with the expander's note beneath it naming the macro at the call site.

View File

@ -99,44 +99,39 @@
(set (.rotation camera) 0.0))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
;; Everything between these two is in world space and goes through the
;; camera. The ground and the crosshair lines are drawn far outside the
;; window on purpose: at zoom 0.1 they still have to reach the edges.
(rl/begin-mode-2d camera)
;; Everything inside with-mode-2d is in world space and goes through
;; the camera. The ground and the crosshair lines are drawn far outside
;; the window on purpose: at zoom 0.1 they still have to reach the edges.
(rl/with-mode-2d camera
(rl/draw-rectangle -6000 320 13000 8000 rl/darkgray)
(rl/draw-rectangle -6000 320 13000 8000 rl/darkgray)
(dotimes [i max-buildings]
(rl/draw-rectangle-rec (at buildings i) (at build-colors i)))
(dotimes [i max-buildings]
(rl/draw-rectangle-rec (at buildings i) (at build-colors i)))
(rl/draw-rectangle-rec player rl/red)
(rl/draw-rectangle-rec player rl/red)
(rl/draw-line (i32 (.x (.target camera))) (* screen-height -10)
(i32 (.x (.target camera))) (* screen-height 10) rl/green)
(rl/draw-line (* screen-width -10) (i32 (.y (.target camera)))
(* screen-width 10) (i32 (.y (.target camera))) rl/green))
(rl/draw-line (i32 (.x (.target camera))) (* screen-height -10)
(i32 (.x (.target camera))) (* screen-height 10) rl/green)
(rl/draw-line (* screen-width -10) (i32 (.y (.target camera)))
(* screen-width 10) (i32 (.y (.target camera))) rl/green)
;; And everything after it is in screen space again — the frame drawn
;; around the window is how the example shows the difference.
(rl/draw-text "SCREEN AREA" 640 10 20 rl/red)
(rl/end-mode-2d)
(rl/draw-rectangle 0 0 screen-width 5 rl/red)
(rl/draw-rectangle 0 5 5 (- screen-height 10) rl/red)
(rl/draw-rectangle (- screen-width 5) 5 5 (- screen-height 10) rl/red)
(rl/draw-rectangle 0 (- screen-height 5) screen-width 5 rl/red)
;; And everything after it is in screen space again — the frame drawn
;; around the window is how the example shows the difference.
(rl/draw-text "SCREEN AREA" 640 10 20 rl/red)
(rl/draw-rectangle 10 10 250 113 (rl/fade rl/skyblue 0.5))
(rl/draw-rectangle-lines 10 10 250 113 rl/blue)
(rl/draw-rectangle 0 0 screen-width 5 rl/red)
(rl/draw-rectangle 0 5 5 (- screen-height 10) rl/red)
(rl/draw-rectangle (- screen-width 5) 5 5 (- screen-height 10) rl/red)
(rl/draw-rectangle 0 (- screen-height 5) screen-width 5 rl/red)
(rl/draw-rectangle 10 10 250 113 (rl/fade rl/skyblue 0.5))
(rl/draw-rectangle-lines 10 10 250 113 rl/blue)
(rl/draw-text "Free 2d camera controls:" 20 20 10 rl/black)
(rl/draw-text "- Right/Left to move Offset" 40 40 10 rl/darkgray)
(rl/draw-text "- Mouse Wheel to Zoom in-out" 40 60 10 rl/darkgray)
(rl/draw-text "- A / S to Rotate" 40 80 10 rl/darkgray)
(rl/draw-text "- R to reset Zoom and Rotation" 40 100 10 rl/darkgray)
(rl/end-drawing)))
(rl/draw-text "Free 2d camera controls:" 20 20 10 rl/black)
(rl/draw-text "- Right/Left to move Offset" 40 40 10 rl/darkgray)
(rl/draw-text "- Mouse Wheel to Zoom in-out" 40 60 10 rl/darkgray)
(rl/draw-text "- A / S to Rotate" 40 80 10 rl/darkgray)
(rl/draw-text "- R to reset Zoom and Rotation" 40 100 10 rl/darkgray))))

View File

@ -113,46 +113,41 @@
(set (.hit collision) false)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/begin-mode-3d camera)
(rl/with-mode-3d camera
(if (.hit collision)
(do
(rl/draw-cube cube-position (.x cube-size) (.y cube-size) (.z cube-size)
rl/red)
(rl/draw-cube-wires cube-position (.x cube-size) (.y cube-size)
(.z cube-size) rl/maroon)
;; A slightly larger wireframe around the selection, which is the
;; whole visual feedback of the example.
(rl/draw-cube-wires cube-position (+ (.x cube-size) 0.2)
(+ (.y cube-size) 0.2) (+ (.z cube-size) 0.2)
rl/green))
(do
(rl/draw-cube cube-position (.x cube-size) (.y cube-size) (.z cube-size)
rl/gray)
(rl/draw-cube-wires cube-position (.x cube-size) (.y cube-size)
(.z cube-size) rl/darkgray)))
(if (.hit collision)
(do
(rl/draw-cube cube-position (.x cube-size) (.y cube-size) (.z cube-size)
rl/red)
(rl/draw-cube-wires cube-position (.x cube-size) (.y cube-size)
(.z cube-size) rl/maroon)
;; A slightly larger wireframe around the selection, which is the
;; whole visual feedback of the example.
(rl/draw-cube-wires cube-position (+ (.x cube-size) 0.2)
(+ (.y cube-size) 0.2) (+ (.z cube-size) 0.2)
rl/green))
(do
(rl/draw-cube cube-position (.x cube-size) (.y cube-size) (.z cube-size)
rl/gray)
(rl/draw-cube-wires cube-position (.x cube-size) (.y cube-size)
(.z cube-size) rl/darkgray)))
;; raylib draws the ray a thousand units long, so it reads as a line
;; crossing the scene rather than as a segment with an end.
(rl/draw-ray ray rl/maroon)
(rl/draw-grid 10 1.0))
;; raylib draws the ray a thousand units long, so it reads as a line
;; crossing the scene rather than as a segment with an end.
(rl/draw-ray ray rl/maroon)
(rl/draw-grid 10 1.0)
(rl/draw-text "Try clicking on the box with your mouse!" 240 10 20
rl/darkgray)
(rl/end-mode-3d)
(when (.hit collision)
(rl/draw-text "BOX SELECTED"
(/ (- screen-width (rl/measure-text "BOX SELECTED" 30)) 2)
(i32 (* (f32 screen-height) 0.1)) 30 rl/green))
(rl/draw-text "Try clicking on the box with your mouse!" 240 10 20
rl/darkgray)
(rl/draw-text "Right click mouse to toggle camera controls" 10 430 10
rl/gray)
(when (.hit collision)
(rl/draw-text "BOX SELECTED"
(/ (- screen-width (rl/measure-text "BOX SELECTED" 30)) 2)
(i32 (* (f32 screen-height) 0.1)) 30 rl/green))
(rl/draw-text "Right click mouse to toggle camera controls" 10 430 10
rl/gray)
(rl/draw-fps 10 10)
(rl/end-drawing)))
(rl/draw-fps 10 10))))

View File

@ -25,8 +25,7 @@
(rl/set-target-fps 60)
(until (rl/window-should-close?)
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/draw-text "Congrats! You created your first window!" 190 200 20
rl/lightgray)
(rl/end-drawing)))
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-text "Congrats! You created your first window!" 190 200 20
rl/lightgray))))

View File

@ -70,50 +70,48 @@
(set (.x frame-circle) 0.0))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-circle-v delta-circle circle-radius rl/red)
(rl/draw-circle-v frame-circle circle-radius rl/blue)
(rl/draw-circle-v delta-circle circle-radius rl/red)
(rl/draw-circle-v frame-circle circle-radius rl/blue)
;; "FPS: unlimited (%i)" when the target is 0, "FPS: %i (target: %i)"
;; otherwise — the C's own branch, reassembled out of literals and
;; draw-int. `x` walks along the line as each piece is drawn, which is what
;; the returned width from draw-int is for.
(if (<= current-fps 0)
;; "FPS: unlimited (%i)" when the target is 0, "FPS: %i (target: %i)"
;; otherwise — the C's own branch, reassembled out of literals and
;; draw-int. `x` walks along the line as each piece is drawn, which is what
;; the returned width from draw-int is for.
(if (<= current-fps 0)
(let [x 10]
(rl/draw-text "FPS: unlimited (" x 10 20 rl/darkgray)
(set x (+ x (rl/measure-text "FPS: unlimited (" 20)))
(set x (+ x (d/draw-int (rl/get-fps) x 10 20 rl/darkgray)))
(rl/draw-text ")" x 10 20 rl/darkgray))
(let [x 10]
(rl/draw-text "FPS: " x 10 20 rl/darkgray)
(set x (+ x (rl/measure-text "FPS: " 20)))
(set x (+ x (d/draw-int (rl/get-fps) x 10 20 rl/darkgray)))
(rl/draw-text " (target: " x 10 20 rl/darkgray)
(set x (+ x (rl/measure-text " (target: " 20)))
(set x (+ x (d/draw-int current-fps x 10 20 rl/darkgray)))
(rl/draw-text ")" x 10 20 rl/darkgray)))
;; The C's line, kept exactly — seconds, labelled ms.
(let [x 10]
(rl/draw-text "FPS: unlimited (" x 10 20 rl/darkgray)
(set x (+ x (rl/measure-text "FPS: unlimited (" 20)))
(set x (+ x (d/draw-int (rl/get-fps) x 10 20 rl/darkgray)))
(rl/draw-text ")" x 10 20 rl/darkgray))
(rl/draw-text "Frame time: " x 30 20 rl/darkgray)
(set x (+ x (rl/measure-text "Frame time: " 20)))
(set x (+ x (d/draw-f32 (rl/get-frame-time) 2 x 30 20 rl/darkgray)))
(rl/draw-text " ms" x 30 20 rl/darkgray))
;; And the same number in the unit the label claims, so the file does not
;; have to be read to notice.
(let [x 10]
(rl/draw-text "FPS: " x 10 20 rl/darkgray)
(set x (+ x (rl/measure-text "FPS: " 20)))
(set x (+ x (d/draw-int (rl/get-fps) x 10 20 rl/darkgray)))
(rl/draw-text " (target: " x 10 20 rl/darkgray)
(set x (+ x (rl/measure-text " (target: " 20)))
(set x (+ x (d/draw-int current-fps x 10 20 rl/darkgray)))
(rl/draw-text ")" x 10 20 rl/darkgray)))
(rl/draw-text "(really " x 50 20 rl/lightgray)
(set x (+ x (rl/measure-text "(really " 20)))
(set x (+ x (d/draw-f32 (* (rl/get-frame-time) 1000.0) 2 x 50 20
rl/lightgray)))
(rl/draw-text " ms)" x 50 20 rl/lightgray))
;; The C's line, kept exactly — seconds, labelled ms.
(let [x 10]
(rl/draw-text "Frame time: " x 30 20 rl/darkgray)
(set x (+ x (rl/measure-text "Frame time: " 20)))
(set x (+ x (d/draw-f32 (rl/get-frame-time) 2 x 30 20 rl/darkgray)))
(rl/draw-text " ms" x 30 20 rl/darkgray))
;; And the same number in the unit the label claims, so the file does not
;; have to be read to notice.
(let [x 10]
(rl/draw-text "(really " x 50 20 rl/lightgray)
(set x (+ x (rl/measure-text "(really " 20)))
(set x (+ x (d/draw-f32 (* (rl/get-frame-time) 1000.0) 2 x 50 20
rl/lightgray)))
(rl/draw-text " ms)" x 50 20 rl/lightgray))
(rl/draw-text "Use the scroll wheel to change the fps limit, r to reset"
10 70 20 rl/darkgray)
(rl/draw-text "Use the scroll wheel to change the fps limit, r to reset"
10 70 20 rl/darkgray)
(rl/draw-text "FUNC: x += GetFrameTime()*speed" 10 110 20 rl/red)
(rl/draw-text "FUNC: x += speed" 10 260 20 rl/blue)
(rl/end-drawing)))
(rl/draw-text "FUNC: x += GetFrameTime()*speed" 10 110 20 rl/red)
(rl/draw-text "FUNC: x += speed" 10 260 20 rl/blue))))

View File

@ -183,98 +183,96 @@
.width 75.0 .height 24.0})]
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(if (rl/gamepad-available? gamepad)
(do
;; The C draws `TextFormat("GP%d: %s", gamepad, GetGamepadName(...))`.
;; The name is unbindable — see the header — so this is the index and
;; the word the C would have followed it with.
(rl/draw-text "GP" 10 10 10 rl/black)
(let [x (+ 10 (rl/measure-text "GP" 10))]
(rl/draw-text ": CONNECTED (name unbindable)"
(+ x (d/draw-int gamepad x 10 10 rl/black))
10 10 rl/black))
(if (rl/gamepad-available? gamepad)
(do
;; The C draws `TextFormat("GP%d: %s", gamepad, GetGamepadName(...))`.
;; The name is unbindable — see the header — so this is the index and
;; the word the C would have followed it with.
(rl/draw-text "GP" 10 10 10 rl/black)
(let [x (+ 10 (rl/measure-text "GP" 10))]
(rl/draw-text ": CONNECTED (name unbindable)"
(+ x (d/draw-int gamepad x 10 10 rl/black))
10 10 rl/black))
(let [lx (deadzone (rl/get-gamepad-axis-movement gamepad :left-x)
stick-deadzone)
ly (deadzone (rl/get-gamepad-axis-movement gamepad :left-y)
stick-deadzone)
rx (deadzone (rl/get-gamepad-axis-movement gamepad :right-x)
stick-deadzone)
ry (deadzone (rl/get-gamepad-axis-movement gamepad :right-y)
stick-deadzone)
lt (trigger-deadzoned
(rl/get-gamepad-axis-movement gamepad :left-trigger))
rt (trigger-deadzoned
(rl/get-gamepad-axis-movement gamepad :right-trigger))]
(let [lx (deadzone (rl/get-gamepad-axis-movement gamepad :left-x)
stick-deadzone)
ly (deadzone (rl/get-gamepad-axis-movement gamepad :left-y)
stick-deadzone)
rx (deadzone (rl/get-gamepad-axis-movement gamepad :right-x)
stick-deadzone)
ry (deadzone (rl/get-gamepad-axis-movement gamepad :right-y)
stick-deadzone)
lt (trigger-deadzoned
(rl/get-gamepad-axis-movement gamepad :left-trigger))
rt (trigger-deadzoned
(rl/get-gamepad-axis-movement gamepad :right-trigger))]
(draw-pad-background)
(draw-pad-buttons)
(draw-pad-background)
(draw-pad-buttons)
(draw-stick 345 260 lx ly
(rl/gamepad-button-down? gamepad :left-thumb))
(draw-stick 465 260 rx ry
(rl/gamepad-button-down? gamepad :right-thumb))
(draw-stick 345 260 lx ly
(rl/gamepad-button-down? gamepad :left-thumb))
(draw-stick 465 260 rx ry
(rl/gamepad-button-down? gamepad :right-thumb))
;; The triggers as bars filling upward from a grey track. The +1
;; and the halving turn raylib's [-1, 1] into [0, 1].
(rl/draw-rectangle 151 110 15 70 rl/gray)
(rl/draw-rectangle 644 110 15 70 rl/gray)
(rl/draw-rectangle 151 110 15 (i32 (* (/ (+ 1.0 lt) 2.0) 70.0))
rl/red)
(rl/draw-rectangle 644 110 15 (i32 (* (/ (+ 1.0 rt) 2.0) 70.0))
rl/red))
;; The triggers as bars filling upward from a grey track. The +1
;; and the halving turn raylib's [-1, 1] into [0, 1].
(rl/draw-rectangle 151 110 15 70 rl/gray)
(rl/draw-rectangle 644 110 15 70 rl/gray)
(rl/draw-rectangle 151 110 15 (i32 (* (/ (+ 1.0 lt) 2.0) 70.0))
rl/red)
(rl/draw-rectangle 644 110 15 (i32 (* (/ (+ 1.0 rt) 2.0) 70.0))
rl/red))
(rl/draw-text "DETECTED AXIS [" 10 50 10 rl/maroon)
(let [x (+ 10 (rl/measure-text "DETECTED AXIS [" 10))]
(rl/draw-text "]:" (+ x (d/draw-int axis-count x 50 10 rl/maroon))
50 10 rl/maroon))
(rl/draw-text "DETECTED AXIS [" 10 50 10 rl/maroon)
(let [x (+ 10 (rl/measure-text "DETECTED AXIS [" 10))]
(rl/draw-text "]:" (+ x (d/draw-int axis-count x 50 10 rl/maroon))
50 10 rl/maroon))
(dotimes [i axis-count]
(rl/draw-text "AXIS " 20 (+ 70 (* 20 i)) 10 rl/darkgray)
(let [x (+ 20 (rl/measure-text "AXIS " 10))
y (+ 70 (* 20 i))]
(set x (+ x (d/draw-int i x y 10 rl/darkgray)))
(rl/draw-text ": " x y 10 rl/darkgray)
(set x (+ x (rl/measure-text ": " 10)))
;; The raw reading, not the deadzoned one — the C shows the raw
;; value here too, which is what makes the deadzone visible as a
;; difference between this row and the stick.
(d/draw-f32 (axis-at gamepad i) 2 x y 10
rl/darkgray)))
(dotimes [i axis-count]
(rl/draw-text "AXIS " 20 (+ 70 (* 20 i)) 10 rl/darkgray)
(let [x (+ 20 (rl/measure-text "AXIS " 10))
y (+ 70 (* 20 i))]
(set x (+ x (d/draw-int i x y 10 rl/darkgray)))
(rl/draw-text ": " x y 10 rl/darkgray)
(set x (+ x (rl/measure-text ": " 10)))
;; The raw reading, not the deadzoned one — the C shows the raw
;; value here too, which is what makes the deadzone visible as a
;; difference between this row and the stick.
(d/draw-f32 (axis-at gamepad i) 2 x y 10
rl/darkgray)))
;; Drawn and inert. See the header: SetGamepadVibration is a stub in
;; this raylib and is not bound.
(rl/draw-rectangle-rec vibrate-rect rl/skyblue)
(rl/draw-text "VIBRATE" (+ (i32 (.x vibrate-rect)) 14)
(+ (i32 (.y vibrate-rect)) 1) 10 rl/darkgray)
(rl/draw-text "(not bound: raylib stub)" 95
(+ (i32 (.y vibrate-rect)) 7) 10 rl/gray)
;; Drawn and inert. See the header: SetGamepadVibration is a stub in
;; this raylib and is not bound.
(rl/draw-rectangle-rec vibrate-rect rl/skyblue)
(rl/draw-text "VIBRATE" (+ (i32 (.x vibrate-rect)) 14)
(+ (i32 (.y vibrate-rect)) 1) 10 rl/darkgray)
(rl/draw-text "(not bound: raylib stub)" 95
(+ (i32 (.y vibrate-rect)) 7) 10 rl/gray)
;; -1 when nothing is pressed, which is why the binding answers an
;; i32 and not a GamepadButton.
;; draw-int answers the width it drew, so a branch that ends in one
;; has type i32 while its sibling has type () and the `if` will not
;; typecheck — "expected i32, found ()". Both arms end in a
;; draw-text here, which is the tidy way out; where that is awkward a
;; trailing `(do)` is the other.
(let [b (rl/get-gamepad-button-pressed)]
(if (>= b 0)
(do (rl/draw-text "DETECTED BUTTON: " 10 430 10 rl/red)
(d/draw-int b (+ 10 (rl/measure-text "DETECTED BUTTON: " 10))
430 10 rl/red)
(do))
(rl/draw-text "DETECTED BUTTON: NONE" 10 430 10 rl/gray))))
;; -1 when nothing is pressed, which is why the binding answers an
;; i32 and not a GamepadButton.
;; draw-int answers the width it drew, so a branch that ends in one
;; has type i32 while its sibling has type () and the `if` will not
;; typecheck — "expected i32, found ()". Both arms end in a
;; draw-text here, which is the tidy way out; where that is awkward a
;; trailing `(do)` is the other.
(let [b (rl/get-gamepad-button-pressed)]
(if (>= b 0)
(do (rl/draw-text "DETECTED BUTTON: " 10 430 10 rl/red)
(d/draw-int b (+ 10 (rl/measure-text "DETECTED BUTTON: " 10))
430 10 rl/red)
(do))
(rl/draw-text "DETECTED BUTTON: NONE" 10 430 10 rl/gray))))
(do
(rl/draw-text "GP" 10 10 10 rl/gray)
(let [x (+ 10 (rl/measure-text "GP" 10))]
(rl/draw-text ": NOT DETECTED"
(+ x (d/draw-int gamepad x 10 10 rl/gray))
10 10 rl/gray))
(rl/draw-text "left/right arrows select another pad" 10 30 10
rl/lightgray)))
(rl/end-drawing))))
(do
(rl/draw-text "GP" 10 10 10 rl/gray)
(let [x (+ 10 (rl/measure-text "GP" 10))]
(rl/draw-text ": NOT DETECTED"
(+ x (d/draw-int gamepad x 10 10 rl/gray))
10 10 rl/gray))
(rl/draw-text "left/right arrows select another pad" 10 30 10
rl/lightgray)))))))

View File

@ -328,37 +328,35 @@
(set (at touch-positions i) (rl/get-touch-position i)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-text "*" 165 12 10 rl/black)
(rl/draw-text "Example optimized for Web/HTML5\non Smartphones with Touch Screen."
175 12 10 rl/black)
(rl/draw-text "*" 165 42 10 rl/black)
(rl/draw-text "While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation."
175 42 10 rl/black)
(rl/draw-text "*" 165 12 10 rl/black)
(rl/draw-text "Example optimized for Web/HTML5\non Smartphones with Touch Screen."
175 12 10 rl/black)
(rl/draw-text "*" 165 42 10 rl/black)
(rl/draw-text "While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation."
175 42 10 rl/black)
(draw-last-gesture touch-count)
(draw-log)
(draw-protractor)
(draw-last-gesture touch-count)
(draw-log)
(draw-protractor)
;; The pointer itself: every live touch, or the mouse when there is no
;; touchscreen. The halo is the gesture colour faded, which is what
;; `fade` was bound for.
(unless (= g :none)
(if (> touch-count 0)
(do
(dotimes [i touch-count]
(let [p (at touch-positions i)]
(rl/draw-circle-v p 50.0 (rl/fade gesture-color 0.5))
(rl/draw-circle-v p 5.0 gesture-color)))
;; Two fingers: the line between them, thinner while pinching
;; out, which is the C's only use of the raw 512.
(when (= touch-count 2)
(rl/draw-line-ex (at touch-positions 0) (at touch-positions 1)
(if (= g :pinch-out) 8.0 12.0) gesture-color)))
(let [m (rl/get-mouse-position)]
(rl/draw-circle-v m 35.0 (rl/fade gesture-color 0.5))
(rl/draw-circle-v m 5.0 gesture-color))))
(rl/end-drawing)))))
;; The pointer itself: every live touch, or the mouse when there is no
;; touchscreen. The halo is the gesture colour faded, which is what
;; `fade` was bound for.
(unless (= g :none)
(if (> touch-count 0)
(do
(dotimes [i touch-count]
(let [p (at touch-positions i)]
(rl/draw-circle-v p 50.0 (rl/fade gesture-color 0.5))
(rl/draw-circle-v p 5.0 gesture-color)))
;; Two fingers: the line between them, thinner while pinching
;; out, which is the C's only use of the raw 512.
(when (= touch-count 2)
(rl/draw-line-ex (at touch-positions 0) (at touch-positions 1)
(if (= g :pinch-out) 8.0 12.0) gesture-color)))
(let [m (rl/get-mouse-position)]
(rl/draw-circle-v m 35.0 (rl/fade gesture-color 0.5))
(rl/draw-circle-v m 5.0 gesture-color)))))))))

View File

@ -80,31 +80,29 @@
(set gestures-count 0)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-rectangle-rec touch-area rl/gray)
(rl/draw-rectangle 225 15 (- screen-width 240) (- screen-height 30)
rl/raywhite)
(rl/draw-rectangle-rec touch-area rl/gray)
(rl/draw-rectangle 225 15 (- screen-width 240) (- screen-height 30)
rl/raywhite)
(rl/draw-text "GESTURES TEST AREA" (- screen-width 270)
(- screen-height 40) 20 (rl/fade rl/gray 0.5))
(rl/draw-text "GESTURES TEST AREA" (- screen-width 270)
(- screen-height 40) 20 (rl/fade rl/gray 0.5))
(dotimes [i gestures-count]
(if (= 0 (% i 2))
(rl/draw-rectangle 10 (+ 30 (* 20 i)) 200 20
(rl/fade rl/lightgray 0.5))
(rl/draw-rectangle 10 (+ 30 (* 20 i)) 200 20
(rl/fade rl/lightgray 0.3)))
;; The newest entry in maroon and the rest in dark grey, as the C
;; has it.
(rl/draw-text (at gesture-log i) 35 (+ 36 (* 20 i)) 10
(if (< i (- gestures-count 1)) rl/darkgray rl/maroon)))
(dotimes [i gestures-count]
(if (= 0 (% i 2))
(rl/draw-rectangle 10 (+ 30 (* 20 i)) 200 20
(rl/fade rl/lightgray 0.5))
(rl/draw-rectangle 10 (+ 30 (* 20 i)) 200 20
(rl/fade rl/lightgray 0.3)))
;; The newest entry in maroon and the rest in dark grey, as the C
;; has it.
(rl/draw-text (at gesture-log i) 35 (+ 36 (* 20 i)) 10
(if (< i (- gestures-count 1)) rl/darkgray rl/maroon)))
(rl/draw-rectangle-lines 10 29 200 (- screen-height 50) rl/gray)
(rl/draw-text "DETECTED GESTURES" 50 15 10 rl/gray)
(rl/draw-rectangle-lines 10 29 200 (- screen-height 50) rl/gray)
(rl/draw-text "DETECTED GESTURES" 50 15 10 rl/gray)
(unless (= current-gesture :none)
(rl/draw-circle-v touch 30.0 rl/maroon))
(rl/end-drawing)))))
(unless (= current-gesture :none)
(rl/draw-circle-v touch 30.0 rl/maroon)))))))

View File

@ -40,8 +40,7 @@
(when (rl/key-down? :down) (set (.y ball) (+ (.y ball) 2.0)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/draw-text "move the ball with arrow keys" 10 10 20 rl/darkgray)
(rl/draw-circle-v ball 50.0 rl/maroon)
(rl/end-drawing)))
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-text "move the ball with arrow keys" 10 10 20 rl/darkgray)
(rl/draw-circle-v ball 50.0 rl/maroon))))

View File

@ -42,14 +42,13 @@
(set box-y (- box-y (i32 (* (rl/get-mouse-wheel-move) (f32 scroll-speed)))))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/draw-rectangle (- (/ screen-width 2) 40) box-y 80 80 rl/maroon)
(rl/draw-text "Use mouse wheel to move the cube up and down!" 10 10 20
rl/gray)
;; The two halves of the C's one TextFormat call: the literal, measured,
;; and then the number starting where it ended.
(rl/draw-text "Box position Y: " 10 40 20 rl/lightgray)
(d/draw-int-padded box-y 3 (+ 10 (rl/measure-text "Box position Y: " 20))
40 20 rl/lightgray)
(rl/end-drawing)))
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-rectangle (- (/ screen-width 2) 40) box-y 80 80 rl/maroon)
(rl/draw-text "Use mouse wheel to move the cube up and down!" 10 10 20
rl/gray)
;; The two halves of the C's one TextFormat call: the literal, measured,
;; and then the number starting where it ended.
(rl/draw-text "Box position Y: " 10 40 20 rl/lightgray)
(d/draw-int-padded box-y 3 (+ 10 (rl/measure-text "Box position Y: " 20))
40 20 rl/lightgray))))

View File

@ -49,13 +49,12 @@
:else ball-color))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/draw-circle-v ball 40.0 ball-color)
(rl/draw-text "move ball with mouse and click mouse button to change color"
10 10 20 rl/darkgray)
(rl/draw-text "Press 'H' to toggle cursor visibility" 10 30 20 rl/darkgray)
(if (rl/cursor-hidden?)
(rl/draw-text "CURSOR HIDDEN" 20 60 20 rl/red)
(rl/draw-text "CURSOR VISIBLE" 20 60 20 rl/lime))
(rl/end-drawing))))
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-circle-v ball 40.0 ball-color)
(rl/draw-text "move ball with mouse and click mouse button to change color"
10 10 20 rl/darkgray)
(rl/draw-text "Press 'H' to toggle cursor visibility" 10 30 20 rl/darkgray)
(if (rl/cursor-hidden?)
(rl/draw-text "CURSOR HIDDEN" 20 60 20 rl/red)
(rl/draw-text "CURSOR VISIBLE" 20 60 20 rl/lime))))))

View File

@ -45,26 +45,24 @@
(set (at touch-positions i) (rl/get-touch-position i)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(dotimes [i count]
(let [p (at touch-positions i)]
;; raylib reports (0,0) for a slot that is not being touched, so the
;; C filters on it and so does this. It means a real touch in the
;; very top-left corner is dropped; that is raylib's ambiguity, not
;; something the port introduced.
(when (and (> (.x p) 0.0) (> (.y p) 0.0))
(rl/draw-circle-v p 34.0 rl/orange)
;; The C's TextFormat("%d", i). This used to need examples/digits.flan
;; and a table of one-character strings; (string b) makes the
;; number a string with no instructions, so it is one draw-text and
;; this file imports nothing but raylib.
(rl/draw-text (string (i64->bytes (i64 i)))
(- (i32 (.x p)) 10) (- (i32 (.y p)) 70) 40
rl/black))))
(dotimes [i count]
(let [p (at touch-positions i)]
;; raylib reports (0,0) for a slot that is not being touched, so the
;; C filters on it and so does this. It means a real touch in the
;; very top-left corner is dropped; that is raylib's ambiguity, not
;; something the port introduced.
(when (and (> (.x p) 0.0) (> (.y p) 0.0))
(rl/draw-circle-v p 34.0 rl/orange)
;; The C's TextFormat("%d", i). This used to need examples/digits.flan
;; and a table of one-character strings; (string b) makes the
;; number a string with no instructions, so it is one draw-text and
;; this file imports nothing but raylib.
(rl/draw-text (string (i64->bytes (i64 i)))
(- (i32 (.x p)) 10) (- (i32 (.y p)) 70) 40
rl/black))))
(rl/draw-text "touch the screen at multiple locations to get multiple balls"
10 10 20 rl/darkgray)
(rl/end-drawing))))
(rl/draw-text "touch the screen at multiple locations to get multiple balls"
10 10 20 rl/darkgray)))))

View File

@ -172,24 +172,22 @@
(move-player pressed (rl/get-frame-time))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-circle-v player 50.0 rl/maroon)
(rl/draw-circle-v player 50.0 rl/maroon)
(dotimes [i button-max]
(rl/draw-circle-v (at button-positions i) button-radius
(if (= i pressed) rl/darkgray rl/black))
(let [t (at arrow-tris i)]
(rl/draw-triangle (at t 0) (at t 1) (at t 2)
(at label-colors i))))
(dotimes [i button-max]
(rl/draw-circle-v (at button-positions i) button-radius
(if (= i pressed) rl/darkgray rl/black))
(let [t (at arrow-tris i)]
(rl/draw-triangle (at t 0) (at t 1) (at t 2)
(at label-colors i))))
(rl/draw-text "move the player with D-Pad buttons" 10 10 20 rl/darkgray)
;; Not in the C: which button the search picked, as a number, so the
;; headless case and the window agree about the same thing.
(rl/draw-text "button: " 10 34 20 rl/lightgray)
(rl/draw-text (string (i64->bytes (i64 pressed)))
(+ 10 (rl/measure-text "button: " 20)) 34 20
rl/lightgray)
(rl/end-drawing))))
(rl/draw-text "move the player with D-Pad buttons" 10 10 20 rl/darkgray)
;; Not in the C: which button the search picked, as a number, so the
;; headless case and the window agree about the same thing.
(rl/draw-text "button: " 10 34 20 rl/lightgray)
(rl/draw-text (string (i64->bytes (i64 pressed)))
(+ 10 (rl/measure-text "button: " 20)) 34 20
rl/lightgray)))))

View File

@ -51,22 +51,20 @@
(set (.y scissor) (- (f32 (rl/get-mouse-y)) (/ (.height scissor) 2.0)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(when scissor-mode
(rl/begin-scissor-mode (i32 (.x scissor)) (i32 (.y scissor))
(i32 (.width scissor)) (i32 (.height scissor))))
(when scissor-mode
(rl/begin-scissor-mode (i32 (.x scissor)) (i32 (.y scissor))
(i32 (.width scissor)) (i32 (.height scissor))))
;; A full-screen rectangle and a line of text. Only the part inside the
;; scissor rectangle reaches the framebuffer, which is the whole example.
(rl/draw-rectangle 0 0 (rl/get-screen-width) (rl/get-screen-height) rl/red)
(rl/draw-text "Move the mouse around to reveal this text!" 190 200 20
rl/lightgray)
;; A full-screen rectangle and a line of text. Only the part inside the
;; scissor rectangle reaches the framebuffer, which is the whole example.
(rl/draw-rectangle 0 0 (rl/get-screen-width) (rl/get-screen-height) rl/red)
(rl/draw-text "Move the mouse around to reveal this text!" 190 200 20
rl/lightgray)
(when scissor-mode (rl/end-scissor-mode))
(when scissor-mode (rl/end-scissor-mode))
(rl/draw-rectangle-lines-ex scissor 1.0 rl/black)
(rl/draw-text "Press S to toggle scissor test" 10 10 20 rl/black)
(rl/end-drawing)))
(rl/draw-rectangle-lines-ex scissor 1.0 rl/black)
(rl/draw-text "Press S to toggle scissor test" 10 10 20 rl/black))))

View File

@ -127,53 +127,51 @@
(set (.y ball-speed) (* (.y ball-speed) -1.0)))
;; Draw
(rl/begin-drawing)
(rl/with-drawing
;; A transparent framebuffer wants a transparent clear; anything else
;; paints over the desktop the flag exists to show.
(if (rl/window-state? rl/flag-window-transparent)
(rl/clear-background rl/blank)
(rl/clear-background rl/raywhite))
;; A transparent framebuffer wants a transparent clear; anything else
;; paints over the desktop the flag exists to show.
(if (rl/window-state? rl/flag-window-transparent)
(rl/clear-background rl/blank)
(rl/clear-background rl/raywhite))
(rl/draw-circle-v ball-pos ball-radius rl/maroon)
(rl/draw-rectangle-lines-ex
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (rl/get-screen-width))
.height (f32 (rl/get-screen-height))})
4.0 rl/raywhite)
(rl/draw-circle-v ball-pos ball-radius rl/maroon)
(rl/draw-rectangle-lines-ex
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (rl/get-screen-width))
.height (f32 (rl/get-screen-height))})
4.0 rl/raywhite)
(rl/draw-circle-v (rl/get-mouse-position) 10.0 rl/darkblue)
(rl/draw-circle-v (rl/get-mouse-position) 10.0 rl/darkblue)
(rl/draw-fps 10 10)
(rl/draw-fps 10 10)
;; The C's TextFormat("Screen Size: [%i, %i]", ...), reassembled out of
;; literals and examples/digits.flan the way the other ports do it: `x`
;; walks along the line, and draw-int hands back the width it drew.
(let [x 10]
(rl/draw-text "Screen Size: [" x 40 10 rl/green)
(set x (+ x (rl/measure-text "Screen Size: [" 10)))
(set x (+ x (d/draw-int (rl/get-screen-width) x 40 10 rl/green)))
(rl/draw-text ", " x 40 10 rl/green)
(set x (+ x (rl/measure-text ", " 10)))
(set x (+ x (d/draw-int (rl/get-screen-height) x 40 10 rl/green)))
(rl/draw-text "]" x 40 10 rl/green))
(rl/draw-text "Following flags can be set after window creation:"
10 60 10 rl/gray)
(draw-flag "[F] FLAG_FULLSCREEN_MODE:" rl/flag-fullscreen-mode 80)
(draw-flag "[R] FLAG_WINDOW_RESIZABLE:" rl/flag-window-resizable 100)
(draw-flag "[D] FLAG_WINDOW_UNDECORATED:" rl/flag-window-undecorated 120)
(draw-flag "[H] FLAG_WINDOW_HIDDEN:" rl/flag-window-hidden 140)
(draw-flag "[N] FLAG_WINDOW_MINIMIZED:" rl/flag-window-minimized 160)
(draw-flag "[M] FLAG_WINDOW_MAXIMIZED:" rl/flag-window-maximized 180)
(draw-flag "[U] FLAG_WINDOW_UNFOCUSED:" rl/flag-window-unfocused 200)
(draw-flag "[T] FLAG_WINDOW_TOPMOST:" rl/flag-window-topmost 220)
(draw-flag "[A] FLAG_WINDOW_ALWAYS_RUN:" rl/flag-window-always-run 240)
(draw-flag "[V] FLAG_VSYNC_HINT:" rl/flag-vsync-hint 260)
;; The C's TextFormat("Screen Size: [%i, %i]", ...), reassembled out of
;; literals and examples/digits.flan the way the other ports do it: `x`
;; walks along the line, and draw-int hands back the width it drew.
(let [x 10]
(rl/draw-text "Screen Size: [" x 40 10 rl/green)
(set x (+ x (rl/measure-text "Screen Size: [" 10)))
(set x (+ x (d/draw-int (rl/get-screen-width) x 40 10 rl/green)))
(rl/draw-text ", " x 40 10 rl/green)
(set x (+ x (rl/measure-text ", " 10)))
(set x (+ x (d/draw-int (rl/get-screen-height) x 40 10 rl/green)))
(rl/draw-text "]" x 40 10 rl/green))
(rl/draw-text "Following flags can be set after window creation:"
10 60 10 rl/gray)
(draw-flag "[F] FLAG_FULLSCREEN_MODE:" rl/flag-fullscreen-mode 80)
(draw-flag "[R] FLAG_WINDOW_RESIZABLE:" rl/flag-window-resizable 100)
(draw-flag "[D] FLAG_WINDOW_UNDECORATED:" rl/flag-window-undecorated 120)
(draw-flag "[H] FLAG_WINDOW_HIDDEN:" rl/flag-window-hidden 140)
(draw-flag "[N] FLAG_WINDOW_MINIMIZED:" rl/flag-window-minimized 160)
(draw-flag "[M] FLAG_WINDOW_MAXIMIZED:" rl/flag-window-maximized 180)
(draw-flag "[U] FLAG_WINDOW_UNFOCUSED:" rl/flag-window-unfocused 200)
(draw-flag "[T] FLAG_WINDOW_TOPMOST:" rl/flag-window-topmost 220)
(draw-flag "[A] FLAG_WINDOW_ALWAYS_RUN:" rl/flag-window-always-run 240)
(draw-flag "[V] FLAG_VSYNC_HINT:" rl/flag-vsync-hint 260)
(rl/draw-text "Following flags can only be set before window creation:"
10 300 10 rl/gray)
(draw-flag "FLAG_WINDOW_HIGHDPI:" rl/flag-window-highdpi 320)
(draw-flag "FLAG_WINDOW_TRANSPARENT:" rl/flag-window-transparent 340)
(draw-flag "FLAG_MSAA_4X_HINT:" rl/flag-msaa-4x-hint 360)
(rl/end-drawing)))
(rl/draw-text "Following flags can only be set before window creation:"
10 300 10 rl/gray)
(draw-flag "FLAG_WINDOW_HIGHDPI:" rl/flag-window-highdpi 320)
(draw-flag "FLAG_WINDOW_TRANSPARENT:" rl/flag-window-transparent 340)
(draw-flag "FLAG_MSAA_4X_HINT:" rl/flag-msaa-4x-hint 360))))

View File

@ -54,15 +54,13 @@
(when (rl/key-pressed? :n) (set exit-requested false))))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(if exit-requested
(do
(rl/draw-rectangle 0 100 screen-width 200 rl/black)
(rl/draw-text "Are you sure you want to exit program? [Y/N]" 40 180 30
rl/white))
(rl/draw-text "Try to close the window to get confirmation message!"
120 200 20 rl/lightgray))
(rl/end-drawing)))
(if exit-requested
(do
(rl/draw-rectangle 0 100 screen-width 200 rl/black)
(rl/draw-text "Are you sure you want to exit program? [Y/N]" 40 180 30
rl/white))
(rl/draw-text "Try to close the window to get confirmation message!"
120 200 20 rl/lightgray)))))

View File

@ -80,35 +80,32 @@
camera)]
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/begin-mode-3d camera)
(rl/draw-cube cube 2.0 2.0 2.0 rl/red)
(rl/draw-cube-wires cube 2.0 2.0 2.0 rl/maroon)
(rl/draw-grid 10 1.0)
(rl/end-mode-3d)
(rl/with-mode-3d camera
(rl/draw-cube cube 2.0 2.0 2.0 rl/red)
(rl/draw-cube-wires cube 2.0 2.0 2.0 rl/maroon)
(rl/draw-grid 10 1.0))
;; And the 2D half, drawn after end-mode-3d and therefore on top of the
;; cube whatever the depth buffer says — which is the second half of
;; what the example demonstrates.
(rl/draw-text "Enemy: 100 / 100"
(- (i32 (.x label-pos))
(/ (rl/measure-text "Enemy: 100/100" 20) 2))
(i32 (.y label-pos)) 20 rl/black)
;; And the 2D half, drawn after end-mode-3d and therefore on top of the
;; cube whatever the depth buffer says — which is the second half of
;; what the example demonstrates.
(rl/draw-text "Enemy: 100 / 100"
(- (i32 (.x label-pos))
(/ (rl/measure-text "Enemy: 100/100" 20) 2))
(i32 (.y label-pos)) 20 rl/black)
(let [x 10]
(rl/draw-text "Cube position in screen space coordinates: [" x 10 20
rl/lime)
(set x (+ x (rl/measure-text
"Cube position in screen space coordinates: [" 20)))
(set x (+ x (d/draw-int (i32 (.x label-pos)) x 10 20 rl/lime)))
(rl/draw-text ", " x 10 20 rl/lime)
(set x (+ x (rl/measure-text ", " 20)))
(set x (+ x (d/draw-int (i32 (.y label-pos)) x 10 20 rl/lime)))
(rl/draw-text "]" x 10 20 rl/lime))
(let [x 10]
(rl/draw-text "Cube position in screen space coordinates: [" x 10 20
rl/lime)
(set x (+ x (rl/measure-text
"Cube position in screen space coordinates: [" 20)))
(set x (+ x (d/draw-int (i32 (.x label-pos)) x 10 20 rl/lime)))
(rl/draw-text ", " x 10 20 rl/lime)
(set x (+ x (rl/measure-text ", " 20)))
(set x (+ x (d/draw-int (i32 (.y label-pos)) x 10 20 rl/lime)))
(rl/draw-text "]" x 10 20 rl/lime))
(rl/draw-text "Text 2d should be always on top of the cube" 10 40 20
rl/gray)
(rl/end-drawing))))
(rl/draw-text "Text 2d should be always on top of the cube" 10 40 20
rl/gray)))))

View File

@ -106,29 +106,24 @@
(set player-color (if hit rl/red rl/green)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/begin-mode-3d camera)
(rl/with-mode-3d camera
(rl/draw-cube enemy-box-pos (.x enemy-box-size) (.y enemy-box-size)
(.z enemy-box-size) rl/gray)
(rl/draw-cube-wires enemy-box-pos (.x enemy-box-size) (.y enemy-box-size)
(.z enemy-box-size) rl/darkgray)
(rl/draw-cube enemy-box-pos (.x enemy-box-size) (.y enemy-box-size)
(.z enemy-box-size) rl/gray)
(rl/draw-cube-wires enemy-box-pos (.x enemy-box-size) (.y enemy-box-size)
(.z enemy-box-size) rl/darkgray)
;; draw-sphere is draw-sphere-ex at 16 rings and 16 slices; the wires form
;; takes them because the tessellation is only visible in the wireframe.
(rl/draw-sphere enemy-sphere-pos enemy-sphere-size rl/gray)
(rl/draw-sphere-wires enemy-sphere-pos enemy-sphere-size 16 16 rl/darkgray)
;; draw-sphere is draw-sphere-ex at 16 rings and 16 slices; the wires form
;; takes them because the tessellation is only visible in the wireframe.
(rl/draw-sphere enemy-sphere-pos enemy-sphere-size rl/gray)
(rl/draw-sphere-wires enemy-sphere-pos enemy-sphere-size 16 16 rl/darkgray)
(rl/draw-cube-v player-position player-size player-color)
(rl/draw-cube-v player-position player-size player-color)
(rl/draw-grid 10 1.0))
(rl/draw-grid 10 1.0)
(rl/draw-text "Move player with arrow keys to collide" 220 40 20 rl/gray)
(rl/end-mode-3d)
(rl/draw-text "Move player with arrow keys to collide" 220 40 20 rl/gray)
(rl/draw-fps 10 10)
(rl/end-drawing)))
(rl/draw-fps 10 10))))

View File

@ -54,49 +54,47 @@
(set rotation (+ rotation 0.2))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-text "some basic shapes available on raylib" 20 20 20 rl/darkgray)
(rl/draw-text "some basic shapes available on raylib" 20 20 20 rl/darkgray)
;; Circles. The gradient one takes an inner and an outer colour and is the
;; first call in the corpus to pass two Colors at once.
(rl/draw-circle col-left 120 35.0 rl/darkblue)
(rl/draw-circle-gradient col-left 220 60.0 rl/green rl/skyblue)
(rl/draw-circle-lines col-left 340 80.0 rl/darkblue)
;; Circles. The gradient one takes an inner and an outer colour and is the
;; first call in the corpus to pass two Colors at once.
(rl/draw-circle col-left 120 35.0 rl/darkblue)
(rl/draw-circle-gradient col-left 220 60.0 rl/green rl/skyblue)
(rl/draw-circle-lines col-left 340 80.0 rl/darkblue)
;; Rectangles. draw-rectangle-gradient-h runs the colour left to right;
;; the -v form runs it top to bottom and the -ex form takes all four
;; corners. The C uses the horizontal one here.
(rl/draw-rectangle (- col-mid 60) 100 120 60 rl/red)
(rl/draw-rectangle-gradient-h (- col-mid 90) 170 180 130 rl/maroon rl/gold)
;; raylib draws this with quads internally rather than with lines, which
;; is why its thickness does not follow the line width anywhere.
(rl/draw-rectangle-lines (- col-mid 40) 320 80 60 rl/orange)
;; Rectangles. draw-rectangle-gradient-h runs the colour left to right;
;; the -v form runs it top to bottom and the -ex form takes all four
;; corners. The C uses the horizontal one here.
(rl/draw-rectangle (- col-mid 60) 100 120 60 rl/red)
(rl/draw-rectangle-gradient-h (- col-mid 90) 170 180 130 rl/maroon rl/gold)
;; raylib draws this with quads internally rather than with lines, which
;; is why its thickness does not follow the line width anywhere.
(rl/draw-rectangle-lines (- col-mid 40) 320 80 60 rl/orange)
;; Triangles. raylib wants the three vertices in counter-clockwise order
;; and draws nothing at all for a clockwise one, so the order here is not
;; cosmetic.
(rl/draw-triangle (rl/Vector2 {.x 600.0 .y 80.0})
(rl/Vector2 {.x 540.0 .y 150.0})
(rl/Vector2 {.x 660.0 .y 150.0})
rl/violet)
;; Triangles. raylib wants the three vertices in counter-clockwise order
;; and draws nothing at all for a clockwise one, so the order here is not
;; cosmetic.
(rl/draw-triangle (rl/Vector2 {.x 600.0 .y 80.0})
(rl/Vector2 {.x 540.0 .y 150.0})
(rl/Vector2 {.x 660.0 .y 150.0})
rl/violet)
(rl/draw-triangle-lines (rl/Vector2 {.x 600.0 .y 160.0})
(rl/Vector2 {.x 580.0 .y 230.0})
(rl/Vector2 {.x 620.0 .y 230.0})
rl/darkblue)
(rl/draw-triangle-lines (rl/Vector2 {.x 600.0 .y 160.0})
(rl/Vector2 {.x 580.0 .y 230.0})
(rl/Vector2 {.x 620.0 .y 230.0})
rl/darkblue)
;; Polygons, all three at the same centre and the same rotation, at three
;; radii, so the filled hexagon sits inside the two outlines.
(let [centre (rl/Vector2 {.x (f32 col-right) .y 330.0})]
(rl/draw-poly centre 6 80.0 rotation rl/brown)
(rl/draw-poly-lines centre 6 90.0 rotation rl/brown)
(rl/draw-poly-lines-ex centre 6 85.0 rotation 6.0 rl/beige))
;; Polygons, all three at the same centre and the same rotation, at three
;; radii, so the filled hexagon sits inside the two outlines.
(let [centre (rl/Vector2 {.x (f32 col-right) .y 330.0})]
(rl/draw-poly centre 6 80.0 rotation rl/brown)
(rl/draw-poly-lines centre 6 90.0 rotation rl/brown)
(rl/draw-poly-lines-ex centre 6 85.0 rotation 6.0 rl/beige))
;; The C draws every LINES-based shape together so raylib can batch them
;; into one pass. This last line is part of that comment and not an
;; afterthought, so it stays where the C has it.
(rl/draw-line 18 42 (- screen-width 18) 42 rl/black)
(rl/end-drawing)))
;; The C draws every LINES-based shape together so raylib can batch them
;; into one pass. This last line is part of that comment and not an
;; afterthought, so it stays where the C has it.
(rl/draw-line 18 42 (- screen-width 18) 42 rl/black))))

View File

@ -93,37 +93,35 @@
(when (rl/key-pressed? :space) (set paused (not paused)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-rectangle 0 0 screen-width screen-upper-limit
(if collision rl/red rl/black))
(rl/draw-rectangle 0 0 screen-width screen-upper-limit
(if collision rl/red rl/black))
(rl/draw-rectangle-rec box-a rl/gold)
(rl/draw-rectangle-rec box-b rl/blue)
(rl/draw-rectangle-rec box-a rl/gold)
(rl/draw-rectangle-rec box-b rl/blue)
(when collision
;; The overlap itself, drawn over both boxes. This patch landing
;; anywhere other than where the gold and the blue cross is what a
;; permuted Rectangle would look like.
(rl/draw-rectangle-rec box-collision rl/lime)
(when collision
;; The overlap itself, drawn over both boxes. This patch landing
;; anywhere other than where the gold and the blue cross is what a
;; permuted Rectangle would look like.
(rl/draw-rectangle-rec box-collision rl/lime)
(rl/draw-text "COLLISION!"
(- (/ (rl/get-screen-width) 2)
(/ (rl/measure-text "COLLISION!" 20) 2))
(- (/ screen-upper-limit 2) 10) 20 rl/black)
(rl/draw-text "COLLISION!"
(- (/ (rl/get-screen-width) 2)
(/ (rl/measure-text "COLLISION!" 20) 2))
(- (/ screen-upper-limit 2) 10) 20 rl/black)
;; The C's TextFormat("Collision Area: %i", ...). Both dimensions are
;; truncated to int before multiplying, as the C's two casts do.
(let [x (- (/ (rl/get-screen-width) 2) 100)
y (+ screen-upper-limit 10)]
(set x (+ x (d/draw-piece "Collision Area: " x y 20 rl/black)))
(d/draw-int (* (i32 (.width box-collision)) (i32 (.height box-collision)))
x y 20 rl/black)))
;; The C's TextFormat("Collision Area: %i", ...). Both dimensions are
;; truncated to int before multiplying, as the C's two casts do.
(let [x (- (/ (rl/get-screen-width) 2) 100)
y (+ screen-upper-limit 10)]
(set x (+ x (d/draw-piece "Collision Area: " x y 20 rl/black)))
(d/draw-int (* (i32 (.width box-collision)) (i32 (.height box-collision)))
x y 20 rl/black)))
(rl/draw-text "Press SPACE to PAUSE/RESUME" 20 (- screen-height 35) 20
rl/lightgray)
(rl/draw-text "Press SPACE to PAUSE/RESUME" 20 (- screen-height 35) 20
rl/lightgray)
(rl/draw-fps 10 10)
(rl/end-drawing)))
(rl/draw-fps 10 10))))

View File

@ -95,18 +95,16 @@
(set iris-right (track mouse sclera-right)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
;; White of the eye, iris, pupil — in that order, each over the last.
(rl/draw-circle-v sclera-left sclera-radius rl/lightgray)
(rl/draw-circle-v iris-left iris-radius rl/brown)
(rl/draw-circle-v iris-left 10.0 rl/black)
;; White of the eye, iris, pupil — in that order, each over the last.
(rl/draw-circle-v sclera-left sclera-radius rl/lightgray)
(rl/draw-circle-v iris-left iris-radius rl/brown)
(rl/draw-circle-v iris-left 10.0 rl/black)
(rl/draw-circle-v sclera-right sclera-radius rl/lightgray)
(rl/draw-circle-v iris-right iris-radius rl/darkgreen)
(rl/draw-circle-v iris-right 10.0 rl/black)
(rl/draw-circle-v sclera-right sclera-radius rl/lightgray)
(rl/draw-circle-v iris-right iris-radius rl/darkgreen)
(rl/draw-circle-v iris-right 10.0 rl/black)
(rl/draw-fps 10 10)
(rl/end-drawing)))
(rl/draw-fps 10 10))))

View File

@ -112,40 +112,38 @@
(set frames-counter 0))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-text "PLACE MOUSE OVER INPUT BOX!" 240 140 20 rl/gray)
(rl/draw-text "PLACE MOUSE OVER INPUT BOX!" 240 140 20 rl/gray)
(rl/draw-rectangle-rec text-box rl/lightgray)
(rl/draw-rectangle-lines (i32 (.x text-box)) (i32 (.y text-box))
(i32 (.width text-box)) (i32 (.height text-box))
(if mouse-on-text rl/red rl/darkgray))
(rl/draw-rectangle-rec text-box rl/lightgray)
(rl/draw-rectangle-lines (i32 (.x text-box)) (i32 (.y text-box))
(i32 (.width text-box)) (i32 (.height text-box))
(if mouse-on-text rl/red rl/darkgray))
(let [typed (string (slice name 0 letter-count))]
(rl/draw-text typed (+ (i32 (.x text-box)) 5) (+ (i32 (.y text-box)) 8)
40 rl/maroon)
(let [typed (string (slice name 0 letter-count))]
(rl/draw-text typed (+ (i32 (.x text-box)) 5) (+ (i32 (.y text-box)) 8)
40 rl/maroon)
;; The C's TextFormat("INPUT CHARS: %i/%i", ...), reassembled out of
;; examples/digits.flan the way the other ports do it. `typed` is a view
;; of `name` and not of the runtime's shared format buffer, so it
;; survives the two numbers being formatted — which a value from
;; i64->bytes would not.
(let [x 315]
(set x (+ x (d/draw-piece "INPUT CHARS: " x 250 20 rl/darkgray)))
(set x (+ x (d/draw-int letter-count x 250 20 rl/darkgray)))
(set x (+ x (d/draw-piece "/" x 250 20 rl/darkgray)))
(d/draw-int max-input-chars x 250 20 rl/darkgray))
;; The C's TextFormat("INPUT CHARS: %i/%i", ...), reassembled out of
;; examples/digits.flan the way the other ports do it. `typed` is a view
;; of `name` and not of the runtime's shared format buffer, so it
;; survives the two numbers being formatted — which a value from
;; i64->bytes would not.
(let [x 315]
(set x (+ x (d/draw-piece "INPUT CHARS: " x 250 20 rl/darkgray)))
(set x (+ x (d/draw-int letter-count x 250 20 rl/darkgray)))
(set x (+ x (d/draw-piece "/" x 250 20 rl/darkgray)))
(d/draw-int max-input-chars x 250 20 rl/darkgray))
(when mouse-on-text
(if (< letter-count max-input-chars)
;; Twenty frames on, twenty frames off, at the pen position after
;; whatever has been typed.
(when (= (% (/ frames-counter 20) 2) 0)
(rl/draw-text "_"
(+ (i32 (.x text-box)) 8 (rl/measure-text typed 40))
(+ (i32 (.y text-box)) 12) 40 rl/maroon))
(rl/draw-text "Press BACKSPACE to delete chars..." 230 300 20
rl/gray))))
(rl/end-drawing)))
(when mouse-on-text
(if (< letter-count max-input-chars)
;; Twenty frames on, twenty frames off, at the pen position after
;; whatever has been typed.
(when (= (% (/ frames-counter 20) 2) 0)
(rl/draw-text "_"
(+ (i32 (.x text-box)) 8 (rl/measure-text typed 40))
(+ (i32 (.y text-box)) 12) 40 rl/maroon))
(rl/draw-text "Press BACKSPACE to delete chars..." 230 300 20
rl/gray)))))))

View File

@ -61,16 +61,14 @@
(when (rl/key-pressed? :enter) (set frames-counter 0))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
;; One character every ten frames. The clamp is the whole difference from
;; the C — see the header comment.
(let [b (bytes message)
n (min (i32 (len b)) (/ frames-counter 10))]
(rl/draw-text (string (slice b 0 n)) 210 160 20 rl/maroon))
;; One character every ten frames. The clamp is the whole difference from
;; the C — see the header comment.
(let [b (bytes message)
n (min (i32 (len b)) (/ frames-counter 10))]
(rl/draw-text (string (slice b 0 n)) 210 160 20 rl/maroon))
(rl/draw-text "PRESS [ENTER] to RESTART!" 240 260 20 rl/lightgray)
(rl/draw-text "HOLD [SPACE] to SPEED UP!" 239 300 20 rl/lightgray)
(rl/end-drawing)))
(rl/draw-text "PRESS [ENTER] to RESTART!" 240 260 20 rl/lightgray)
(rl/draw-text "HOLD [SPACE] to SPEED UP!" 239 300 20 rl/lightgray))))

View File

@ -138,60 +138,57 @@
;; Draw the fog into its own little target first, at one pixel per tile.
;; blank is alpha 0, so a tile that is neither unseen nor remembered
;; leaves nothing behind and the map below shows through unmodified.
(rl/begin-texture-mode fog-of-war)
(rl/clear-background rl/blank)
(dotimes [y tiles-y]
(dotimes [x tiles-x]
;; A tile in view (1) draws nothing at all and stays transparent.
(let [f (at tile-fog (+ (* y tiles-x) x))]
(when (!= f 1)
(rl/draw-rectangle x y 1 1
(if (= f 0) rl/black (rl/fade rl/black 0.8)))))))
(rl/end-texture-mode)
(rl/with-texture-mode fog-of-war
(rl/clear-background rl/blank)
(dotimes [y tiles-y]
(dotimes [x tiles-x]
;; A tile in view (1) draws nothing at all and stays transparent.
(let [f (at tile-fog (+ (* y tiles-x) x))]
(when (!= f 1)
(rl/draw-rectangle x y 1 1
(if (= f 0) rl/black (rl/fade rl/black 0.8))))))))
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
;; The map itself, in full size.
(dotimes [y tiles-y]
(dotimes [x tiles-x]
(rl/draw-rectangle (* x map-tile-size) (* y map-tile-size)
map-tile-size map-tile-size
(if (= (at tile-ids (+ (* y tiles-x) x)) 0)
rl/blue
(rl/fade rl/blue 0.9)))
(rl/draw-rectangle-lines (* x map-tile-size) (* y map-tile-size)
map-tile-size map-tile-size
(rl/fade rl/darkblue 0.5))))
;; The map itself, in full size.
(dotimes [y tiles-y]
(dotimes [x tiles-x]
(rl/draw-rectangle (* x map-tile-size) (* y map-tile-size)
map-tile-size map-tile-size
(if (= (at tile-ids (+ (* y tiles-x) x)) 0)
rl/blue
(rl/fade rl/blue 0.9)))
(rl/draw-rectangle-lines (* x map-tile-size) (* y map-tile-size)
map-tile-size map-tile-size
(rl/fade rl/darkblue 0.5))))
(rl/draw-rectangle-v player-position
(rl/Vector2 {.x (f32 player-size) .y (f32 player-size)})
rl/red)
(rl/draw-rectangle-v player-position
(rl/Vector2 {.x (f32 player-size) .y (f32 player-size)})
rl/red)
;; The fog, stretched over the whole map. The negative source height is
;; the flip — see the header comment.
(rl/draw-texture-pro
(.texture fog-of-war)
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (.width (.texture fog-of-war)))
.height (- 0.0 (f32 (.height (.texture fog-of-war))))})
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (* tiles-x map-tile-size))
.height (f32 (* tiles-y map-tile-size))})
(rl/Vector2 {.x 0.0 .y 0.0})
0.0
rl/white)
;; The fog, stretched over the whole map. The negative source height is
;; the flip — see the header comment.
(rl/draw-texture-pro
(.texture fog-of-war)
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (.width (.texture fog-of-war)))
.height (- 0.0 (f32 (.height (.texture fog-of-war))))})
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (* tiles-x map-tile-size))
.height (f32 (* tiles-y map-tile-size))})
(rl/Vector2 {.x 0.0 .y 0.0})
0.0
rl/white)
;; The C's TextFormat("Current tile: [%i,%i]", ...). Each number is drawn
;; before the next is formatted, which examples/digits.flan requires:
;; both share one static buffer in the runtime.
(let [x 10]
(set x (+ x (d/draw-piece "Current tile: [" x 10 20 rl/raywhite)))
(set x (+ x (d/draw-int player-tile-x x 10 20 rl/raywhite)))
(set x (+ x (d/draw-piece "," x 10 20 rl/raywhite)))
(set x (+ x (d/draw-int player-tile-y x 10 20 rl/raywhite)))
(d/draw-piece "]" x 10 20 rl/raywhite))
;; The C's TextFormat("Current tile: [%i,%i]", ...). Each number is drawn
;; before the next is formatted, which examples/digits.flan requires:
;; both share one static buffer in the runtime.
(let [x 10]
(set x (+ x (d/draw-piece "Current tile: [" x 10 20 rl/raywhite)))
(set x (+ x (d/draw-int player-tile-x x 10 20 rl/raywhite)))
(set x (+ x (d/draw-piece "," x 10 20 rl/raywhite)))
(set x (+ x (d/draw-int player-tile-y x 10 20 rl/raywhite)))
(d/draw-piece "]" x 10 20 rl/raywhite))
(rl/draw-text "ARROW KEYS to move" 10 (- screen-height 25) 20 rl/raywhite)
(rl/end-drawing)))
(rl/draw-text "ARROW KEYS to move" 10 (- screen-height 25) 20 rl/raywhite))))

View File

@ -104,27 +104,25 @@
(set current-texture (% (+ current-texture 1) num-textures)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/draw-texture (at textures current-texture) 0 0 rl/white)
(rl/draw-texture (at textures current-texture) 0 0 rl/white)
(rl/draw-rectangle 30 400 325 30 (rl/fade rl/skyblue 0.5))
(rl/draw-rectangle-lines 30 400 325 30 (rl/fade rl/white 0.5))
(rl/draw-text "MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES"
40 410 10 rl/white)
(rl/draw-rectangle 30 400 325 30 (rl/fade rl/skyblue 0.5))
(rl/draw-rectangle-lines 30 400 325 30 (rl/fade rl/white 0.5))
(rl/draw-text "MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES"
40 410 10 rl/white)
;; The C's switch. Each label is positioned so its right edge lands in the
;; same place, which is why the x differs per line.
(cond
(= current-texture 0) (rl/draw-text "VERTICAL GRADIENT" 560 10 20 rl/raywhite)
(= current-texture 1) (rl/draw-text "HORIZONTAL GRADIENT" 540 10 20 rl/raywhite)
(= current-texture 2) (rl/draw-text "DIAGONAL GRADIENT" 540 10 20 rl/raywhite)
(= current-texture 3) (rl/draw-text "RADIAL GRADIENT" 580 10 20 rl/lightgray)
(= current-texture 4) (rl/draw-text "SQUARE GRADIENT" 580 10 20 rl/lightgray)
(= current-texture 5) (rl/draw-text "CHECKED" 680 10 20 rl/raywhite)
(= current-texture 6) (rl/draw-text "WHITE NOISE" 640 10 20 rl/red)
(= current-texture 7) (rl/draw-text "PERLIN NOISE" 640 10 20 rl/red)
:else (rl/draw-text "CELLULAR" 670 10 20 rl/raywhite))
(rl/end-drawing)))
;; The C's switch. Each label is positioned so its right edge lands in the
;; same place, which is why the x differs per line.
(cond
(= current-texture 0) (rl/draw-text "VERTICAL GRADIENT" 560 10 20 rl/raywhite)
(= current-texture 1) (rl/draw-text "HORIZONTAL GRADIENT" 540 10 20 rl/raywhite)
(= current-texture 2) (rl/draw-text "DIAGONAL GRADIENT" 540 10 20 rl/raywhite)
(= current-texture 3) (rl/draw-text "RADIAL GRADIENT" 580 10 20 rl/lightgray)
(= current-texture 4) (rl/draw-text "SQUARE GRADIENT" 580 10 20 rl/lightgray)
(= current-texture 5) (rl/draw-text "CHECKED" 680 10 20 rl/raywhite)
(= current-texture 6) (rl/draw-text "WHITE NOISE" 640 10 20 rl/red)
(= current-texture 7) (rl/draw-text "PERLIN NOISE" 640 10 20 rl/red)
:else (rl/draw-text "CELLULAR" 670 10 20 rl/raywhite)))))

View File

@ -114,9 +114,8 @@
(set target (rl/load-render-texture screen-width screen-height))
(defer (rl/unload-render-texture target))
(rl/begin-texture-mode target)
(rl/clear-background (at colors 0))
(rl/end-texture-mode)
(rl/with-texture-mode target
(rl/clear-background (at colors 0)))
;; 120 and not 60: the stroke is a circle stamped at the mouse position once
;; per frame, so the gaps between stamps during a fast drag are a function
@ -148,21 +147,19 @@
2.0 50.0))
(when (rl/key-pressed? :c)
(rl/begin-texture-mode target)
(rl/clear-background (at colors 0))
(rl/end-texture-mode))
(rl/with-texture-mode target
(rl/clear-background (at colors 0))))
;; Paint. The gesture test is what makes the example work on a
;; touchscreen, where there is no mouse button to hold.
(when (or (rl/mouse-button-down? :left)
(= (rl/get-gesture-detected) :drag))
(rl/begin-texture-mode target)
;; Above y=50 is the palette strip, and a stroke there would paint
;; under the toolbar where it could never be seen.
(when (> (.y mouse-pos) 50.0)
(rl/draw-circle (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
brush-size (at colors color-selected)))
(rl/end-texture-mode))
(rl/with-texture-mode target
;; Above y=50 is the palette strip, and a stroke there would paint
;; under the toolbar where it could never be seen.
(when (> (.y mouse-pos) 50.0)
(rl/draw-circle (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
brush-size (at colors color-selected)))))
;; Right button erases, which is painting in the clear colour. The
;; selected swatch is parked while the button is held so the toolbar
@ -173,11 +170,10 @@
(set color-selected-prev color-selected)
(set color-selected 0))
(set mouse-was-pressed true)
(rl/begin-texture-mode target)
(when (> (.y mouse-pos) 50.0)
(rl/draw-circle (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
brush-size (at colors 0)))
(rl/end-texture-mode))
(rl/with-texture-mode target
(when (> (.y mouse-pos) 50.0)
(rl/draw-circle (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
brush-size (at colors 0)))))
(when (and (rl/mouse-button-released? :right) mouse-was-pressed)
(set color-selected color-selected-prev)
(set mouse-was-pressed false)))
@ -201,55 +197,53 @@
(set save-message-counter 0)))
;; Draw
(rl/begin-drawing)
(rl/clear-background rl/raywhite)
(rl/with-drawing
(rl/clear-background rl/raywhite)
;; The canvas, flipped by the negative source height.
(rl/draw-texture-rec (.texture target)
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (.width (.texture target)))
.height (- 0.0 (f32 (.height (.texture target))))})
(rl/Vector2 {.x 0.0 .y 0.0})
rl/white)
;; The canvas, flipped by the negative source height.
(rl/draw-texture-rec (.texture target)
(rl/Rectangle {.x 0.0 .y 0.0
.width (f32 (.width (.texture target)))
.height (- 0.0 (f32 (.height (.texture target))))})
(rl/Vector2 {.x 0.0 .y 0.0})
rl/white)
;; The brush preview, drawn on the screen and not into the canvas.
(when (> (.y mouse-pos) 50.0)
(if (rl/mouse-button-down? :right)
(rl/draw-circle-lines (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
brush-size rl/gray)
(rl/draw-circle (rl/get-mouse-x) (rl/get-mouse-y)
brush-size (at colors color-selected))))
;; The brush preview, drawn on the screen and not into the canvas.
(when (> (.y mouse-pos) 50.0)
(if (rl/mouse-button-down? :right)
(rl/draw-circle-lines (i32 (.x mouse-pos)) (i32 (.y mouse-pos))
brush-size rl/gray)
(rl/draw-circle (rl/get-mouse-x) (rl/get-mouse-y)
brush-size (at colors color-selected))))
;; The toolbar, over the canvas.
(rl/draw-rectangle 0 0 (rl/get-screen-width) 50 rl/raywhite)
(rl/draw-line 0 50 (rl/get-screen-width) 50 rl/lightgray)
;; The toolbar, over the canvas.
(rl/draw-rectangle 0 0 (rl/get-screen-width) 50 rl/raywhite)
(rl/draw-line 0 50 (rl/get-screen-width) 50 rl/lightgray)
(dotimes [i max-colors-count]
(rl/draw-rectangle-rec (at colors-recs i) (at colors i)))
;; The first swatch is raywhite on raywhite, so it needs an outline to
;; be visible at all.
(rl/draw-rectangle-lines 10 10 30 30 rl/lightgray)
(dotimes [i max-colors-count]
(rl/draw-rectangle-rec (at colors-recs i) (at colors i)))
;; The first swatch is raywhite on raywhite, so it needs an outline to
;; be visible at all.
(rl/draw-rectangle-lines 10 10 30 30 rl/lightgray)
(when (>= color-mouse-hover 0)
(rl/draw-rectangle-rec (at colors-recs color-mouse-hover)
(rl/fade rl/white 0.6)))
(when (>= color-mouse-hover 0)
(rl/draw-rectangle-rec (at colors-recs color-mouse-hover)
(rl/fade rl/white 0.6)))
(let [r (at colors-recs color-selected)]
(rl/draw-rectangle-lines-ex
(rl/Rectangle {.x (- (.x r) 2.0) .y (- (.y r) 2.0)
.width (+ (.width r) 4.0) .height (+ (.height r) 4.0)})
2.0 rl/black))
(let [r (at colors-recs color-selected)]
(rl/draw-rectangle-lines-ex
(rl/Rectangle {.x (- (.x r) 2.0) .y (- (.y r) 2.0)
.width (+ (.width r) 4.0) .height (+ (.height r) 4.0)})
2.0 rl/black))
(rl/draw-rectangle-lines-ex btn-save-rec 2.0
(if btn-save-mouse-hover rl/red rl/black))
(rl/draw-text "SAVE!" 755 20 10
(if btn-save-mouse-hover rl/red rl/black))
(rl/draw-rectangle-lines-ex btn-save-rec 2.0
(if btn-save-mouse-hover rl/red rl/black))
(rl/draw-text "SAVE!" 755 20 10
(if btn-save-mouse-hover rl/red rl/black))
(when show-save-message
(rl/draw-rectangle 0 0 (rl/get-screen-width) (rl/get-screen-height)
(rl/fade rl/raywhite 0.8))
(rl/draw-rectangle 0 150 (rl/get-screen-width) 80 rl/black)
(rl/draw-text "IMAGE SAVED: my_amazing_texture_painting.png"
150 180 20 rl/raywhite))
(rl/end-drawing))))
(when show-save-message
(rl/draw-rectangle 0 0 (rl/get-screen-width) (rl/get-screen-height)
(rl/fade rl/raywhite 0.8))
(rl/draw-rectangle 0 150 (rl/get-screen-width) 80 rl/black)
(rl/draw-text "IMAGE SAVED: my_amazing_texture_painting.png"
150 180 20 rl/raywhite))))))

View File

@ -15,7 +15,10 @@
;;;; offers.
;;;;
;;;; Note what it deliberately does not use: no Vec, no Map, no generics, no
;;;; user-written macros, no allocator other than the stack and static storage.
;;;; macros of its own, no allocator other than the stack and static storage.
;;;; It does call rl/with-drawing, which is the raylib package's macro over
;;;; the BeginDrawing/EndDrawing pair — the parity rule is what the reference
;;;; versions do, and lisp/sand.lisp writes rl:with-drawing there.
;;;;
;;;; Notation reminders (see plan.org and spec-memory.md):
;;;; [n T] fixed array, length n, element T — a VALUE, copies
@ -191,11 +194,13 @@
;; leaves one CL-style escape hatch: choose `continue' in the editor to
;; abandon this frame and return to the next one with the game still live.
;; Keep drawing outside it. Skipping between BeginDrawing and EndDrawing
;; would leave raylib's frame unbalanced.
;; would leave raylib's frame unbalanced, and rl/with-drawing does not
;; change that — it guarantees the two calls stay together and stay
;; matched, not that a transfer out of the body reaches the second one.
;; The restart being out here is still what makes `continue' safe.
(restart-case
(do (agent/poll)
(game-update))
(continue [] (do)))
(rl/begin-drawing)
(game-draw)
(rl/end-drawing)))
(rl/with-drawing
(game-draw))))

View File

@ -0,0 +1,16 @@
;;;; with-mode-2d without a camera.
;;;;
;;;; The prelude's idiom for a macro's own arity check, which clamp and unless
;;;; and into all use: answer a symbol that is not a name, spelled as the
;;;; sentence the author needed to read. There is no way for a macro to signal
;;;; at expansion time, so the message travels as the unknown function — and
;;;; what makes it land usefully is the note the expander adds beneath it,
;;;; naming the macro and the call site.
;;;;
;;;; Being refused is the whole test; this is never built.
(import rl "vendor:raylib")
(defn main [] i32
(rl/with-mode-2d)
0)

View File

@ -0,0 +1,43 @@
;;;; The raylib package's five begin/end macros, expanded.
;;;;
;;;; They cannot be *run* here. Every one of them brackets calls that need a
;;;; window and a GL context, so a headless case can only assert what the
;;;; expansion is, not what it draws — which is the whole of what a macro
;;;; over a pair can get wrong. The four examples/ uses of with-drawing and
;;;; friends already prove they compile; what this adds is with-scissor-mode,
;;;; which no example can use (examples/core-scissor-test.flan turns its
;;;; scissor on and off from a flag, so its Begin and its End sit inside two
;;;; separate `when`s and there is no body to wrap). The arity refusals are
;;;; rl-with-reject.flan, beside this.
;;;;
;;;; [frame] is deliberately not reached from [main]. lib/reach.ml answers the
;;;; link from the checked program, so an unreachable frame means this case
;;;; needs no libraylib to run and goes in the suite unconditionally — while
;;;; [check] still walks every line of it, which is where a wrong expansion
;;;; would show.
(import rl "vendor:raylib")
(defn frame [cam rl/Camera2D cam3 rl/Camera3D target rl/RenderTexture2D] ()
(rl/with-drawing
(rl/clear-background rl/raywhite)
(rl/with-mode-2d cam
(rl/draw-rectangle 0 0 10 10 rl/red))
(rl/with-mode-3d cam3
(rl/draw-fps 0 0))
(rl/with-scissor-mode 10 10 100 100
(rl/draw-rectangle 0 0 320 240 rl/blue)
(rl/draw-text "clipped" 20 20 10 rl/black))
(rl/draw-fps 10 10))
;; Off-screen, outside the frame, which is where a render texture belongs.
(rl/with-texture-mode target
(rl/clear-background rl/blank)))
(defn main [] i32
(println "expanded")
0)

View File

@ -896,6 +896,16 @@ let () =
outputs ~opt:"-O0" "raylib, bindings generated from the header, -O0"
"programs/raylib-imported.flan" out;
(* The raylib package's begin/end macros — vendor/raylib/modes.flan.
Nothing here draws: every one of the five brackets calls that want a
window and a GL context, so what can be asserted headless is that the
expansion compiles, which is the whole of what a macro over a pair can
get wrong. The program keeps the frame function unreachable from main
on purpose, so reach.ml links no libraylib and this runs unconditionally
alongside the cases that need one. See the file's header. *)
outputs "raylib begin/end macros expand" "programs/rl-with.flan"
"expanded\n";
(* raylib's Image family, headless, and the strongest FFI case here: an
Image is pixels in RAM, so raylib *computes* with it rather than
storing and returning it.
@ -1397,6 +1407,17 @@ let () =
name m needle
end
in
(* A package macro's arity check. A macro cannot signal while it expands,
so the prelude's idiom clamp's, unless's, into's is to answer a
symbol that is not a name and reads as the sentence the caller needs.
What makes that usable rather than baffling is the expander's note
beneath it, naming rl/with-mode-2d at the call site rather than
pointing into the package that half is rendering and is not asserted
here. *)
refuses "with-mode-2d written without a camera"
"programs/rl-with-reject.flan"
"with-mode-2d-takes-a-camera-and-a-body";
(* Visibility: main is not a name a package offers, and saying so is the
point "unknown name sand/main" would be true and useless. *)
(* Generics, at the definition rather than at a call site. Both of these

125
vendor/raylib/modes.flan vendored Normal file
View File

@ -0,0 +1,125 @@
;;;; The begin/end pairs, as macros that cannot come apart.
;;;;
;;;; raylib has five pairs this package binds — drawing, 2D camera, 3D camera,
;;;; render texture, scissor — and every one of them is the same hazard: two
;;;; calls that have to bracket a body, written by hand, with nothing checking
;;;; that the second one is there or that it is the matching one. An
;;;; EndDrawing that is missing hangs the frame; an EndMode2D that should have
;;;; been EndMode3D corrupts the matrix stack; a pair that drifted apart during
;;;; an edit does either. NEXT.md put `unwind-protect` for this and rejected
;;;; it, with the note that these are "three-line macros once the expander
;;;; lands". They are, and this is them.
;;;;
;;;; (rl/with-drawing
;;;; (rl/clear-background rl/raywhite)
;;;; (rl/draw-fps 10 10))
;;;;
;;;; expands to exactly what the caller used to type, in the order they used
;;;; to type it:
;;;;
;;;; (do (rl/begin-drawing)
;;;; (rl/clear-background rl/raywhite)
;;;; (rl/draw-fps 10 10)
;;;; (rl/end-drawing))
;;;;
;;;; The argument-taking ones put the argument where it always went, evaluated
;;;; once, in place. There is no `let` and no `gensym` anywhere below: nothing
;;;; here binds a name, so there is no name for a caller's to collide with,
;;;; and introducing one in a deliberately non-hygienic macro that does not
;;;; need it would be a step backwards.
;;;;
;;;; ── What this does not fix, and why it cannot ───────────────────────
;;;;
;;;; The bug class removed is "the End* is missing, wrong, or no longer
;;;; beside its Begin*". The bug class NOT removed is a body that leaves
;;;; through the unwind path instead of the bottom: a `return`, or an
;;;; `invoke-restart` reaching an enclosing `restart-case`. Both of those skip
;;;; the rest of the `do` and the `End*` with it, exactly as they would skip a
;;;; hand-written one.
;;;;
;;;; The obvious fix is `defer`, and it is not available here. `defer` is a
;;;; compile-time construct — the forms are copied into the function's exit
;;;; paths — so BUILT.md refuses it inside a loop or a branch, and a
;;;; begin/end pair lives inside the game loop essentially always. Were it
;;;; permitted there it would be wrong in the worse direction: one EndDrawing
;;;; at function exit for N BeginDrawings. A macro expanding to a defer would
;;;; therefore compile at a function body's top level and be refused in the
;;;; one place anybody writes it, which is a worse thing to hand somebody than
;;;; a macro that is honest about its extent.
;;;;
;;;; So the discipline sand.flan already writes down stays the discipline:
;;;; keep the restart boundary OUTSIDE the pair, so choosing `continue` for a
;;;; frame abandons the update and still reaches the drawing. spec-conditions
;;;; §5 is the rule behind that — a transfer runs the intervening frames'
;;;; defers and moves control, and there are no defers here to run.
;;;;
;;;; ── Why this is a file of its own ───────────────────────────────────
;;;;
;;;; vector.flan's reasoning, unchanged: the split is on `declare-c`.
;;;; raylib.flan is the package's statement about C, it is the file the header
;;;; check reads hand-written signatures out of, and a wrong line in it stops
;;;; the build. There is not one `declare-c` below — every macro here expands
;;;; into names raylib.flan already declares — so there is nothing for the
;;;; header check to read and nothing raylib can make wrong. A package is a
;;;; directory, so this is another .flan beside the others and is qualified
;;;; `rl/` like the rest of it.
;;;;
;;;; The names come out qualified: the importer writes `(rl/with-drawing …)`
;;;; and a bare `(with-drawing …)` is an unknown name there, the rule every
;;;; declaration in this package follows. The expansions name this package's
;;;; own functions unqualified and the expander qualifies them on the way out
;;;; — see test/programs/pkgs/mac/mac.flan, which is that rule's worked
;;;; example.
;;;;
;;;; Each macro answers the value of its `End*` call, which is (). A pair was
;;;; never an expression worth reading anyway.
;; The frame. Everything drawn lands on the back buffer; end-drawing swaps it
;; and waits out the frame time set by set-target-fps.
(defmacro with-drawing [args]
(if (< (len args) 1)
`(with-drawing-takes-a-body)
`(do (begin-drawing)
~@args
(end-drawing))))
;; The 2D camera. The argument is a Camera2D value, evaluated once where it
;; always was. Remember that a fresh (Camera2D {}) has zoom 0.0 and is not
;; usable as an identity — raylib.flan says so beside the struct.
(defmacro with-mode-2d [args]
(if (< (len args) 2)
`(with-mode-2d-takes-a-camera-and-a-body)
`(do (begin-mode-2d ~(at args 0))
~@(form-rest args 1)
(end-mode-2d))))
;; The 3D camera. Same shape, same argument-once rule, and the pair matters
;; more here than anywhere: ending a 3D mode with end-mode-2d type-checks
;; fine and leaves the projection matrix wrong for everything after it.
(defmacro with-mode-3d [args]
(if (< (len args) 2)
`(with-mode-3d-takes-a-camera-and-a-body)
`(do (begin-mode-3d ~(at args 0))
~@(form-rest args 1)
(end-mode-3d))))
;; Render to a texture instead of the screen. The target's texture comes out
;; of the GPU upside down, so drawing it back wants a negative source height —
;; that correction is the caller's and is deliberately not hidden here, since
;; it belongs with the draw and not with the mode.
(defmacro with-texture-mode [args]
(if (< (len args) 2)
`(with-texture-mode-takes-a-target-and-a-body)
`(do (begin-texture-mode ~(at args 0))
~@(form-rest args 1)
(end-texture-mode))))
;; Clip to a rectangle, in screen pixels with y down from the top. Four
;; scalars rather than a Rectangle, because that is what BeginScissorMode
;; takes and this file is not the place to invent a second spelling.
(defmacro with-scissor-mode [args]
(if (< (len args) 5)
`(with-scissor-mode-takes-x-y-width-height-and-a-body)
`(do (begin-scissor-mode ~(at args 0) ~(at args 1) ~(at args 2) ~(at args 3))
~@(form-rest args 4)
(end-scissor-mode))))