The trio the author decided on 2026-09-20 is now all built: def is CL's defparameter — its initialiser runs on every daemon re-run, unguarded, so an edited initialiser repaints the same storage on C-c C-c plus re-run — defonce (Clojure's name for CL's defvar, per the author) initialises once behind the .init~once. flag, and defconst stays the image. One parse arm reads both forms; the difference is Ast.reinit, carried to Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and Check.check_global lifts every def initialiser — zero and literal included — into global/<n>, so the host's startup reaches it through the function cell and a re-evaluated def swaps it (Session's def_inits; Emit.redefinition declares the cell for a non-sibling target). The old defvar spelling is refused with the rename and both compiling spellings, and every program, test, doc and editor list is swept — except sand.flan, the author's live WIP, whose seven defvar lines are flagged in FIX.org and keep its three dependent tests red on this branch.
111 lines
5.1 KiB
Plaintext
111 lines
5.1 KiB
Plaintext
;;;; raylib [shapes] example - following eyes
|
|
;;;;
|
|
;;;; examples/shapes/shapes_following_eyes.c. Ported for the thing it needs
|
|
;;;; that raylib does not supply: the whole update is vector arithmetic —
|
|
;;;; subtract two points, take the angle, walk back out along it — and every
|
|
;;;; one of those steps is raymath. raymath is `static inline` in
|
|
;;;; raymath.h, so Vector2Subtract and Vector2Angle have no symbol in
|
|
;;;; libraylib at all and there is nothing for declare-c to bind. NEXT.md
|
|
;;;; names this as one of the three open gaps.
|
|
;;;;
|
|
;;;; What that costs here is nothing, because the C does not use raymath
|
|
;;;; either — it writes the four subtractions out and calls atan2f, cosf and
|
|
;;;; sinf from libm, and Flan's prelude already declares all three as
|
|
;;;; atan2-f32, cos-f32 and sin-f32. So this file is the *measurement* of the
|
|
;;;; gap rather than a workaround for it: an example whose every line is
|
|
;;;; vector maths, written without a vector library, in the same shape the C
|
|
;;;; has. It reads fine. A Vector2Add would have saved two lines in the whole
|
|
;;;; file.
|
|
;;;;
|
|
;;;; That measurement is what decided the answer, and the answer landed:
|
|
;;;; vendor/raylib/vector.flan is raymath written in Flan, since a C shim
|
|
;;;; re-exporting the inlines would have bought identical arithmetic at the
|
|
;;;; price of a compilation unit and a second place raylib's semantics live.
|
|
;;;; The measurement is left standing rather than rewritten away — one
|
|
;;;; subtraction below is `rl/v2-sub` and the rest of the file is unchanged,
|
|
;;;; including the atan2/cos/sin path, which is deliberate: it needs no guard
|
|
;;;; for a zero-length vector where a normalise would. One line saved, in a
|
|
;;;; file that is nothing but vector maths, is the honest size of the gap.
|
|
;;;;
|
|
;;;; The raylib call it does exercise is collision-point-circle?, which no
|
|
;;;; ported example had called, on a frame path, with the point coming
|
|
;;;; straight out of get-mouse-position — one struct out of raylib and back
|
|
;;;; into it untouched.
|
|
;;;;
|
|
;;;; The eye geometry, since the C hides it in a subtraction: the iris is
|
|
;;;; pinned inside a circle of (sclera - iris) radius about the sclera's
|
|
;;;; centre, so the iris disc is tangent to the sclera's edge at the extreme
|
|
;;;; rather than hanging over it.
|
|
|
|
(import rl "vendor:raylib")
|
|
|
|
(defconst screen-width 800)
|
|
(defconst screen-height 450)
|
|
|
|
(defconst sclera-radius f32 80.0)
|
|
(defconst iris-radius f32 24.0)
|
|
|
|
(defonce sclera-left rl/Vector2)
|
|
(defonce sclera-right rl/Vector2)
|
|
(defonce iris-left rl/Vector2)
|
|
(defonce iris-right rl/Vector2)
|
|
|
|
;; One eye's pupil, given where the mouse is and where the eye is. Answers the
|
|
;; mouse position unchanged while it is inside the eye, and the point on the
|
|
;; limit circle in that direction when it is outside.
|
|
;;
|
|
;; The C repeats this block twice with different variables; it is one function
|
|
;; here because the two copies are identical and a difference between them
|
|
;; would be invisible.
|
|
(defn track [mouse rl/Vector2 centre rl/Vector2] rl/Vector2
|
|
(let [limit (- sclera-radius iris-radius)]
|
|
(if (rl/collision-point-circle? mouse centre limit)
|
|
mouse
|
|
;; Outside: the direction from the eye to the mouse, and then `limit`
|
|
;; units back out along it. atan2 then cos/sin rather than a normalise,
|
|
;; because that is what the C does and because it needs no guard for a
|
|
;; zero-length vector — atan2(0,0) is 0 and the pupil sits at the right
|
|
;; of the eye, which is unreachable anyway since (0,0) is inside.
|
|
(let [d (rl/v2-sub mouse centre)
|
|
angle (atan2-f32 (.y d) (.x d))]
|
|
(rl/Vector2 {.x (+ (.x centre) (* limit (cos-f32 angle)))
|
|
.y (+ (.y centre) (* limit (sin-f32 angle)))})))))
|
|
|
|
(defn main [] ()
|
|
(rl/init-window screen-width screen-height
|
|
"raylib [shapes] example - following eyes")
|
|
(defer (rl/close-window))
|
|
|
|
;; The C reads these off get-screen-width/get-screen-height after the window
|
|
;; is open rather than off the constants, and so does this: on a high-DPI
|
|
;; display the two can differ.
|
|
(set sclera-left (rl/Vector2 {.x (- (/ (f32 (rl/get-screen-width)) 2.0) 100.0)
|
|
.y (/ (f32 (rl/get-screen-height)) 2.0)}))
|
|
(set sclera-right (rl/Vector2 {.x (+ (/ (f32 (rl/get-screen-width)) 2.0) 100.0)
|
|
.y (/ (f32 (rl/get-screen-height)) 2.0)}))
|
|
(set iris-left sclera-left)
|
|
(set iris-right sclera-right)
|
|
|
|
(rl/set-target-fps 60)
|
|
|
|
(until (rl/window-should-close?)
|
|
;; Update
|
|
(let [mouse (rl/get-mouse-position)]
|
|
(set iris-left (track mouse sclera-left))
|
|
(set iris-right (track mouse sclera-right)))
|
|
|
|
;; Draw
|
|
(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)
|
|
|
|
(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))))
|