flan/examples/shapes-collision-area.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

130 lines
5.3 KiB
Plaintext

;;;; raylib [shapes] example - collision area
;;;;
;;;; examples/shapes/shapes_collision_area.c. Picked for one call:
;;;; get-collision-rec, which is the only binding in the package that takes
;;;; two Rectangles and answers a third. Everything else in the corpus that
;;;; crosses a struct either passes one in (Camera2D, Color) or gets one back
;;;; (get-mouse-position, fade) — this is the first per-frame call doing both
;;;; at once, and the returned struct comes back through the shim's `out`
;;;; pointer rather than in registers, which is a different lowering again.
;;;;
;;;; It is also the only headless-adjacent thing in the file: both
;;;; collision-recs? and get-collision-rec are pure arithmetic over their
;;;; arguments and need no window at all. The acceptance table already asserts
;;;; the pair; what this adds is the same two calls on a frame path with one
;;;; rectangle driven by the mouse, where a permuted Rectangle would put the
;;;; green overlap patch somewhere the two boxes are not.
;;;;
;;;; Both TextFormats go through examples/digits.flan the way the core ports
;;;; do it: draw the literal part, then the number at x plus its width.
(import rl "vendor:raylib")
(import d "digits.flan")
(defconst screen-width 800)
(defconst screen-height 450)
;; The top strip the collision message is drawn into, and the floor box B is
;; not allowed above.
(defconst screen-upper-limit 40)
(defvar box-a rl/Rectangle) ; moves by itself, bounces off the sides
(defvar box-b rl/Rectangle) ; follows the mouse
(defvar box-collision rl/Rectangle) ; their overlap, valid only while touching
(defvar box-a-speed-x i32)
(defvar paused bool)
(defvar collision bool)
(defn main [] ()
(rl/init-window screen-width screen-height
"raylib [shapes] example - collision area")
(defer (rl/close-window))
(set box-a (rl/Rectangle {.x 10.0
.y (- (/ (f32 screen-height) 2.0) 50.0)
.width 200.0 .height 100.0}))
(set box-a-speed-x 4)
(set box-b (rl/Rectangle {.x (- (/ (f32 screen-width) 2.0) 30.0)
.y (- (/ (f32 screen-height) 2.0) 30.0)
.width 60.0 .height 60.0}))
;; A fresh Rectangle is four zeroes, which is the C's `= { 0 }`. It is never
;; drawn before the first collision sets it.
(set box-collision (rl/Rectangle {.x 0.0 .y 0.0 .width 0.0 .height 0.0}))
(set paused false)
(set collision false)
(rl/set-target-fps 60)
(until (rl/window-should-close?)
;; Update
(when (not paused)
(set (.x box-a) (+ (.x box-a) (f32 box-a-speed-x))))
;; Bounce at either edge. The test is on the far edge going right and on
;; the near one going left, so the box reverses on whichever it reaches.
(when (or (>= (+ (.x box-a) (.width box-a)) (f32 (rl/get-screen-width)))
(<= (.x box-a) 0.0))
(set box-a-speed-x (* box-a-speed-x -1)))
;; Box B is centred on the mouse and then clamped back inside the play
;; area — which is the window minus the top strip.
(set (.x box-b) (- (f32 (rl/get-mouse-x)) (/ (.width box-b) 2.0)))
(set (.y box-b) (- (f32 (rl/get-mouse-y)) (/ (.height box-b) 2.0)))
(if (>= (+ (.x box-b) (.width box-b)) (f32 (rl/get-screen-width)))
(set (.x box-b) (- (f32 (rl/get-screen-width)) (.width box-b)))
(when (<= (.x box-b) 0.0) (set (.x box-b) 0.0)))
(if (>= (+ (.y box-b) (.height box-b)) (f32 (rl/get-screen-height)))
(set (.y box-b) (- (f32 (rl/get-screen-height)) (.height box-b)))
(when (<= (.y box-b) (f32 screen-upper-limit))
(set (.y box-b) (f32 screen-upper-limit))))
(set collision (rl/collision-recs? box-a box-b))
;; Only meaningful while they touch: raylib answers a zero rectangle for
;; two boxes that do not, and the C leaves the previous one in place
;; rather than reading that back. This follows it.
(when collision (set box-collision (rl/get-collision-rec box-a box-b)))
(when (rl/key-pressed? :space) (set paused (not paused)))
;; Draw
(rl/begin-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-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)
(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)))
(rl/draw-text "Press SPACE to PAUSE/RESUME" 20 (- screen-height 35) 20
rl/lightgray)
(rl/draw-fps 10 10)
(rl/end-drawing)))