From a90badbcd50872b20e3adf0691a19a2c0938ef00 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 11 Sep 2026 19:44:25 +0700 Subject: [PATCH] A camera nothing looked through was a camera nothing tested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- sand.flan | 182 ++++++++++++++++++++++++++++- vendor/raylib/raylib.flan | 233 ++++++++++++++++++++++++++++++++++++++ vendor/raylib/shim.c | 175 ++++++++++++++++++++++++++++ 3 files changed, 586 insertions(+), 4 deletions(-) diff --git a/sand.flan b/sand.flan index eb29636..1a14d49 100644 --- a/sand.flan +++ b/sand.flan @@ -62,9 +62,61 @@ (rl/get-color (nth sim/colors sim/current-color))) (rl/draw-texture-ex brush (rl/Vector2 {:x 72.0 :y 46.0}) 0.0 2.0 rl/white)))) +;; ── The view ──────────────────────────────────────────────────────── +;; +;; begin-mode-2d and end-mode-2d were bound along with Camera2D and then +;; called by nothing at all, which is the same as not having bound them. They +;; are load-bearing here now: the grid is drawn through this camera, the arrow +;; keys pan it, comma and period zoom, and `paint` has to undo the transform +;; with get-screen-to-world-2d or the sand lands somewhere other than the +;; cursor. That last part is what makes this a check rather than decoration — +;; a camera plumbed in wrongly shows up as grains appearing in the wrong +;; place, at once, while zoomed. +(defvar view rl/Camera2D) + +;; A fresh (Camera2D {}) has a zoom of 0, which is singular: both conversions +;; hand back NaN and nothing draws. 1.0 is the identity. +(defn reset-view [] + (set view (rl/Camera2D {:offset (rl/Vector2 {:x 0.0 :y 0.0}) + :target (rl/Vector2 {:x 0.0 :y 0.0}) + :rotation 0.0 + :zoom 1.0}))) + +(defn set-view [target-x f32 target-y f32 zoom f32] + (set view (rl/Camera2D {:offset (.offset view) + :target (rl/Vector2 {:x target-x :y target-y}) + :rotation (.rotation view) + :zoom zoom}))) + +;; Panning is world units per second and zooming is a factor per second, so +;; neither changes with the frame rate. That is the whole of what +;; get-frame-time is for, and a loop that assumed it hit its target fps would +;; be a loop that moves differently on a slower machine. +(defn move-view [] + (let [dt (rl/get-frame-time) + pan (* (f32 600.0) dt) + tx (.x (.target view)) + ty (.y (.target view)) + zoom (.zoom view)] + (when (rl/key-down? :left) (set tx (- tx pan))) + (when (rl/key-down? :right) (set tx (+ tx pan))) + (when (rl/key-down? :up) (set ty (- ty pan))) + (when (rl/key-down? :down) (set ty (+ ty pan))) + (when (rl/key-down? :comma) (set zoom (- zoom (* zoom dt)))) + (when (rl/key-down? :period) (set zoom (+ zoom (* zoom dt)))) + ;; Clamped away from 0 for the reason above, and away from the far end + ;; because a cell is 5 pixels and there is no point past a screenful of + ;; one of them. + (set-view tx ty (min (f32 8.0) (max (f32 0.125) zoom))) + (when (rl/key-pressed? :zero) (reset-view)))) + ;; Locals are assignable places (spec-memory.md); parameters are not. +;; +;; The mouse is in screen pixels and the grid is in world cells, and with a +;; camera in the way those stopped being the same thing — so this is the one +;; place get-screen-to-world-2d is not a test case but a requirement. (defn paint [] - (let [m (rl/get-mouse-position) + (let [m (rl/get-screen-to-world-2d (rl/get-mouse-position) view) row (/ (i32 (.y m)) sim/cell-size) col (/ (i32 (.x m)) sim/cell-size)] (sim/paint-at row col))) @@ -81,12 +133,12 @@ ;; reload rejects it. See plan.org "What redefinition cannot do". (defn game-update [] (when (rl/key-pressed? :r) (sim/clear-grid)) + (move-view) (when (rl/mouse-button-down? :left) (paint)) (when (rl/mouse-button-released? :left) (sim/next-color)) (sim/step)) -(defn game-draw [] - (rl/clear-background rl/black) +(defn draw-grid [] (dotimes [row sim/rows] (dotimes [col sim/cols] (let [c (at sim/grid row col)] @@ -94,8 +146,128 @@ (rl/draw-rectangle (i32 (* col sim/cell-size)) (i32 (* row sim/cell-size)) sim/cell-size sim/cell-size - (rl/get-color c)))))) + (rl/get-color c))))))) + +;; Drawn inside the camera, in world units, so every one of these moves and +;; scales with the grid. That is the point: a shape binding that is subtly +;; wrong is easiest to see when it is supposed to sit exactly on the cursor +;; and does not. +(defn draw-world-cursor [] + (let [p (rl/get-screen-to-world-2d (rl/get-mouse-position) view) + tint (rl/get-color (nth sim/colors sim/current-color)) + x (.x p) + y (.y p) + r (f32 (* sim/brush-size sim/cell-size))] + ;; The brush's actual reach, as a ring, plus a thinner circle outside it. + (rl/draw-ring p (* r (f32 0.9)) r (f32 0.0) (f32 360.0) 48 tint) + (rl/draw-circle-lines-v p (+ r (f32 6.0)) tint) + ;; A crosshair: two thin lines and one thick one, which is three separate + ;; raylib calls with three different shapes of argument. + (rl/draw-line-v (rl/Vector2 {:x (- x r) :y y}) + (rl/Vector2 {:x (+ x r) :y y}) tint) + (rl/draw-line-v (rl/Vector2 {:x x :y (- y r)}) + (rl/Vector2 {:x x :y (+ y r)}) tint) + (rl/draw-line-ex (rl/Vector2 {:x (- x (f32 4.0)) :y y}) + (rl/Vector2 {:x (+ x (f32 4.0)) :y y}) (f32 3.0) rl/white) + ;; The exact world point, one pixel of it. + (rl/draw-pixel-v p rl/white) + ;; A pointer above the cursor. Counter-clockwise, because raylib culls the + ;; other winding and draws nothing — which looks exactly like a broken + ;; binding and is why the outline is drawn over it as a control. + (let [tip (rl/Vector2 {:x x :y (- y (+ r (f32 26.0)))}) + left (rl/Vector2 {:x (- x (f32 12.0)) :y (- y (+ r (f32 6.0)))}) + rght (rl/Vector2 {:x (+ x (f32 12.0)) :y (- y (+ r (f32 6.0)))})] + (rl/draw-triangle tip left rght tint) + (rl/draw-triangle-lines tip left rght rl/white)) + ;; And the world's own edge, so panning has something to pan against. + (rl/draw-rectangle-lines-ex + (rl/Rectangle {:x 0.0 :y 0.0 + :width (f32 sim/screen-width) + :height (f32 sim/screen-height)}) + (f32 2.0) (rl/get-color 0x303030FF)))) + +;; Drawn outside the camera, in screen pixels, so it stays put while the world +;; moves underneath it. Nothing here can be asserted — it needs a GL context — +;; so it is built to be *looked* at: every shape binding appears once, and each +;; one is asymmetric enough that a wrapper with its arguments crossed is +;; visible rather than merely different. +(defn draw-hud [] + (let [title "SAND" + keys "arrows pan , . zoom 0 reset r clear" + ;; measure-text is what sizes the panel, so the box fits the string + ;; rather than a number somebody guessed. Headless it answers 0 for + ;; everything, which is why it is not in the acceptance table. + w (max (rl/measure-text title 30) (rl/measure-text keys 20)) + h 72 + x 24 + y (- (rl/get-screen-height) (+ h 24)) + panel (rl/Rectangle {:x (f32 (- x 12)) :y (f32 (- y 12)) + :width (f32 (+ w 24)) :height (f32 (+ h 24))})] + (rl/draw-rectangle-rounded panel (f32 0.2) 8 (rl/get-color 0x101018E0)) + ;; Both outline forms, one inside the other: the plain one has no + ;; thickness in raylib 5.5 and the -ex one is where thickness went. + (rl/draw-rectangle-rounded-lines panel (f32 0.2) 8 (rl/get-color 0x404060FF)) + (rl/draw-rectangle-rounded-lines-ex panel (f32 0.2) 8 (f32 2.0) + (rl/get-color 0x6060A0FF)) + (rl/draw-text title x y 30 rl/white) + (rl/draw-text keys x (+ y 40) 20 (rl/get-color 0xA0A0B0FF)) + + ;; The palette, along the bottom right. The selected colour is the one + ;; with a ring round it, so `current-color` is readable off the screen. + (let [sw (- (rl/get-screen-width) 40) + sh (- (rl/get-screen-height) 40)] + (dotimes [i (len sim/colors)] + (let [cx (- sw (* (- (len sim/colors) (+ i 1)) 46)) + c (rl/get-color (nth sim/colors i))] + (rl/draw-circle cx sh (f32 16.0) c) + (when (= i sim/current-color) + (rl/draw-circle-lines cx sh (f32 22.0) rl/white)))) + + ;; A zoom read-out with no number in it, because there is no string + ;; formatting yet: the bar's length is the zoom. draw-rectangle-rec, + ;; draw-rectangle-v and draw-rectangle-lines are the three remaining + ;; rectangle shapes, so all three are here rather than invented + ;; elsewhere. + (let [bx (f32 (- sw 220)) + by (f32 (- sh 60)) + fill (* (f32 200.0) (min (f32 1.0) (/ (.zoom view) (f32 8.0))))] + (rl/draw-rectangle-rec (rl/Rectangle {:x bx :y by :width (f32 200.0) + :height (f32 10.0)}) + (rl/get-color 0x202028FF)) + (rl/draw-rectangle-v (rl/Vector2 {:x bx :y by}) + (rl/Vector2 {:x fill :y (f32 10.0)}) + (rl/get-color 0x6060A0FF)) + (rl/draw-rectangle-lines (- sw 220) (- sh 60) 200 10 + (rl/get-color 0x8080C0FF)) + ;; The tick at the identity zoom, and a single pixel marking its left + ;; end — the integer forms of the line and pixel calls. + (rl/draw-line (+ (- sw 220) 25) (- sh 66) (+ (- sw 220) 25) (- sh 46) + rl/white) + (rl/draw-pixel (- sw 220) (- sh 66) rl/white)) + + ;; An ellipse, deliberately wider than it is tall so that exchanging its + ;; two radii would be obvious, and a ring whose sweep is driven by + ;; get-time so that something on screen proves the clock is running. + (let [ex (- sw 320) + ey (- sh 20) + spin (f32 (* 60.0 (rl/get-time)))] + (rl/draw-ellipse ex ey (f32 26.0) (f32 12.0) (rl/get-color 0x303040FF)) + (rl/draw-ellipse-lines ex ey (f32 26.0) (f32 12.0) + (rl/get-color 0x8080C0FF)) + (rl/draw-ring-lines (rl/Vector2 {:x (f32 ex) :y (f32 ey)}) + (f32 30.0) (f32 34.0) spin (+ spin (f32 270.0)) 32 + rl/white))))) + +(defn game-draw [] + (rl/clear-background rl/black) + ;; Everything between these two is in world space and moves with the camera. + (rl/begin-mode-2d view) + (draw-grid) + (draw-world-cursor) + (rl/end-mode-2d) + ;; And everything after it is in screen pixels again. (draw-brush) + (draw-hud) (rl/draw-fps 20 20)) (defn main [] @@ -103,6 +275,8 @@ (rl/init-window sim/screen-width sim/screen-height "SAND") (defer (rl/close-window)) (rl/set-target-fps 120) + ;; Before anything draws: a zero zoom is singular and nothing would appear. + (reset-view) ;; After the window, never before: LoadTexture uploads to the GPU and there ;; is no GPU to upload to until InitWindow has made a context. (load-brush) diff --git a/vendor/raylib/raylib.flan b/vendor/raylib/raylib.flan index 031df54..5b0bb55 100644 --- a/vendor/raylib/raylib.flan +++ b/vendor/raylib/raylib.flan @@ -505,3 +505,236 @@ 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") diff --git a/vendor/raylib/shim.c b/vendor/raylib/shim.c index 85b00c3..b9f9e1e 100644 --- a/vendor/raylib/shim.c +++ b/vendor/raylib/shim.c @@ -299,3 +299,178 @@ void flan_rl_get_image_color(const Image *image, int x, int y, Color *out) { void flan_rl_load_texture_from_image(const Image *image, Texture2D *out) { *out = LoadTextureFromImage(*image); } + +/* ── Shapes, text and timing ───────────────────────────────────────── + * + * All of the drawing below needs a GL context and therefore a window, so none + * of it can be in the acceptance table — it is exercised by running sand.flan + * and looking. The three prototypes most likely to be remembered wrong were + * checked against nm -D on libraylib.so.550 rather than against a header: + * raylib 5.5 moved the line thickness off DrawRectangleRoundedLines onto + * DrawRectangleRoundedLinesEx, so the four-argument form here is the 5.5 one + * and not the 5.1 one. + * + * MeasureText, GetFrameTime, GetTime and GetScreenWidth/Height need no GL + * context but do need InitWindow: the first reads the default font, which + * only InitWindow loads, and the others read window state. Headless they all + * answer 0 — measured, not assumed — so they are no more assertable than the + * drawing is. + */ + +extern void DrawPixel(int posX, int posY, Color color); +extern void DrawPixelV(Vector2 position, Color color); +extern void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, + Color color); +extern void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); +extern void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); +extern void DrawCircle(int centerX, int centerY, float radius, Color color); +extern void DrawCircleV(Vector2 center, float radius, Color color); +extern void DrawCircleLines(int centerX, int centerY, float radius, Color color); +extern void DrawCircleLinesV(Vector2 center, float radius, Color color); +extern void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, + Color color); +extern void DrawEllipseLines(int centerX, int centerY, float radiusH, + float radiusV, Color color); +extern void DrawRing(Vector2 center, float innerRadius, float outerRadius, + float startAngle, float endAngle, int segments, Color color); +extern void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, + float startAngle, float endAngle, int segments, + Color color); +extern void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); +extern void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); +extern void DrawRectangleV(Vector2 position, Vector2 size, Color color); +extern void DrawRectangleRec(Rectangle rec, Color color); +extern void DrawRectangleLines(int posX, int posY, int width, int height, + Color color); +extern void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); +extern void DrawRectangleRounded(Rectangle rec, float roundness, int segments, + Color color); +extern void DrawRectangleRoundedLines(Rectangle rec, float roundness, + int segments, Color color); +extern void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, + int segments, float lineThick, + Color color); +extern void DrawText(const char *text, int posX, int posY, int fontSize, + Color color); +extern int MeasureText(const char *text, int fontSize); +extern float GetFrameTime(void); +extern double GetTime(void); +extern int GetScreenWidth(void); +extern int GetScreenHeight(void); + +void flan_rl_draw_pixel(int x, int y, const Color *c) { DrawPixel(x, y, *c); } + +void flan_rl_draw_pixel_v(const Vector2 *p, const Color *c) { + DrawPixelV(*p, *c); +} + +void flan_rl_draw_line(int x1, int y1, int x2, int y2, const Color *c) { + DrawLine(x1, y1, x2, y2, *c); +} + +void flan_rl_draw_line_v(const Vector2 *a, const Vector2 *b, const Color *c) { + DrawLineV(*a, *b, *c); +} + +void flan_rl_draw_line_ex(const Vector2 *a, const Vector2 *b, float thick, + const Color *c) { + DrawLineEx(*a, *b, thick, *c); +} + +void flan_rl_draw_circle(int x, int y, float radius, const Color *c) { + DrawCircle(x, y, radius, *c); +} + +void flan_rl_draw_circle_v(const Vector2 *center, float radius, const Color *c) { + DrawCircleV(*center, radius, *c); +} + +void flan_rl_draw_circle_lines(int x, int y, float radius, const Color *c) { + DrawCircleLines(x, y, radius, *c); +} + +void flan_rl_draw_circle_lines_v(const Vector2 *center, float radius, + const Color *c) { + DrawCircleLinesV(*center, radius, *c); +} + +void flan_rl_draw_ellipse(int x, int y, float rh, float rv, const Color *c) { + DrawEllipse(x, y, rh, rv, *c); +} + +void flan_rl_draw_ellipse_lines(int x, int y, float rh, float rv, const Color *c) { + DrawEllipseLines(x, y, rh, rv, *c); +} + +void flan_rl_draw_ring(const Vector2 *center, float inner, float outer, + float start, float end, int segments, const Color *c) { + DrawRing(*center, inner, outer, start, end, segments, *c); +} + +void flan_rl_draw_ring_lines(const Vector2 *center, float inner, float outer, + float start, float end, int segments, + const Color *c) { + DrawRingLines(*center, inner, outer, start, end, segments, *c); +} + +void flan_rl_draw_triangle(const Vector2 *a, const Vector2 *b, const Vector2 *d, + const Color *c) { + DrawTriangle(*a, *b, *d, *c); +} + +void flan_rl_draw_triangle_lines(const Vector2 *a, const Vector2 *b, + const Vector2 *d, const Color *c) { + DrawTriangleLines(*a, *b, *d, *c); +} + +void flan_rl_draw_rectangle_v(const Vector2 *position, const Vector2 *size, + const Color *c) { + DrawRectangleV(*position, *size, *c); +} + +void flan_rl_draw_rectangle_rec(const Rectangle *rec, const Color *c) { + DrawRectangleRec(*rec, *c); +} + +void flan_rl_draw_rectangle_lines(int x, int y, int w, int h, const Color *c) { + DrawRectangleLines(x, y, w, h, *c); +} + +void flan_rl_draw_rectangle_lines_ex(const Rectangle *rec, float thick, + const Color *c) { + DrawRectangleLinesEx(*rec, thick, *c); +} + +void flan_rl_draw_rectangle_rounded(const Rectangle *rec, float roundness, + int segments, const Color *c) { + DrawRectangleRounded(*rec, roundness, segments, *c); +} + +/* Four arguments, not five: 5.5's DrawRectangleRoundedLines has no thickness + * and the Ex variant below is where it went. Getting this wrong links fine. */ +void flan_rl_draw_rectangle_rounded_lines(const Rectangle *rec, float roundness, + int segments, const Color *c) { + DrawRectangleRoundedLines(*rec, roundness, segments, *c); +} + +void flan_rl_draw_rectangle_rounded_lines_ex(const Rectangle *rec, + float roundness, int segments, + float thick, const Color *c) { + DrawRectangleRoundedLinesEx(*rec, roundness, segments, thick, *c); +} + +void flan_rl_draw_text(const char *text, long long n, int x, int y, + int font_size, const Color *c) { + char buf[512]; + DrawText(cstr(text, n, buf, sizeof buf), x, y, font_size, *c); +} + +int flan_rl_measure_text(const char *text, long long n, int font_size) { + char buf[512]; + return MeasureText(cstr(text, n, buf, sizeof buf), font_size); +} + +float flan_rl_get_frame_time(void) { return GetFrameTime(); } +double flan_rl_get_time(void) { return GetTime(); } +int flan_rl_get_screen_width(void) { return GetScreenWidth(); } +int flan_rl_get_screen_height(void) { return GetScreenHeight(); }