Remove the corner helpers akgl_collide_rectangles no longer uses
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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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` |
|
||||
|
||||
Reference in New Issue
Block a user