begin-mode-2d and end-mode-2d have been bound since Camera2D went in and called by nothing, which is the same as not having bound them. The grid now draws through a camera the arrow keys pan and comma and period zoom, and paint has to undo that transform with get-screen-to-world-2d — so a camera plumbed in wrongly is visible at once as grains landing somewhere other than the cursor, rather than as nothing at all. The shapes, the text and the timing come with it, and none of them can be asserted: every one needs a GL context, and measure-text needs init-window too — the default font is loaded there and nowhere else, so headless it answers 0 for every string. Measured against libraylib.so.550, not assumed, which is why it is absent from the acceptance table despite looking exactly like a call that belongs in it. So the HUD is built to be looked at instead: each shape binding appears once and each is asymmetric enough that crossed arguments show. The ellipse is wider than it is tall, the ring's sweep comes from get-time, the triangle has its counter-clockwise winding with an outline over it as a control, and the panel is sized by measure-text rather than by a guess. draw-rectangle-rounded-lines takes no thickness in raylib 5.5 — it moved to the -ex form, and both are here. The 5.1 header on this machine still shows the five-argument version; nm -D on the library is what settled it. Font loading stays unbound and says so: a Font carries a Texture2D, a Rectangle* and a GlyphInfo*, and a GlyphInfo carries an Image.
741 lines
31 KiB
Plaintext
741 lines
31 KiB
Plaintext
;;;; raylib, declared for Flan. The directory is the package (plan.org,
|
|
;;;; Modules), and (import rl "vendor:raylib") qualifies all of it as rl/…
|
|
;;;;
|
|
;;;; Nothing here names a raylib symbol. Every `declare` names a wrapper in
|
|
;;;; shim.c, and that is the whole design decision: raylib passes Color by
|
|
;;;; value and returns Vector2 by value, and how a small aggregate is passed
|
|
;;;; differs between x86-64, arm64 and wasm32. Written in C, clang classifies
|
|
;;;; each one correctly per target for free; written in emit.ml it would be
|
|
;;;; three calling conventions to reimplement and then maintain forever. This
|
|
;;;; is "one narrow host ABI, implemented twice" (plan.org, Targets), and it
|
|
;;;; is why the checker rejects an aggregate in a `declare` signature at all.
|
|
;;;;
|
|
;;;; So each struct that crosses does it through a pointer, and the `-raw`
|
|
;;;; declaration is wrapped by an ordinary Flan function just below it. The
|
|
;;;; `-raw` names are visible as rl/…-raw because there is no visibility rule
|
|
;;;; yet; they are not meant to be called.
|
|
|
|
;; Layouts are C's — no object headers anywhere — so these are exactly
|
|
;; raylib's structs and nothing marshals.
|
|
(defstruct Vector2 [x f32 y f32])
|
|
(defstruct Color [r u8 g u8 b u8 a u8])
|
|
|
|
;; Texture2D is five 4-byte fields in a row, which is the layout most likely
|
|
;; to be silently wrong: permute two of them and every field still reads as a
|
|
;; plausible number. Rectangle is four floats in x/y/width/height order.
|
|
(defstruct Texture2D [id u32 width i32 height i32 mipmaps i32 format i32])
|
|
(defstruct Rectangle [x f32 y f32 width f32 height f32])
|
|
|
|
;; KeyboardKey, the subset sand.flan uses. A keyword at a call site resolves
|
|
;; against these members at compile time and a typo is an error there.
|
|
(defenum Key
|
|
[space 32 apostrophe 39 comma 44 minus 45 period 46 slash 47
|
|
zero 48 one 49 two 50 three 51 four 52
|
|
five 53 six 54 seven 55 eight 56 nine 57
|
|
a 65 b 66 c 67 d 68 e 69 f 70 g 71 h 72 i 73
|
|
j 74 k 75 l 76 m 77 n 78 o 79 p 80 q 81 r 82
|
|
s 83 t 84 u 85 v 86 w 87 x 88 y 89 z 90
|
|
escape 256 enter 257 tab 258 backspace 259
|
|
right 262 left 263 down 264 up 265])
|
|
|
|
(defenum MouseButton
|
|
[left 0 right 1 middle 2 side 3 extra 4 forward 5 back 6])
|
|
|
|
(defenum TraceLogLevel
|
|
[all 0 trace 1 debug 2 info 3 warning 4 error 5 fatal 6 none 7])
|
|
|
|
;; ── Window ──────────────────────────────────────────────────────────
|
|
|
|
(declare init-window [width i32 height i32 title string] "flan_rl_init_window")
|
|
(declare close-window [] "flan_rl_close_window")
|
|
(declare window-should-close? [] bool "flan_rl_window_should_close")
|
|
(declare set-target-fps [fps i32] "flan_rl_set_target_fps")
|
|
(declare set-trace-log-level [level TraceLogLevel] "flan_rl_set_trace_log_level")
|
|
|
|
;; ── Input ───────────────────────────────────────────────────────────
|
|
|
|
(declare key-pressed? [key Key] bool "flan_rl_is_key_pressed")
|
|
(declare key-down? [key Key] bool "flan_rl_is_key_down")
|
|
(declare key-released? [key Key] bool "flan_rl_is_key_released")
|
|
|
|
(declare mouse-button-pressed? [button MouseButton] bool
|
|
"flan_rl_is_mouse_button_pressed")
|
|
(declare mouse-button-down? [button MouseButton] bool
|
|
"flan_rl_is_mouse_button_down")
|
|
(declare mouse-button-released? [button MouseButton] bool
|
|
"flan_rl_is_mouse_button_released")
|
|
|
|
(declare get-mouse-position-raw [out (Ptr Vector2)] "flan_rl_get_mouse_position")
|
|
|
|
(defn get-mouse-position [] Vector2
|
|
(let [v (Vector2 {})]
|
|
(get-mouse-position-raw (addr v))
|
|
v))
|
|
|
|
;; ── Colours ─────────────────────────────────────────────────────────
|
|
;;
|
|
;; A Color is four bytes in RGBA order, so it is *not* the little-endian
|
|
;; reading of the packed 0xRRGGBBAA integer — that is why get-color is a real
|
|
;; call and not a reinterpretation.
|
|
|
|
(declare get-color-raw [hex u32 out (Ptr Color)] "flan_rl_get_color")
|
|
|
|
(defn get-color [hex u32] Color
|
|
(let [c (Color {})]
|
|
(get-color-raw hex (addr c))
|
|
c))
|
|
|
|
(defconst black (Color {:r 0 :g 0 :b 0 :a 255}))
|
|
(defconst white (Color {:r 255 :g 255 :b 255 :a 255}))
|
|
|
|
;; ── Drawing ─────────────────────────────────────────────────────────
|
|
|
|
(declare begin-drawing [] "flan_rl_begin_drawing")
|
|
(declare end-drawing [] "flan_rl_end_drawing")
|
|
(declare draw-fps [x i32 y i32] "flan_rl_draw_fps")
|
|
|
|
(declare clear-background-raw [color (Ptr Color)] "flan_rl_clear_background")
|
|
|
|
(defn clear-background [color Color]
|
|
;; The copy is not ceremony: a parameter is not an assignable place
|
|
;; (spec-memory.md), so there is no address to take without one.
|
|
(let [c color]
|
|
(clear-background-raw (addr c))))
|
|
|
|
(declare draw-rectangle-raw
|
|
[x i32 y i32 width i32 height i32 color (Ptr Color)]
|
|
"flan_rl_draw_rectangle")
|
|
|
|
(defn draw-rectangle [x i32 y i32 width i32 height i32 color Color]
|
|
(let [c color]
|
|
(draw-rectangle-raw x y width height (addr c))))
|
|
|
|
;; ── Shapes texture ──────────────────────────────────────────────────
|
|
;;
|
|
;; raylib draws every shape from one atlas texture, and this pair sets and
|
|
;; reads it. It is bound here for a second reason: it is the only part of the
|
|
;; API that stores a Texture2D and a Rectangle and hands them back without
|
|
;; touching the GPU, so it is how the acceptance table checks both layouts
|
|
;; headlessly. Everything else that takes a texture needs a GL context.
|
|
;;
|
|
;; raylib substitutes a default ({1,1,1,1,7} / {0,0,1,1}) when the id or the
|
|
;; source's width or height is not positive, so a caller — and the test —
|
|
;; should keep clear of those values if it wants its own back.
|
|
|
|
(declare set-shapes-texture-raw
|
|
[texture (Ptr Texture2D) source (Ptr Rectangle)]
|
|
"flan_rl_set_shapes_texture")
|
|
|
|
(defn set-shapes-texture [texture Texture2D source Rectangle]
|
|
(let [t texture
|
|
r source]
|
|
(set-shapes-texture-raw (addr t) (addr r))))
|
|
|
|
(declare get-shapes-texture-raw [out (Ptr Texture2D)] "flan_rl_get_shapes_texture")
|
|
|
|
(defn get-shapes-texture [] Texture2D
|
|
(let [t (Texture2D {})]
|
|
(get-shapes-texture-raw (addr t))
|
|
t))
|
|
|
|
(declare get-shapes-texture-rectangle-raw
|
|
[out (Ptr Rectangle)] "flan_rl_get_shapes_texture_rectangle")
|
|
|
|
(defn get-shapes-texture-rectangle [] Rectangle
|
|
(let [r (Rectangle {})]
|
|
(get-shapes-texture-rectangle-raw (addr r))
|
|
r))
|
|
|
|
;; ── Camera2D ────────────────────────────────────────────────────────
|
|
;;
|
|
;; The 2D camera: everything drawn between begin-mode-2d and end-mode-2d is
|
|
;; transformed by it. `offset` is where the camera's target lands on screen —
|
|
;; half the window size is what centres it — `target` is the world point that
|
|
;; goes there, and rotation is in degrees.
|
|
;;
|
|
;; `zoom` of 0 makes the transform singular and both conversions below hand
|
|
;; back NaN rather than failing. raylib does not guard it and neither does
|
|
;; this; 1.0 is the identity and a fresh (Camera2D {}) is therefore NOT usable
|
|
;; as one — it has to be given a zoom.
|
|
|
|
(defstruct Camera2D [offset Vector2 target Vector2 rotation f32 zoom f32])
|
|
|
|
(declare begin-mode-2d-raw [camera (Ptr Camera2D)] "flan_rl_begin_mode_2d")
|
|
|
|
(defn begin-mode-2d [camera Camera2D]
|
|
(let [c camera]
|
|
(begin-mode-2d-raw (addr c))))
|
|
|
|
(declare end-mode-2d [] "flan_rl_end_mode_2d")
|
|
|
|
;; The two conversions are pure arithmetic over every field of the camera, so
|
|
;; unlike the rest of the camera they run with no window and no GL context.
|
|
;; That is what the acceptance table uses to pin Camera2D's layout, and — via
|
|
;; a rotated camera, which is the only call here that mixes x into y — it is
|
|
;; also the only thing that pins Vector2's two fields against each other.
|
|
|
|
(declare get-screen-to-world-2d-raw
|
|
[position (Ptr Vector2) camera (Ptr Camera2D) out (Ptr Vector2)]
|
|
"flan_rl_get_screen_to_world_2d")
|
|
|
|
(defn get-screen-to-world-2d [position Vector2 camera Camera2D] Vector2
|
|
(let [p position
|
|
c camera
|
|
out (Vector2 {})]
|
|
(get-screen-to-world-2d-raw (addr p) (addr c) (addr out))
|
|
out))
|
|
|
|
(declare get-world-to-screen-2d-raw
|
|
[position (Ptr Vector2) camera (Ptr Camera2D) out (Ptr Vector2)]
|
|
"flan_rl_get_world_to_screen_2d")
|
|
|
|
(defn get-world-to-screen-2d [position Vector2 camera Camera2D] Vector2
|
|
(let [p position
|
|
c camera
|
|
out (Vector2 {})]
|
|
(get-world-to-screen-2d-raw (addr p) (addr c) (addr out))
|
|
out))
|
|
|
|
;; ── Shapes ──────────────────────────────────────────────────────────
|
|
;;
|
|
;; Rectangle intersection, which raylib computes from all four fields in
|
|
;; different ways. It is the one Rectangle call that needs no GPU, so it is
|
|
;; also how the acceptance table pins the layout: a store-and-return check is
|
|
;; symmetric and a permuted layout survives it untouched.
|
|
|
|
(declare get-collision-rec-raw
|
|
[a (Ptr Rectangle) b (Ptr Rectangle) out (Ptr Rectangle)]
|
|
"flan_rl_get_collision_rec")
|
|
|
|
(defn get-collision-rec [a Rectangle b Rectangle] Rectangle
|
|
(let [x a
|
|
y b
|
|
out (Rectangle {})]
|
|
(get-collision-rec-raw (addr x) (addr y) (addr out))
|
|
out))
|
|
|
|
;; ── Collision ───────────────────────────────────────────────────────
|
|
;;
|
|
;; All of these are pure geometry: no window, no GL context, no state. That
|
|
;; makes them the other half of what the acceptance table can assert, and the
|
|
;; only part of the 2D surface that is tested as thoroughly as it is bound.
|
|
;;
|
|
;; Each takes its aggregates through pointers for the usual reason, and each
|
|
;; wrapper copies its parameters into locals first — a parameter is not an
|
|
;; assignable place (spec-memory.md), so there is no address to take.
|
|
|
|
(declare collision-recs?-raw [a (Ptr Rectangle) b (Ptr Rectangle)] bool
|
|
"flan_rl_check_collision_recs")
|
|
|
|
(defn collision-recs? [a Rectangle b Rectangle] bool
|
|
(let [x a y b]
|
|
(collision-recs?-raw (addr x) (addr y))))
|
|
|
|
(declare collision-circles?-raw
|
|
[c1 (Ptr Vector2) r1 f32 c2 (Ptr Vector2) r2 f32] bool
|
|
"flan_rl_check_collision_circles")
|
|
|
|
(defn collision-circles? [c1 Vector2 r1 f32 c2 Vector2 r2 f32] bool
|
|
(let [a c1 b c2]
|
|
(collision-circles?-raw (addr a) r1 (addr b) r2)))
|
|
|
|
(declare collision-circle-rec?-raw
|
|
[center (Ptr Vector2) radius f32 rec (Ptr Rectangle)] bool
|
|
"flan_rl_check_collision_circle_rec")
|
|
|
|
(defn collision-circle-rec? [center Vector2 radius f32 rec Rectangle] bool
|
|
(let [c center r rec]
|
|
(collision-circle-rec?-raw (addr c) radius (addr r))))
|
|
|
|
(declare collision-circle-line?-raw
|
|
[center (Ptr Vector2) radius f32 p1 (Ptr Vector2) p2 (Ptr Vector2)] bool
|
|
"flan_rl_check_collision_circle_line")
|
|
|
|
(defn collision-circle-line? [center Vector2 radius f32
|
|
p1 Vector2 p2 Vector2] bool
|
|
(let [c center a p1 b p2]
|
|
(collision-circle-line?-raw (addr c) radius (addr a) (addr b))))
|
|
|
|
(declare collision-point-rec?-raw [point (Ptr Vector2) rec (Ptr Rectangle)] bool
|
|
"flan_rl_check_collision_point_rec")
|
|
|
|
(defn collision-point-rec? [point Vector2 rec Rectangle] bool
|
|
(let [p point r rec]
|
|
(collision-point-rec?-raw (addr p) (addr r))))
|
|
|
|
(declare collision-point-circle?-raw
|
|
[point (Ptr Vector2) center (Ptr Vector2) radius f32] bool
|
|
"flan_rl_check_collision_point_circle")
|
|
|
|
(defn collision-point-circle? [point Vector2 center Vector2 radius f32] bool
|
|
(let [p point c center]
|
|
(collision-point-circle?-raw (addr p) (addr c) radius)))
|
|
|
|
(declare collision-point-triangle?-raw
|
|
[point (Ptr Vector2) a (Ptr Vector2) b (Ptr Vector2) c (Ptr Vector2)] bool
|
|
"flan_rl_check_collision_point_triangle")
|
|
|
|
(defn collision-point-triangle? [point Vector2 a Vector2 b Vector2
|
|
c Vector2] bool
|
|
(let [p point x a y b z c]
|
|
(collision-point-triangle?-raw (addr p) (addr x) (addr y) (addr z))))
|
|
|
|
;; `threshold` is in pixels, and it is not optional in practice: raylib's test
|
|
;; is a distance comparison in floats, so a point exactly on the line fails at
|
|
;; a threshold of 0. 1 is the useful smallest value.
|
|
(declare collision-point-line?-raw
|
|
[point (Ptr Vector2) p1 (Ptr Vector2) p2 (Ptr Vector2) threshold i32] bool
|
|
"flan_rl_check_collision_point_line")
|
|
|
|
(defn collision-point-line? [point Vector2 p1 Vector2 p2 Vector2
|
|
threshold i32] bool
|
|
(let [p point a p1 b p2]
|
|
(collision-point-line?-raw (addr p) (addr a) (addr b) threshold)))
|
|
|
|
;; A slice crosses as ptr+len, which is exactly what raylib wants here, so
|
|
;; this is the one collision call that needs no per-element copying. The
|
|
;; polygon is not closed explicitly — raylib joins the last point to the
|
|
;; first.
|
|
(declare collision-point-poly?-raw [point (Ptr Vector2) points [Vector2]] bool
|
|
"flan_rl_check_collision_point_poly")
|
|
|
|
(defn collision-point-poly? [point Vector2 points [Vector2]] bool
|
|
(let [p point]
|
|
(collision-point-poly?-raw (addr p) points)))
|
|
|
|
;; The one that answers with more than yes or no: where the two segments meet.
|
|
;; None is "they do not", so the point cannot be read when there isn't one —
|
|
;; raylib's own signature leaves the out-parameter untouched in that case and
|
|
;; a caller that forgets reads whatever was there.
|
|
(declare collision-lines-raw
|
|
[a1 (Ptr Vector2) a2 (Ptr Vector2) b1 (Ptr Vector2) b2 (Ptr Vector2)
|
|
out (Ptr Vector2)] bool
|
|
"flan_rl_check_collision_lines")
|
|
|
|
(defn collision-lines [a1 Vector2 a2 Vector2 b1 Vector2 b2 Vector2]
|
|
(Option Vector2)
|
|
(let [p a1 q a2 r b1 s b2
|
|
out (Vector2 {})]
|
|
(if (collision-lines-raw (addr p) (addr q) (addr r) (addr s) (addr out))
|
|
(Some out)
|
|
None)))
|
|
|
|
;; ── Textures ────────────────────────────────────────────────────────
|
|
;;
|
|
;; Everything here needs a GL context, so a window has to be open first —
|
|
;; load-texture before init-window returns an id of 0 and raylib says so on
|
|
;; the log. texture-valid? is how that is noticed in the program rather than
|
|
;; only in the log; raylib 5.5 spells it IsTextureValid, and IsTextureReady,
|
|
;; which older code calls, does not exist in this version.
|
|
|
|
(declare load-texture-raw [path string out (Ptr Texture2D)] "flan_rl_load_texture")
|
|
|
|
(defn load-texture [path string] Texture2D
|
|
(let [t (Texture2D {})]
|
|
(load-texture-raw path (addr t))
|
|
t))
|
|
|
|
(declare texture-valid?-raw [texture (Ptr Texture2D)] bool "flan_rl_is_texture_valid")
|
|
|
|
(defn texture-valid? [texture Texture2D] bool
|
|
(let [t texture]
|
|
(texture-valid?-raw (addr t))))
|
|
|
|
(declare unload-texture-raw [texture (Ptr Texture2D)] "flan_rl_unload_texture")
|
|
|
|
(defn unload-texture [texture Texture2D]
|
|
(let [t texture]
|
|
(unload-texture-raw (addr t))))
|
|
|
|
(declare draw-texture-raw
|
|
[texture (Ptr Texture2D) x i32 y i32 tint (Ptr Color)]
|
|
"flan_rl_draw_texture")
|
|
|
|
(defn draw-texture [texture Texture2D x i32 y i32 tint Color]
|
|
(let [t texture
|
|
c tint]
|
|
(draw-texture-raw (addr t) x y (addr c))))
|
|
|
|
(declare draw-texture-v-raw
|
|
[texture (Ptr Texture2D) position (Ptr Vector2) tint (Ptr Color)]
|
|
"flan_rl_draw_texture_v")
|
|
|
|
(defn draw-texture-v [texture Texture2D position Vector2 tint Color]
|
|
(let [t texture
|
|
p position
|
|
c tint]
|
|
(draw-texture-v-raw (addr t) (addr p) (addr c))))
|
|
|
|
(declare draw-texture-ex-raw
|
|
[texture (Ptr Texture2D) position (Ptr Vector2) rotation f32 scale f32
|
|
tint (Ptr Color)]
|
|
"flan_rl_draw_texture_ex")
|
|
|
|
(defn draw-texture-ex [texture Texture2D position Vector2 rotation f32
|
|
scale f32 tint Color]
|
|
(let [t texture
|
|
p position
|
|
c tint]
|
|
(draw-texture-ex-raw (addr t) (addr p) rotation scale (addr c))))
|
|
|
|
;; A negative source width or height flips the sprite, which is how a sheet is
|
|
;; drawn facing the other way without a second image.
|
|
(declare draw-texture-rec-raw
|
|
[texture (Ptr Texture2D) source (Ptr Rectangle) position (Ptr Vector2)
|
|
tint (Ptr Color)]
|
|
"flan_rl_draw_texture_rec")
|
|
|
|
(defn draw-texture-rec [texture Texture2D source Rectangle position Vector2
|
|
tint Color]
|
|
(let [t texture
|
|
s source
|
|
p position
|
|
c tint]
|
|
(draw-texture-rec-raw (addr t) (addr s) (addr p) (addr c))))
|
|
|
|
;; ── Images ──────────────────────────────────────────────────────────
|
|
;;
|
|
;; An Image is pixels in RAM. Nothing here touches the GPU, which makes it the
|
|
;; one corner of the 2D surface a headless test can assert properly — raylib
|
|
;; *computes* with these, and a wrong answer is a wrong number rather than the
|
|
;; struct handed back unchanged.
|
|
;;
|
|
;; `data` is raylib's buffer and Flan never reads through it; it is here so
|
|
;; the struct is the right size and the four ints that follow are at the right
|
|
;; offsets. `format` is a PixelFormat code — GenImageColor makes 7, which is
|
|
;; uncompressed R8G8B8A8, one byte per channel.
|
|
;;
|
|
;; The split between by-value and by-pointer here is raylib's own and worth
|
|
;; keeping: a call that *mutates* the image takes (Ptr Image) at the Flan
|
|
;; level too, so a caller can see which ones change what they are given.
|
|
(defstruct Image [data (Ptr u8) width i32 height i32 mipmaps i32 format i32])
|
|
|
|
(declare load-image-raw [path string out (Ptr Image)] "flan_rl_load_image")
|
|
|
|
(defn load-image [path string] Image
|
|
(let [i (Image {})]
|
|
(load-image-raw path (addr i))
|
|
i))
|
|
|
|
;; raylib 5.5 spells this IsImageValid. IsImageReady, which older code calls,
|
|
;; does not exist here — the same rename that took IsTextureReady.
|
|
(declare image-valid?-raw [image (Ptr Image)] bool "flan_rl_is_image_valid")
|
|
|
|
(defn image-valid? [image Image] bool
|
|
(let [i image]
|
|
(image-valid?-raw (addr i))))
|
|
|
|
;; By value, as raylib has it. The caller's copy is dangling afterwards —
|
|
;; `data` pointed at the buffer this just freed — so an Image is used or
|
|
;; unloaded, never both.
|
|
(declare unload-image-raw [image (Ptr Image)] "flan_rl_unload_image")
|
|
|
|
(defn unload-image [image Image]
|
|
(let [i image]
|
|
(unload-image-raw (addr i))))
|
|
|
|
;; The format is taken from the path's extension, so ".png" writes a PNG.
|
|
;; False means it could not be written.
|
|
(declare export-image-raw [image (Ptr Image) path string] bool
|
|
"flan_rl_export_image")
|
|
|
|
(defn export-image [image Image path string] bool
|
|
(let [i image]
|
|
(export-image-raw (addr i) path)))
|
|
|
|
(declare gen-image-color-raw
|
|
[width i32 height i32 color (Ptr Color) out (Ptr Image)]
|
|
"flan_rl_gen_image_color")
|
|
|
|
(defn gen-image-color [width i32 height i32 color Color] Image
|
|
(let [c color
|
|
i (Image {})]
|
|
(gen-image-color-raw width height (addr c) (addr i))
|
|
i))
|
|
|
|
;; Bicubic, so the pixels that come out are interpolated and only the new
|
|
;; width and height are exactly predictable. image-resize-nn is the
|
|
;; nearest-neighbour one, and it is the one to reach for when the colours
|
|
;; have to survive.
|
|
(declare image-resize [image (Ptr Image) width i32 height i32]
|
|
"flan_rl_image_resize")
|
|
(declare image-resize-nn [image (Ptr Image) width i32 height i32]
|
|
"flan_rl_image_resize_nn")
|
|
|
|
(declare image-crop-raw [image (Ptr Image) crop (Ptr Rectangle)]
|
|
"flan_rl_image_crop")
|
|
|
|
(defn image-crop [image (Ptr Image) crop Rectangle]
|
|
(let [r crop]
|
|
(image-crop-raw image (addr r))))
|
|
|
|
(declare image-flip-horizontal [image (Ptr Image)]
|
|
"flan_rl_image_flip_horizontal")
|
|
(declare image-flip-vertical [image (Ptr Image)]
|
|
"flan_rl_image_flip_vertical")
|
|
|
|
(declare image-draw-pixel-raw
|
|
[image (Ptr Image) x i32 y i32 color (Ptr Color)]
|
|
"flan_rl_image_draw_pixel")
|
|
|
|
(defn image-draw-pixel [image (Ptr Image) x i32 y i32 color Color]
|
|
(let [c color]
|
|
(image-draw-pixel-raw image x y (addr c))))
|
|
|
|
;; Out of bounds is not an error: raylib logs a warning and hands back a
|
|
;; transparent black, so a caller that is off by one gets zeroes rather than
|
|
;; somebody else's memory.
|
|
(declare get-image-color-raw
|
|
[image (Ptr Image) x i32 y i32 out (Ptr Color)]
|
|
"flan_rl_get_image_color")
|
|
|
|
(defn get-image-color [image Image x i32 y i32] Color
|
|
(let [i image
|
|
c (Color {})]
|
|
(get-image-color-raw (addr i) x y (addr c))
|
|
c))
|
|
|
|
;; The one call in this section that does need a GL context — it uploads. An
|
|
;; image loaded and edited on the CPU becomes something draw-texture can use.
|
|
(declare load-texture-from-image-raw [image (Ptr Image) out (Ptr Texture2D)]
|
|
"flan_rl_load_texture_from_image")
|
|
|
|
(defn load-texture-from-image [image Image] Texture2D
|
|
(let [i image
|
|
t (Texture2D {})]
|
|
(load-texture-from-image-raw (addr i) (addr t))
|
|
t))
|
|
|
|
;; ── Shapes ──────────────────────────────────────────────────────────
|
|
;;
|
|
;; Immediate-mode drawing: each of these needs a GL context, so a window has
|
|
;; to be open and none of them can be in the acceptance table. They are
|
|
;; exercised by running sand.flan and looking at it, which is the honest
|
|
;; description — "it links" is not "it draws the right thing".
|
|
;;
|
|
;; raylib's own naming is kept: a plain name fills, `-lines` outlines, and a
|
|
;; `-v` suffix takes Vector2s where the plain form takes integers.
|
|
;;
|
|
;; The one signature worth calling out is draw-rectangle-rounded-lines, which
|
|
;; in raylib 5.5 has NO thickness — it moved to the `-ex` form. The 5.1 header
|
|
;; still shows the five-argument version, and getting it wrong links cleanly
|
|
;; and draws nonsense, so this was read off the library with nm rather than
|
|
;; remembered.
|
|
|
|
(declare draw-pixel-raw [x i32 y i32 color (Ptr Color)] "flan_rl_draw_pixel")
|
|
|
|
(defn draw-pixel [x i32 y i32 color Color]
|
|
(let [c color] (draw-pixel-raw x y (addr c))))
|
|
|
|
(declare draw-pixel-v-raw [position (Ptr Vector2) color (Ptr Color)]
|
|
"flan_rl_draw_pixel_v")
|
|
|
|
(defn draw-pixel-v [position Vector2 color Color]
|
|
(let [p position c color] (draw-pixel-v-raw (addr p) (addr c))))
|
|
|
|
(declare draw-line-raw [x1 i32 y1 i32 x2 i32 y2 i32 color (Ptr Color)]
|
|
"flan_rl_draw_line")
|
|
|
|
(defn draw-line [x1 i32 y1 i32 x2 i32 y2 i32 color Color]
|
|
(let [c color] (draw-line-raw x1 y1 x2 y2 (addr c))))
|
|
|
|
(declare draw-line-v-raw
|
|
[start (Ptr Vector2) end (Ptr Vector2) color (Ptr Color)]
|
|
"flan_rl_draw_line_v")
|
|
|
|
(defn draw-line-v [start Vector2 end Vector2 color Color]
|
|
(let [a start b end c color] (draw-line-v-raw (addr a) (addr b) (addr c))))
|
|
|
|
;; The thick one is built from triangles rather than GL lines, which is why it
|
|
;; is a separate call and not a parameter on the one above.
|
|
(declare draw-line-ex-raw
|
|
[start (Ptr Vector2) end (Ptr Vector2) thick f32 color (Ptr Color)]
|
|
"flan_rl_draw_line_ex")
|
|
|
|
(defn draw-line-ex [start Vector2 end Vector2 thick f32 color Color]
|
|
(let [a start b end c color]
|
|
(draw-line-ex-raw (addr a) (addr b) thick (addr c))))
|
|
|
|
(declare draw-circle-raw [x i32 y i32 radius f32 color (Ptr Color)]
|
|
"flan_rl_draw_circle")
|
|
|
|
(defn draw-circle [x i32 y i32 radius f32 color Color]
|
|
(let [c color] (draw-circle-raw x y radius (addr c))))
|
|
|
|
(declare draw-circle-v-raw
|
|
[center (Ptr Vector2) radius f32 color (Ptr Color)] "flan_rl_draw_circle_v")
|
|
|
|
(defn draw-circle-v [center Vector2 radius f32 color Color]
|
|
(let [p center c color] (draw-circle-v-raw (addr p) radius (addr c))))
|
|
|
|
(declare draw-circle-lines-raw [x i32 y i32 radius f32 color (Ptr Color)]
|
|
"flan_rl_draw_circle_lines")
|
|
|
|
(defn draw-circle-lines [x i32 y i32 radius f32 color Color]
|
|
(let [c color] (draw-circle-lines-raw x y radius (addr c))))
|
|
|
|
(declare draw-circle-lines-v-raw
|
|
[center (Ptr Vector2) radius f32 color (Ptr Color)]
|
|
"flan_rl_draw_circle_lines_v")
|
|
|
|
(defn draw-circle-lines-v [center Vector2 radius f32 color Color]
|
|
(let [p center c color] (draw-circle-lines-v-raw (addr p) radius (addr c))))
|
|
|
|
;; Two radii, horizontal then vertical. Equal radii is a circle, so a wrapper
|
|
;; that exchanged them would be invisible unless they differ — which is why
|
|
;; sand.flan's ellipse is deliberately wider than it is tall.
|
|
(declare draw-ellipse-raw
|
|
[x i32 y i32 radius-h f32 radius-v f32 color (Ptr Color)]
|
|
"flan_rl_draw_ellipse")
|
|
|
|
(defn draw-ellipse [x i32 y i32 radius-h f32 radius-v f32 color Color]
|
|
(let [c color] (draw-ellipse-raw x y radius-h radius-v (addr c))))
|
|
|
|
(declare draw-ellipse-lines-raw
|
|
[x i32 y i32 radius-h f32 radius-v f32 color (Ptr Color)]
|
|
"flan_rl_draw_ellipse_lines")
|
|
|
|
(defn draw-ellipse-lines [x i32 y i32 radius-h f32 radius-v f32 color Color]
|
|
(let [c color] (draw-ellipse-lines-raw x y radius-h radius-v (addr c))))
|
|
|
|
;; Angles are degrees, clockwise from the +x axis, and `segments` is how many
|
|
;; straight pieces the arc is made of — 0 lets raylib pick from the radius.
|
|
(declare draw-ring-raw
|
|
[center (Ptr Vector2) inner f32 outer f32 start f32 end f32
|
|
segments i32 color (Ptr Color)]
|
|
"flan_rl_draw_ring")
|
|
|
|
(defn draw-ring [center Vector2 inner f32 outer f32 start f32 end f32
|
|
segments i32 color Color]
|
|
(let [p center c color]
|
|
(draw-ring-raw (addr p) inner outer start end segments (addr c))))
|
|
|
|
(declare draw-ring-lines-raw
|
|
[center (Ptr Vector2) inner f32 outer f32 start f32 end f32
|
|
segments i32 color (Ptr Color)]
|
|
"flan_rl_draw_ring_lines")
|
|
|
|
(defn draw-ring-lines [center Vector2 inner f32 outer f32 start f32 end f32
|
|
segments i32 color Color]
|
|
(let [p center c color]
|
|
(draw-ring-lines-raw (addr p) inner outer start end segments (addr c))))
|
|
|
|
;; Counter-clockwise, and raylib means it: the clockwise winding is culled and
|
|
;; draws nothing at all, which looks exactly like a broken binding.
|
|
(declare draw-triangle-raw
|
|
[v1 (Ptr Vector2) v2 (Ptr Vector2) v3 (Ptr Vector2) color (Ptr Color)]
|
|
"flan_rl_draw_triangle")
|
|
|
|
(defn draw-triangle [v1 Vector2 v2 Vector2 v3 Vector2 color Color]
|
|
(let [a v1 b v2 d v3 c color]
|
|
(draw-triangle-raw (addr a) (addr b) (addr d) (addr c))))
|
|
|
|
(declare draw-triangle-lines-raw
|
|
[v1 (Ptr Vector2) v2 (Ptr Vector2) v3 (Ptr Vector2) color (Ptr Color)]
|
|
"flan_rl_draw_triangle_lines")
|
|
|
|
(defn draw-triangle-lines [v1 Vector2 v2 Vector2 v3 Vector2 color Color]
|
|
(let [a v1 b v2 d v3 c color]
|
|
(draw-triangle-lines-raw (addr a) (addr b) (addr d) (addr c))))
|
|
|
|
(declare draw-rectangle-v-raw
|
|
[position (Ptr Vector2) size (Ptr Vector2) color (Ptr Color)]
|
|
"flan_rl_draw_rectangle_v")
|
|
|
|
(defn draw-rectangle-v [position Vector2 size Vector2 color Color]
|
|
(let [p position s size c color]
|
|
(draw-rectangle-v-raw (addr p) (addr s) (addr c))))
|
|
|
|
(declare draw-rectangle-rec-raw [rec (Ptr Rectangle) color (Ptr Color)]
|
|
"flan_rl_draw_rectangle_rec")
|
|
|
|
(defn draw-rectangle-rec [rec Rectangle color Color]
|
|
(let [r rec c color] (draw-rectangle-rec-raw (addr r) (addr c))))
|
|
|
|
(declare draw-rectangle-lines-raw
|
|
[x i32 y i32 width i32 height i32 color (Ptr Color)]
|
|
"flan_rl_draw_rectangle_lines")
|
|
|
|
(defn draw-rectangle-lines [x i32 y i32 width i32 height i32 color Color]
|
|
(let [c color] (draw-rectangle-lines-raw x y width height (addr c))))
|
|
|
|
;; The one-pixel outline above is drawn with GL lines and sits *on* the
|
|
;; rectangle's edge; this one is drawn with quads and sits inside it, so the
|
|
;; two do not agree at thickness 1 and that is raylib's doing, not a bug here.
|
|
(declare draw-rectangle-lines-ex-raw
|
|
[rec (Ptr Rectangle) thick f32 color (Ptr Color)]
|
|
"flan_rl_draw_rectangle_lines_ex")
|
|
|
|
(defn draw-rectangle-lines-ex [rec Rectangle thick f32 color Color]
|
|
(let [r rec c color] (draw-rectangle-lines-ex-raw (addr r) thick (addr c))))
|
|
|
|
;; `roundness` is 0 to 1 as a fraction of the shorter side, so 0 is a plain
|
|
;; rectangle and 1 is a stadium.
|
|
(declare draw-rectangle-rounded-raw
|
|
[rec (Ptr Rectangle) roundness f32 segments i32 color (Ptr Color)]
|
|
"flan_rl_draw_rectangle_rounded")
|
|
|
|
(defn draw-rectangle-rounded [rec Rectangle roundness f32 segments i32
|
|
color Color]
|
|
(let [r rec c color]
|
|
(draw-rectangle-rounded-raw (addr r) roundness segments (addr c))))
|
|
|
|
;; No thickness here — see the section note. The `-ex` form below is the one
|
|
;; that takes it.
|
|
(declare draw-rectangle-rounded-lines-raw
|
|
[rec (Ptr Rectangle) roundness f32 segments i32 color (Ptr Color)]
|
|
"flan_rl_draw_rectangle_rounded_lines")
|
|
|
|
(defn draw-rectangle-rounded-lines [rec Rectangle roundness f32 segments i32
|
|
color Color]
|
|
(let [r rec c color]
|
|
(draw-rectangle-rounded-lines-raw (addr r) roundness segments (addr c))))
|
|
|
|
(declare draw-rectangle-rounded-lines-ex-raw
|
|
[rec (Ptr Rectangle) roundness f32 segments i32 thick f32
|
|
color (Ptr Color)]
|
|
"flan_rl_draw_rectangle_rounded_lines_ex")
|
|
|
|
(defn draw-rectangle-rounded-lines-ex [rec Rectangle roundness f32
|
|
segments i32 thick f32 color Color]
|
|
(let [r rec c color]
|
|
(draw-rectangle-rounded-lines-ex-raw (addr r) roundness segments thick
|
|
(addr c))))
|
|
|
|
;; ── Text ────────────────────────────────────────────────────────────
|
|
;;
|
|
;; Both of these use raylib's built-in font, and both therefore need
|
|
;; init-window — not for the GPU in measure-text's case, but because the
|
|
;; default font is only loaded as part of opening a window. Called headless,
|
|
;; measure-text answers 0 for every string, which was measured against
|
|
;; libraylib.so.550 and is why it is NOT in the acceptance table despite
|
|
;; looking like exactly the kind of call that could be.
|
|
;;
|
|
;; Font loading is not bound, deliberately. A Font is baseSize, glyphCount and
|
|
;; glyphPadding beside a Texture2D, a Rectangle* and a GlyphInfo* — and a
|
|
;; GlyphInfo embeds an Image. Binding it means binding two more aggregates and
|
|
;; two owned arrays for something with no headless test at the end of it, so
|
|
;; load-font, load-font-ex, unload-font, get-font-default, draw-text-ex and
|
|
;; measure-text-ex are all absent rather than half-done.
|
|
|
|
(declare draw-text-raw
|
|
[text string x i32 y i32 font-size i32 color (Ptr Color)]
|
|
"flan_rl_draw_text")
|
|
|
|
(defn draw-text [text string x i32 y i32 font-size i32 color Color]
|
|
(let [c color] (draw-text-raw text x y font-size (addr c))))
|
|
|
|
(declare measure-text [text string font-size i32] i32 "flan_rl_measure_text")
|
|
|
|
;; ── Timing and window state ─────────────────────────────────────────
|
|
;;
|
|
;; All four read state that init-window creates, so all four answer 0 before
|
|
;; there is a window — again measured, not assumed. get-frame-time is the
|
|
;; delta the last frame took, in seconds, which is what a simulation should
|
|
;; scale by instead of assuming the target fps was met.
|
|
|
|
(declare get-frame-time [] f32 "flan_rl_get_frame_time")
|
|
(declare get-time [] f64 "flan_rl_get_time")
|
|
(declare get-screen-width [] i32 "flan_rl_get_screen_width")
|
|
(declare get-screen-height [] i32 "flan_rl_get_screen_height")
|