Remove the corner helpers akgl_collide_rectangles no longer uses
Some checks failed
libakgl CI Build / cmake_build (push) Failing after 21s
libakgl CI Build / performance (push) Failing after 21s
libakgl CI Build / memory_check (push) Failing after 17s
libakgl CI Build / mutation_test (push) Failing after 19s

akgl_rectangle_points, akgl_collide_point_rectangle, akgl_Point and
akgl_RectanglePoints go. They were the intermediate form of an implementation
that changed: akgl_collide_rectangles was eight corner-containment tests built on
them, and it has been four span comparisons since the cross-case fix. Nothing
outside tests/ called either function, and a point-in-rectangle test is four
comparisons a caller can write without a struct conversion in front of them.

akgl_collide_rectangles stays. It has two correct callers in the sidescroller
asking a game-level overlap question -- a coin, a hazard, from an updatefunc --
where a bool is the whole answer and a proxy plus a narrowphase call would be
computing a normal nothing reads. TODO.md records the split rather than leaving
it to be rediscovered.

Public API removal, so 194 exported akgl_ symbols against 196, and the manual's
counts move with them. The perf suite loses its rectangle_points row; the
all-pairs sweep stays as the control it is now labelled, and PERFORMANCE.md says
what 0.8.0 measured against it -- 188.5 us for 32,640 pairs at 256 actors, where
a whole step with collision attached is 54.1 us doing strictly more.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzBDV2fqgnUAcqCKqKvc71
This commit is contained in:
2026-08-02 08:09:42 -04:00
parent 3a6569e384
commit 4e32328681
11 changed files with 134 additions and 395 deletions

View File

@@ -1,13 +1,15 @@
# 01. Introduction
libakgl is a C library for building 2D games on SDL3. This is version **0.8.0**. It ships
195 functions declared across the twenty-three public headers in `include/akgl/`, plus
`akgl_version()` from the generated `version.h` — 196 exported `akgl_` symbols in
193 functions declared across the twenty-three public headers in `include/akgl/`, plus
`akgl_version()` from the generated `version.h` — 194 exported `akgl_` symbols in
`libakgl.so.0.8`.
**0.8.0 is an ABI break.** The `collide` slot on `akgl_PhysicsBackend` changed signature,
four public structs grew, and `akgl_physics_arcade_collide` stopped raising `AKERR_API`.
Collision is the reason; it is [Chapter 15](15-collision.md).
four public structs grew, `akgl_physics_arcade_collide` stopped raising `AKERR_API`, and
four symbols were removed — `akgl_rectangle_points`, `akgl_collide_point_rectangle` and the
two types they used ([Chapter 19](19-utilities.md)). Collision is the reason for all of it;
it is [Chapter 15](15-collision.md).
It gives you object pools instead of `malloc`, name-based registries instead of pointer
plumbing, a per-frame tick that updates and draws every live actor, JSON asset formats for
@@ -122,7 +124,7 @@ text you read *is* the header.
## Reading order
[Chapter 4](04-errors.md) comes before every subsystem chapter, because 183 of those 195
[Chapter 4](04-errors.md) comes before every subsystem chapter, because 181 of those 193
functions return `akerr_ErrorContext AKERR_NOIGNORE *` and you cannot read a single example
until you can read that return value. The dozen that do not are the `void` SDL enumeration
callbacks and the collision arena's accessors. After that, [Chapter 3](03-getting-started.md) gets a window on

View File

@@ -214,7 +214,7 @@ in the tree negated it, which is the only reason that was latent rather than liv
## Errors carry context, and callers cannot ignore them
**183 of libakgl's 195 functions return `akerr_ErrorContext AKERR_NOIGNORE *`.**
**181 of libakgl's 193 functions return `akerr_ErrorContext AKERR_NOIGNORE *`.**
Results come back through pointer parameters; the return value is always the error. The
`AKERR_NOIGNORE` attribute makes discarding it a compiler diagnostic.

View File

@@ -14,7 +14,7 @@ This manual does not restate them, because a copy here would be wrong the day li
changes and nothing in this repository would notice.
What is genuinely libakgl's, and is written down nowhere else, is **which statuses these
195 functions raise and what each one means here**. That is the three tables below.
193 functions raise and what each one means here**. That is the three tables below.
The house rules for *writing* code against the protocol — never a `*_RETURN` inside an
`ATTEMPT`, never a bare `return` out of a `HANDLE`, `CLEANUP` before `PROCESS`, an

View File

@@ -6,14 +6,12 @@ everything else passes around. The JSON accessors are the valuable part of this
their status semantics are what every asset loader in the library is written against, and
they are documented nowhere else.
## Rectangle overlap helpers
## The rectangle overlap test
Three functions, all axis-aligned, all in `util.h`:
One function, in `util.h`:
| Function | Question it answers |
|---|---|
| `akgl_rectangle_points(dest, rect)` | What are the four corners of this `SDL_FRect`? |
| `akgl_collide_point_rectangle(p, r, collide)` | Is this point inside those corners? |
| `akgl_collide_rectangles(r1, r2, collide)` | Do these two rectangles overlap? |
```c
@@ -28,38 +26,19 @@ akerr_ErrorContext *boxes_touch(SDL_FRect *a, SDL_FRect *b, bool *hit)
PASS(errctx, akgl_collide_rectangles(a, b, hit));
SUCCEED_RETURN(errctx);
}
/* Is the mouse over a button? Corners first, then the point test. */
akerr_ErrorContext *point_in_box(int x, int y, SDL_FRect *box, bool *hit)
{
akgl_RectanglePoints corners;
akgl_Point p;
PREPARE_ERROR(errctx);
p.x = x;
p.y = y;
p.z = 0;
PASS(errctx, akgl_rectangle_points(&corners, box));
PASS(errctx, akgl_collide_point_rectangle(&p, &corners, hit));
SUCCEED_RETURN(errctx);
}
```
Four properties, all deliberate:
Three properties, all deliberate:
**Axis-aligned only.** There is no rotation support and no separating-axis test. An actor
drawn with a non-zero `angle` still collides as its unrotated box.
**Axis-aligned only.** There is no rotation support and no separating-axis test here. If you
want either, you want a collision shape — [Chapter 15](15-collision.md).
**Edges count as touching.** A point exactly on a boundary is inside; two rectangles sharing
an edge and no area overlap. Both comparisons are `>=` and `<=`.
**Edges count as touching.** Two rectangles sharing an edge and no area overlap. Every
comparison is `<=`, and a version written with `<` would silently change what every pickup
test in a consumer answers.
**`akgl_rectangle_points` truncates from `float` to `int`.** A rectangle at `x = 10.9` has
its corners at 10. That is right for tile-grid work and wrong for sub-pixel work; if you
need the latter, do not round-trip through it. **`akgl_collide_rectangles` no longer does**
— it compares the spans in `float` directly, so a sub-pixel overlap registers.
**`z` is carried and never used.** `akgl_Point` has one, `akgl_rectangle_points` never
writes it, and neither collision test reads it. These are 2D tests.
**The comparison is in `float32_t`.** No intermediate form, no truncation, so an overlap
smaller than a pixel registers.
### The arrangement it used to miss
@@ -84,21 +63,34 @@ tall thin character is exactly that shape. The header carried a `@note` describi
than a fix.
It is four comparisons now, `r1.x <= r2.x + r2.w && r2.x <= r1.x + r1.w` on both axes.
`<=` rather than `<` because [`akgl_collide_point_rectangle`](#rectangle-overlap-helpers) is
inclusive on all four edges and these two have always agreed: touching counts.
**If you are upgrading from 0.7.x, two answers change.** The cross now reports `true`, which
is the point. And because the comparison no longer round-trips through `akgl_Point`'s `int`
members, overlaps and gaps smaller than a pixel are now seen rather than truncated away — so
a pickup test that was accidentally forgiving by up to a pixel is no longer forgiving.
**If you are upgrading from 0.7.x, three things change.** The cross now reports `true`, which
is the point. The comparison no longer round-trips through an `int` intermediate, so overlaps
and gaps smaller than a pixel are seen rather than truncated away — a pickup test that was
accidentally forgiving by up to a pixel is no longer forgiving. And **four symbols are
gone**:
**The library runs its own collision now** — see [Chapter 15](15-collision.md) — and it does
not use these. `akgl_collision_test` works in centres and half-extents, answers with a normal
and a depth rather than a boolean, and handles circles and capsules as well as boxes.
| Removed in 0.8.0 | Why | What to write instead |
|---|---|---|
| `akgl_rectangle_points` | Built the corner form `akgl_collide_rectangles` no longer uses | Nothing. It had no other caller |
| `akgl_collide_point_rectangle` | Same | `p.x >= r.x && p.x <= r.x + r.w` on both axes — four comparisons, and no `SDL_FRect`-to-corners conversion in front of them |
| `akgl_Point` | Existed to carry an `int` position into the above, with a `z` nothing ever read | `SDL_FPoint`, if you want a point type |
| `akgl_RectanglePoints` | Existed to carry four of them | — |
These stay because a game-level overlap question is not always a physics question: a
minimap marker, a UI hit test, "is the cursor over this". For anything that should push or be
pushed, use a collision shape.
Nothing outside `tests/` called either function. They were the intermediate form of an
implementation that changed.
### What stays, and why it is not collision
**The library runs its own collision** — [Chapter 15](15-collision.md) — and it does not use
this. `akgl_collision_test` works in centres and half-extents, answers with a normal and a
depth rather than a boolean, and handles circles and capsules as well as boxes.
`akgl_collide_rectangles` stays because a game-level overlap question is not always a physics
question: a coin, a minimap marker, a UI hit test, "is the cursor over this". Both tutorials
use it for pickups and hazards from an `updatefunc`, where a `bool` is the whole answer and a
proxy, a broad-phase insert and a narrowphase call would be computing a normal nothing reads.
For anything that should push or be pushed, use a collision shape.
## Path resolution

View File

@@ -188,7 +188,7 @@ What the statuses mean is [Chapter 4](04-errors.md).
| `akgl_audio_voice_active`, `akgl_audio_mix` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
| `akgl_load_start_bgm` | `AKERR_NULLPOINTER`, `AKGL_ERR_SDL` |
| `akgl_path_relative` | `AKERR_NULLPOINTER`, `AKERR_OUTOFBOUNDS` |
| `akgl_rectangle_points`, `akgl_collide_point_rectangle`, `akgl_collide_rectangles` | `AKERR_NULLPOINTER` |
| `akgl_collide_rectangles` | `AKERR_NULLPOINTER` |
| `akgl_compare_sdl_surfaces` | `AKERR_NULLPOINTER`, `AKERR_VALUE` |
| `akgl_render_and_compare` | `AKERR_NULLPOINTER`, `AKERR_IO`, `AKGL_ERR_SDL` |
| `akgl_string_initialize` | `AKERR_NULLPOINTER` |