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(); }