Document what the functions actually do instead of that they can fail
The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,15 @@
|
||||
/**
|
||||
* @file util.h
|
||||
* @brief Declares the public util API.
|
||||
* @brief Axis-aligned collision tests, path resolution, and two test-only image helpers.
|
||||
*
|
||||
* The grab bag. Three unrelated groups live here: rectangle/point overlap for
|
||||
* the physics backend, path resolution for the asset loaders, and a pair of
|
||||
* pixel-comparison routines that exist only so tests can assert on what was
|
||||
* actually drawn.
|
||||
*
|
||||
* All the geometry here is axis-aligned and treats edges as touching: a point
|
||||
* exactly on a boundary is inside. There is no rotation support and no
|
||||
* separating-axis test.
|
||||
*/
|
||||
|
||||
#ifndef _UTIL_H_
|
||||
@@ -9,83 +18,173 @@
|
||||
#include <akerror.h>
|
||||
#include <akgl/staticstring.h>
|
||||
|
||||
/** @brief Represents a two-dimensional point. */
|
||||
/** @brief An integer point. Carries a `z` the collision routines do not use. */
|
||||
typedef struct point {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
int x; /**< Horizontal position, in whatever space the caller is working in. */
|
||||
int y; /**< Vertical position. */
|
||||
int z; /**< Depth. Never written by akgl_rectangle_points and never read by the collision tests. */
|
||||
} point;
|
||||
|
||||
/** @brief Stores the corners of an axis-aligned rectangle. */
|
||||
/**
|
||||
* @brief The four corners of an axis-aligned rectangle, precomputed.
|
||||
*
|
||||
* akgl_collide_rectangles works corner by corner rather than by comparing edge
|
||||
* spans, so it wants the corners as points. akgl_rectangle_points derives one of
|
||||
* these from an `SDL_FRect`.
|
||||
*/
|
||||
typedef struct RectanglePoints {
|
||||
point topleft;
|
||||
point topright;
|
||||
point bottomleft;
|
||||
point bottomright;
|
||||
point topleft; /**< (x, y). */
|
||||
point topright; /**< (x + w, y). */
|
||||
point bottomleft; /**< (x, y + h). */
|
||||
point bottomright; /**< (x + w, y + h). */
|
||||
} RectanglePoints;
|
||||
|
||||
/**
|
||||
* @brief Do not use. Three open parentheses, two closes -- any expansion is a
|
||||
* syntax error. It duplicates akgl_collide_rectangles(), has no callers,
|
||||
* and is slated for deletion. TODO.md item 20.
|
||||
*/
|
||||
#define AKGL_COLLIDE_RECTANGLES(r1x, r1y, r1w, r1h, r2x, r2y, r2w, r2h) ((r1x < (r2x + r2w)) || ((r1x + r1w) > r2x)
|
||||
|
||||
/**
|
||||
* @brief Rectangle points.
|
||||
* @param dest Output destination populated by the function.
|
||||
* @param rect Rectangle whose corner points are calculated.
|
||||
* @brief Expand a rectangle into its four corner points.
|
||||
*
|
||||
* Coordinates are truncated from `float` to `int` on the way in, so a rectangle
|
||||
* at x = 10.9 has its corners at 10. That is deliberate for tile-grid work and
|
||||
* wrong for sub-pixel work; callers needing the latter should not round-trip
|
||||
* through this.
|
||||
*
|
||||
* @param dest Receives the corners. Required.
|
||||
* @param rect The rectangle, in any coordinate space. Required. `w` and `h` are
|
||||
* taken as extents from `x`/`y`, so a negative one produces a
|
||||
* rectangle whose "bottom right" is above and left of its "top
|
||||
* left" -- which every test here then reports as empty.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p dest or @p rect is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_rectangle_points(RectanglePoints *dest, SDL_FRect *rect);
|
||||
/**
|
||||
* @brief Collide point rectangle.
|
||||
* @param p Point to test for containment.
|
||||
* @param r Rectangle corner data used for collision testing.
|
||||
* @param collide Output set to whether the tested geometry intersects.
|
||||
* @brief Test whether a point falls inside a rectangle, edges included.
|
||||
*
|
||||
* Compares against `topleft` and `bottomright` only, so it assumes @p r is
|
||||
* well-formed -- the two corners actually being the minimum and maximum. `z` is
|
||||
* ignored on both sides: this is a 2D test.
|
||||
*
|
||||
* @param p The point to test. Required.
|
||||
* @param r The rectangle, as corners from akgl_rectangle_points. Required.
|
||||
* @param collide Receives `true` when the point is inside or exactly on an edge,
|
||||
* `false` otherwise. Required -- the return value is the error
|
||||
* context. Not written on any failure path.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p p, @p r, or @p collide is `NULL`.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_collide_point_rectangle(point *p, RectanglePoints *r, bool *collide);
|
||||
/**
|
||||
* @brief Collide rectangles.
|
||||
* @param r1 First rectangle to compare.
|
||||
* @param r2 Second rectangle to compare.
|
||||
* @param collide Output set to whether the tested geometry intersects.
|
||||
* @brief Test whether two rectangles overlap, edges included.
|
||||
*
|
||||
* Tests all eight corners -- each rectangle's four against the other -- and
|
||||
* stops at the first hit. Checking both directions is what catches the case
|
||||
* where one rectangle is entirely inside the other and so has no corner within
|
||||
* its neighbour.
|
||||
*
|
||||
* @param r1 First rectangle. Required.
|
||||
* @param r2 Second rectangle. Required. Order does not matter.
|
||||
* @param collide Receives `true` on any overlap or shared edge, `false`
|
||||
* otherwise. Required.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p r1, @p r2, or @p collide is `NULL`.
|
||||
*
|
||||
* @note A corner-containment test misses the one arrangement where two
|
||||
* rectangles overlap in a cross without either enclosing a corner of the
|
||||
* other -- a tall thin rectangle crossing a short wide one. Both are
|
||||
* reported as not colliding.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *collide);
|
||||
|
||||
/**
|
||||
* @brief Path relative.
|
||||
* @param root Base directory used to resolve the path.
|
||||
* @param path Path to resolve or transform.
|
||||
* @param dst Output destination populated by the function.
|
||||
* @brief Resolve an asset path, trying the working directory before the given root.
|
||||
*
|
||||
* Asset files name their neighbours relatively -- a sprite definition names its
|
||||
* spritesheet, a tilemap names its tilesets -- and "relative" has to mean
|
||||
* relative to the file doing the naming, not to wherever the game was launched
|
||||
* from. So this tries @p path against the process working directory first, and
|
||||
* only if that does not exist joins it onto @p root and resolves that. Either
|
||||
* way the result is absolute, with symlinks and `..` folded out.
|
||||
*
|
||||
* @param root Directory to fall back to, normally `dirname` of the file that
|
||||
* contained @p path. Required, even when unused.
|
||||
* @param path The path to resolve, relative or absolute. Required.
|
||||
* @param dst Receives the resolved absolute path. Required, and must already be
|
||||
* a claimed pool string -- this writes into it, it does not claim
|
||||
* one for you.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p root, @p path, or @p dst is `NULL`.
|
||||
* @throws AKERR_OUTOFBOUNDS If `root + "/" + path` would not fit in
|
||||
* #AKGL_MAX_STRING_LENGTH.
|
||||
* @throws ENOENT If neither spelling names an existing file. Any other `errno`
|
||||
* `realpath(3)` can raise -- EACCES on an unsearchable directory,
|
||||
* ELOOP, ENOTDIR -- propagates the same way.
|
||||
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
|
||||
*
|
||||
* @note The fallback path -- the common one, since most asset references are
|
||||
* relative to their own file rather than to the working directory --
|
||||
* returns straight out of the ENOENT handler and so never releases the
|
||||
* error context it was handling. Each such call consumes one slot of
|
||||
* libakerror's fixed 128-entry context array for the life of the process.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_path_relative(char *root, char *path, akgl_String *dst);
|
||||
|
||||
// These are REALLY slow routines that are only useful in testing harnesses
|
||||
/**
|
||||
* @brief Compare sdl surfaces.
|
||||
* @param s1 First SDL surface to compare.
|
||||
* @param s2 Second SDL surface to compare.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_VALUE When the corresponding validation or operation fails.
|
||||
* @brief Assert that two surfaces hold byte-identical pixels.
|
||||
*
|
||||
* A `memcmp` over the raw pixel buffer, so it is exact: one differing byte in
|
||||
* one pixel is a failure. Meant for test harnesses asserting on rendered output,
|
||||
* not for anything on a frame path.
|
||||
*
|
||||
* @param s1 First surface. Required. Its `pitch * h` is what determines how many
|
||||
* bytes are compared.
|
||||
* @param s2 Second surface. Required.
|
||||
* @return `NULL` when the pixels match, otherwise an error context owned by the
|
||||
* caller. "Not equal" is reported as an error, not as an out-param.
|
||||
* @throws AKERR_NULLPOINTER If @p s1 or @p s2 is `NULL`.
|
||||
* @throws AKERR_VALUE If the pixels differ.
|
||||
*
|
||||
* @warning The surfaces' dimensions, pitch, and format are not compared, so a
|
||||
* smaller @p s2 is read past its end rather than reported as a
|
||||
* mismatch. TODO.md, "Known and still open" item 5.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_compare_sdl_surfaces(SDL_Surface *s1, SDL_Surface *s2);
|
||||
/**
|
||||
* @brief Render and compare.
|
||||
* @param t1 First texture to render for comparison.
|
||||
* @param t2 Second texture to render for comparison.
|
||||
* @param x Horizontal destination coordinate.
|
||||
* @param y Vertical destination coordinate.
|
||||
* @param w Destination width.
|
||||
* @param h Destination height.
|
||||
* @param writeout Optional filename for the rendered diagnostic image.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_IO When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
||||
* @brief Draw two textures in turn, read the framebuffer back after each, and compare.
|
||||
*
|
||||
* The test-harness counterpart to akgl_compare_sdl_surfaces: it answers "do
|
||||
* these two textures *render* the same", which is not the same question as "are
|
||||
* these two textures identical", because the renderer's scaling and blending sit
|
||||
* in between. Both are drawn into the same rectangle against a cleared target.
|
||||
*
|
||||
* @param t1 First texture. Required.
|
||||
* @param t2 Second texture. Required.
|
||||
* @param x Left edge of the region, in pixels. Used for the source
|
||||
* rectangle, the destination, and the readback alike.
|
||||
* @param y Top edge of the region.
|
||||
* @param w Width of the region.
|
||||
* @param h Height of the region.
|
||||
* @param writeout Optional filename for a PNG of the *first* render, written
|
||||
* under `SDL_GetBasePath()`. `NULL` skips it. This is a
|
||||
* debugging aid -- when an image assertion fails, this is how
|
||||
* you see what was actually drawn.
|
||||
* @return `NULL` when the two renders match, otherwise an error context owned by
|
||||
* the caller.
|
||||
* @throws AKERR_NULLPOINTER If @p t1 or @p t2 is `NULL`.
|
||||
* @throws AKGL_ERR_SDL If the framebuffer cannot be read back.
|
||||
* @throws AKERR_IO If @p writeout is given and the PNG cannot be written.
|
||||
* @throws AKERR_VALUE If the two renders differ.
|
||||
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
|
||||
*
|
||||
* @warning Known defect: both passes draw @p t1 -- @p t2 is never rendered -- so
|
||||
* this currently always reports a match. TODO.md, "Known and still
|
||||
* open" item 1.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y, int w, int h, char *writeout);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user