From 386d9e0372928fdfdf211d290e3fc44973240a55 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 11 Sep 2026 19:20:13 +0700 Subject: [PATCH] Call every collision binding at least once Six of them were bound, linked, and had never been called by anything. That is the state a wrong argument order survives indefinitely: the link succeeds, the program runs, and the answer is nonsense that nobody has looked at. An audit for wrappers with no caller is worth doing after any binding lane. All six turned out to be correct, which is worth recording either way - the point of the audit is not that it finds bugs but that it converts "probably fine" into "called, and the answer checked". Each has a case that must come out the other way, because a predicate that always said yes would pass a single one. The one that earns the most is the polygon, the only binding here that crosses a slice, so the only place ptr+len has to arrive as raylib's pointer and count. Everything else about it would pass with a hardcoded count or with the pointer alone; the same point against the same array with three corners instead of four is what pins the length. Verified by hardcoding the count in the shim and watching it go the wrong way. --- test/programs/raylib-ffi.flan | 73 +++++++++++++++++++++++++++++++++++ test/test_acceptance.ml | 10 ++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/test/programs/raylib-ffi.flan b/test/programs/raylib-ffi.flan index 5a514b0..068d80b 100644 --- a/test/programs/raylib-ffi.flan +++ b/test/programs/raylib-ffi.flan @@ -191,6 +191,79 @@ (rl/Vector2 {:x 3.0 :y 0.0}) (rl/Vector2 {:x 3.0 :y 10.0})) (Some p) (show-v p) None (print-line "no crossing")) + ;; The rest of the collision family, each with the case that must come out + ;; the other way. Bound and linking is not the same as working: a wrapper + ;; whose arguments are in the wrong order links perfectly and answers + ;; nonsense, and until something calls it nothing says so. + (let [r (rl/Rectangle {:x 0.0 :y 0.0 :width 10.0 :height 4.0})] + ;; Circle against rect: just touching at the right edge, then clear of it. + (show-bool "circle meets rect" + (rl/collision-circle-rec? (rl/Vector2 {:x 12.0 :y 2.0}) 3.0 r)) + (show-bool "circle clears rect" + (rl/collision-circle-rec? (rl/Vector2 {:x 14.0 :y 2.0}) 3.0 r))) + + ;; Circle against a segment, which is the one that pins the radius against + ;; the two endpoints rather than against a single centre. + (show-bool "circle meets line" + (rl/collision-circle-line? (rl/Vector2 {:x 5.0 :y 2.0}) 3.0 + (rl/Vector2 {:x 0.0 :y 0.0}) + (rl/Vector2 {:x 10.0 :y 0.0}))) + (show-bool "circle clears line" + (rl/collision-circle-line? (rl/Vector2 {:x 5.0 :y 4.0}) 3.0 + (rl/Vector2 {:x 0.0 :y 0.0}) + (rl/Vector2 {:x 10.0 :y 0.0}))) + + (show-bool "point in circle" + (rl/collision-point-circle? (rl/Vector2 {:x 2.0 :y 0.0}) + (rl/Vector2 {:x 0.0 :y 0.0}) 3.0)) + (show-bool "point outside circle" + (rl/collision-point-circle? (rl/Vector2 {:x 4.0 :y 0.0}) + (rl/Vector2 {:x 0.0 :y 0.0}) 3.0)) + + ;; A right triangle with the square corner at the origin. The inside point is + ;; inside for one vertex order and not the other, so this is one of the few + ;; here that notices which vertex is which. + (show-bool "point in triangle" + (rl/collision-point-triangle? (rl/Vector2 {:x 1.0 :y 1.0}) + (rl/Vector2 {:x 0.0 :y 0.0}) + (rl/Vector2 {:x 8.0 :y 0.0}) + (rl/Vector2 {:x 0.0 :y 6.0}))) + (show-bool "point outside triangle" + (rl/collision-point-triangle? (rl/Vector2 {:x 7.0 :y 5.0}) + (rl/Vector2 {:x 0.0 :y 0.0}) + (rl/Vector2 {:x 8.0 :y 0.0}) + (rl/Vector2 {:x 0.0 :y 6.0}))) + + ;; On the segment, then beside it. The threshold is the last argument, so a + ;; wrapper that lost it among the four coordinates answers with whatever was + ;; in that register. + (show-bool "point on line" + (rl/collision-point-line? (rl/Vector2 {:x 5.0 :y 0.0}) + (rl/Vector2 {:x 0.0 :y 0.0}) + (rl/Vector2 {:x 10.0 :y 0.0}) 1)) + (show-bool "point off line" + (rl/collision-point-line? (rl/Vector2 {:x 5.0 :y 4.0}) + (rl/Vector2 {:x 0.0 :y 0.0}) + (rl/Vector2 {:x 10.0 :y 0.0}) 1)) + + ;; The only one that crosses a *slice*, so it is the only one where ptr+len + ;; has to arrive as raylib's pointer-and-count. A wrong length reads past the + ;; array or stops short, and either way the square stops being a square. + (let [square [(rl/Vector2 {:x 0.0 :y 0.0}) (rl/Vector2 {:x 8.0 :y 0.0}) + (rl/Vector2 {:x 8.0 :y 8.0}) (rl/Vector2 {:x 0.0 :y 8.0})]] + (show-bool "point in poly" + (rl/collision-point-poly? (rl/Vector2 {:x 4.0 :y 4.0}) (slice square 0 4))) + (show-bool "point outside poly" + (rl/collision-point-poly? (rl/Vector2 {:x 12.0 :y 4.0}) (slice square 0 4))) + ;; The same point against the same array, three corners instead of four: + ;; inside the square, outside the triangle the first three make. This is + ;; the case that proves the *length* crosses — everything above would pass + ;; with a hardcoded count, or with the pointer alone. + (show-bool "in square, four corners" + (rl/collision-point-poly? (rl/Vector2 {:x 2.0 :y 6.0}) (slice square 0 4))) + (show-bool "out of triangle, three" + (rl/collision-point-poly? (rl/Vector2 {:x 2.0 :y 6.0}) (slice square 0 3)))) + ;; Parallel, so they never meet: None rather than a point nobody wrote. (match (rl/collision-lines (rl/Vector2 {:x 0.0 :y 0.0}) (rl/Vector2 {:x 1.0 :y 2.0}) (rl/Vector2 {:x 5.0 :y 0.0}) (rl/Vector2 {:x 6.0 :y 2.0})) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 73942b4..a3c5e84 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -216,7 +216,15 @@ let () = point in rect yes\npoint below rect no\n\ rects overlap yes\nrects apart no\n\ circles touch yes\ncircles clear no\n\ - 3\n7\nno crossing\n" + 3\n7\n\ + circle meets rect yes\ncircle clears rect no\n\ + circle meets line yes\ncircle clears line no\n\ + point in circle yes\npoint outside circle no\n\ + point in triangle yes\npoint outside triangle no\n\ + point on line yes\npoint off line no\n\ + point in poly yes\npoint outside poly no\n\ + in square, four corners yes\nout of triangle, three no\n\ + no crossing\n" in if Sys.command "ldconfig -p 2>/dev/null | grep -q libraylib" = 0 then begin outputs "raylib ffi, headless" "programs/raylib-ffi.flan" raylib_out;