flan/examples/text-writing-anim.flan
Joseph Ferano d7ceec448e Ten more raylib examples, and the three small structs 3D needed
Three shapes, two text, three textures, one models and one core, picked for
binding surface rather than for how they look.

shapes-basic-shapes brings in six draw families nothing had called — the
circle and rectangle gradients, the triangles, all three poly draws — and is
the first call in the corpus to pass two Colors or three Vector2s at once.
shapes-collision-area is get-collision-rec, the only binding that takes two
Rectangles and answers a third, on a frame path. shapes-following-eyes is the
raymath gap measured rather than worked around: every line of it is vector
arithmetic written without a vector library, the way the C writes it.

text-input-box drains get-char-pressed's queue, which no example had read,
and needed a MouseCursor defenum for set-mouse-cursor. text-writing-anim
replaces TextSubtext — unbindable, it answers a pointer into a rotating
static buffer — with (string (slice b 0 n)), which is the same operation
without the shared state.

textures-image-generation runs nine Gen* calls and the
gen/upload/unload-image path, all procedural, no file on disk.
textures-fog-of-war needed a TextureFilter defenum: the smooth fog edge is
entirely :bilinear on a 25x15 render texture, and it is also the first
draw-texture-pro with a negative source height. textures-mouse-painting is
the same render texture used as a document rather than as scratch, plus the
round trip back off the GPU — load-image-from-texture, image-flip-vertical,
export-image — which nothing had run.

models-box-collisions is the counterexample to "a models example is a binding
exercise": nothing in it is a Model, and one BoundingBox defstruct un-refuses
four functions. core-3d-picking is the only caller anywhere for Ray and
RayCollision, and picking is the inverse of the get-world-to-screen the
corpus already had.

Added to vendor/raylib: defstructs BoundingBox, Ray and RayCollision;
defenums MouseCursor and TextureFilter with their mapping lines in bindings;
hand-written declare-c for SetMouseCursor, SetTextureFilter, DrawCubeV,
DrawSphere, DrawSphereWires, DrawRay and GetScreenToWorldRay, each excluded
from the generated half on the rule bindings already states. generated.flan
regenerated against raylib 5.5: 272 declarations, 117 refused, every
defstruct, hand-written declare-c and mapped constant agreeing with the
header.
2026-09-13 14:42:49 +07:00

77 lines
3.1 KiB
Plaintext

;;;; raylib [text] example - text writing anim
;;;;
;;;; examples/text/text_writing_anim.c. Sixty lines of C whose whole substance
;;;; is one call this package cannot bind:
;;;;
;;;; DrawText(TextSubtext(message, 0, framesCounter/10), ...)
;;;;
;;;; TextSubtext is in raylib.h and is not in the bindings, on the rule
;;;; raylib.flan states for the whole Text* family: it answers a `char *` into
;;;; a rotating static buffer, and declare-c refuses a returned pointer to
;;;; memory the caller does not own. There is no missing line to add — the
;;;; binding would be wrong at any signature.
;;;;
;;;; And it does not matter, because Flan has the operation as a primitive.
;;;; `(slice a lo hi)` takes a view of an array, `(string b)` reinterprets the
;;;; bytes as a string at no cost, and `(string (slice message 0 n))` is
;;;; TextSubtext with the static buffer removed — no copy, no shared state, no
;;;; rotation to run out of. Porting the example is therefore how the gap gets
;;;; *closed* rather than reported: the C's workaround for not having slices
;;;; is the thing Flan did not need.
;;;;
;;;; The one place they differ, and it is why the clamp below is here rather
;;;; than in the C: TextSubtext clips its length to the string, so the C can
;;;; pass a counter that runs past the end for ever and see nothing happen.
;;;; `slice` does not clip — an out-of-range bound is an error, and a bound
;;;; computed at run time is checked at run time — so the length has to be
;;;; brought inside the string before the call. That is a better failure than
;;;; the C's, and it costs one `min`.
;;;;
;;;; The message is a `[u8]` and not a `string` because `slice` takes an array
;;;; or a slice; `(bytes "…")` is the bridge in the other direction from
;;;; `(string …)` and costs nothing either. The embedded newline is written as
;;;; an escape, and raylib's draw-text breaks the line on it.
(import rl "vendor:raylib")
(defconst screen-width 800)
(defconst screen-height 450)
(defconst message
"This sample illustrates a text writing\nanimation effect! Check it out! ;)")
(defvar frames-counter i32)
(defn main [] ()
(rl/init-window screen-width screen-height
"raylib [text] example - text writing anim")
(defer (rl/close-window))
(set frames-counter 0)
(rl/set-target-fps 60)
(until (rl/window-should-close?)
;; Update. Holding space runs the counter eight times faster; enter starts
;; it over.
(if (rl/key-down? :space)
(set frames-counter (+ frames-counter 8))
(set frames-counter (+ frames-counter 1)))
(when (rl/key-pressed? :enter) (set frames-counter 0))
;; Draw
(rl/begin-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))
(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)))