Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5d304a6
feat(capi): callback Canvas so C embedders can render primitives them…
beetlebugorg Jul 7, 2026
d3812b1
render: add VectorSurface — world-space portrayal over a C ABI
beetlebugorg Jul 7, 2026
619e528
render: draw symbols/soundings as atlas sprites over the C ABI
beetlebugorg Jul 7, 2026
9b238a7
render: tile area fill patterns via a draw_pattern callback
beetlebugorg Jul 7, 2026
df6ee94
render: declutter labels/symbols behind the VectorSurface
beetlebugorg Jul 7, 2026
d922b72
render: draw complex-linestyle bricks at native size so they tile
beetlebugorg Jul 7, 2026
15b6c56
render: declutter text only, per S-52 (not symbols/soundings)
beetlebugorg Jul 7, 2026
0f530b1
render: cursor object-query (S-52 pick) over the C ABI
beetlebugorg Jul 7, 2026
ff9fe3b
docs: document the host-surface, object-query, and sprite-mln C APIs
beetlebugorg Jul 7, 2026
1bc424c
render: honor S-101 ColorFill transparency (fill areas)
beetlebugorg Jul 7, 2026
18a285d
render: object query at the view zoom, with the SCAMIN cull
beetlebugorg Jul 7, 2026
47a8f6c
sprite: SDF glyph atlas via stb_truetype (GPU text, phase 1)
beetlebugorg Jul 7, 2026
97def68
capi/vector: SDF glyph atlas export + draw_text_str (GPU text, phase 2)
beetlebugorg Jul 7, 2026
62996c5
chart: live single-cell open (.cell) + M_COVR coverage C API
beetlebugorg Jul 7, 2026
9c67a85
chart: bake single cells to in-memory PMTiles + header/zoom opens
beetlebugorg Jul 7, 2026
aa005d1
chart: tile57_warmup() to pre-populate global registries for threaded…
beetlebugorg Jul 7, 2026
18f5f9f
chart: tile57_bake_cell_bytes for persisting a per-cell tile cache
beetlebugorg Jul 8, 2026
9d1e885
render: tessellate complex linestyles at render time, display-scaled
beetlebugorg Jul 8, 2026
03c49a6
render: declutter soundings (per-number) so HiDPI enlargement doesn't…
beetlebugorg Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ C ABI is not yet frozen.

## [Unreleased]

### Added — C ABI: host-surface rendering, object query, sprite atlas
- **`tile57_chart_render_surface_cb`** + the **`tile57_surface_cb`** vtable: hand
the portrayed scene to a host as world-space draw calls (area/line geometry in
web-mercator, point symbols/text as a world anchor plus a reference-px outline,
each call tagged with its SCAMIN) so a GPU host tessellates once and pans/zooms
by transforming the vertices. Includes optional `draw_sprite` / `draw_pattern`
callbacks that name atlas entries to draw point symbols and area patterns as
textured quads. See [docs/c-api.md](docs/docs/c-api.md).
- **`tile57_chart_query`** + **`tile57_query_cb`**: the S-52 cursor pick — report
every feature under a lon/lat (area point-in-polygon; line/point within a small
radius) with its class, S-57 attribute JSON, and source cell.
- **`tile57_bake_sprite_mln`**: bake just the MapLibre sprite-mln atlas (pivot-
centered symbol cells + a name-to-rect index) for a GPU host to load.
- **Fix**: the reader (baked PMTiles) replay now copies the baked `s57` / `cell`
tile properties into `FeatureMeta`, so a baked chart's object query returns
attributes, not just the class.

### Added — S-52 overscale indication (AP(OVERSC01), specs/overscale.md)
- **Baked overscale hatch geometry**: every cell contributing to a tile
(including band-handoff carried cells) now emits its M_COVR (CATCOV=1)
Expand Down
7 changes: 7 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,13 @@ pub fn build(b: *std.Build) void {
// module import does NOT pull another module's `test {}` blocks in.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = mod })).step);
// The sprite module (SDF glyph atlas lives here): needs the C glue
// (stb_truetype/nanosvg) + libc + render.
const sprite_test = addPkgTest(b, test_step, "src/sprite/sprite.zig", target, optimize, &.{
.{ .name = "render", .module = render_mod },
});
sprite_test.link_libc = true;
addSvgRaster(b, sprite_test);

_ = addPkgTest(b, test_step, "src/s57/s57.zig", target, optimize, &.{});
const s100_test = addPkgTest(b, test_step, "src/s100/s100.zig", target, optimize, &.{
Expand Down
100 changes: 100 additions & 0 deletions docs/docs/c-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,60 @@ int tile57_chart_render_pdf(tile57_chart *chart, double lon, double lat, double
The `tile57_mariner` settings struct is defined in the
[chart-style section](#build-a-maplibre-style) below.

## Render to a host surface (vector callbacks)

Instead of a finished raster, tile57 can hand you the portrayed scene as a stream
of draw calls in world space. A GPU host tessellates that stream once, then pans
and zooms by transforming the vertices each frame, so symbols and text stay a
constant size on screen and no re-portrayal is needed while the view moves.

You fill in a `tile57_surface_cb` vtable and pass it to
`tile57_chart_render_surface_cb`. Area and line geometry come in web-mercator world
coordinates (the range 0 to 1, with y pointing down). Point symbols, soundings, and
text come as a world anchor plus a small outline in reference pixels, so you can
draw them at a fixed size on screen. Every call carries the feature's SCAMIN, so you
can hide it by zoom in a shader.

```c
typedef struct { double x, y; } tile57_world_point; /* web-mercator 0..1, y down */
typedef struct { const tile57_world_point *pts; uint32_t n;
const uint32_t *ring_starts; uint32_t ring_count; } tile57_world_rings;
typedef struct { const char *cls; int64_t scamin; int32_t plane; } tile57_feature;

typedef struct {
void *ctx; /* handed back to every call */
void (*fill_area) (void *ctx, const tile57_feature *f, const tile57_world_rings *rings,
tile57_rgba color, int even_odd);
void (*stroke_line)(void *ctx, const tile57_feature *f, const tile57_world_rings *lines,
float width_px, float dash_on, float dash_off, tile57_rgba color);
void (*draw_symbol)(void *ctx, const tile57_feature *f, tile57_world_point anchor,
const tile57_local_rings *rings, tile57_rgba color, int even_odd, float stroke_w);
void (*draw_text) (void *ctx, const tile57_feature *f, tile57_world_point anchor,
const tile57_local_rings *glyphs, tile57_rgba color, tile57_rgba halo, float halo_px);
/* Optional. Leave NULL to get vector outlines from the two calls above; set them
* to draw point symbols and area patterns from the sprite atlas as textured quads. */
void (*draw_sprite) (void *ctx, const tile57_feature *f, const char *name, size_t name_len,
tile57_world_point anchor, float rot_deg, float half_w_px, float half_h_px);
void (*draw_pattern)(void *ctx, const tile57_feature *f, const char *name, size_t name_len,
const tile57_world_rings *rings);
} tile57_surface_cb;

/* Portray the view once and drive the callbacks. 0 ok, -1 bad handle,
* -2 render failure, -3 unsupported source. */
int tile57_chart_render_surface_cb(tile57_chart *chart, double lon, double lat, double zoom,
uint32_t width, uint32_t height,
const tile57_mariner *m, const tile57_surface_cb *surface);
```

Set `draw_sprite` and `draw_pattern` once you have the sprite atlas loaded (see
[`tile57_bake_sprite_mln`](#generate-portrayal-assets)). tile57 then hands point
symbols, soundings, and area patterns by name, and you draw them as atlas quads —
smoothed by texture filtering and cheaper than tessellating outlines. If you leave
those two fields NULL, the same features arrive as vector outlines instead.

tile57 also declutters overlapping text for you before it makes the calls (symbols
and soundings always draw, per S-52), so you don't repeat that work.

## Inspect a chart: cells, features, catalogues

```c
Expand Down Expand Up @@ -157,6 +211,40 @@ int tile57_catalog_entries(const uint8_t *catalog_031, size_t len,
The CLI mirrors these as `tile57 cells`, `tile57 features`, and
`tile57 catalog`.

## Query the features under a point (object query / pick)

The S-52 cursor pick. Given a lon/lat and the current view `zoom`, tile57 replays
the tile at that zoom and reports every feature the point falls in — an area you
are inside, or a line or point symbol within a small radius. Each hit calls you
back with the S-57 object-class acronym, the attribute JSON (acronym to value),
and the source cell name. This is what a chart application shows when you tap a
feature to see what it is.

Passing the view zoom matters: the query reports the features actually DISPLAYED
at that zoom (it applies the same SCAMIN cull the renderer does), and the pick
tolerance tracks on-screen distance instead of ground distance — so a buoy is just
as easy to tap zoomed out as zoomed in, and a zoomed-out click doesn't return
finer-scale features that aren't drawn.

```c
typedef struct {
void *ctx;
void (*feature)(void *ctx, const char *cls, size_t cls_len,
const char *s57, size_t s57_len,
const char *cell, size_t cell_len);
} tile57_query_cb;

/* Calls cb->feature once per displayed feature under (lon,lat) at view `zoom`.
* 0 ok, -1 bad args. Callback pointers are valid only during that call. */
int tile57_chart_query(tile57_chart *chart, double lon, double lat, double zoom,
const tile57_query_cb *cb);
```

The class and cell come through for any chart. The attribute JSON is filled in
only when the chart was baked with pick attributes — `tile57_bake_bundle` /
`tile57_bake_pmtiles` include them by default (set `omit_pick_attrs` to leave them
out for leaner tiles). Without them, `s57` is an empty string.

## Bake an ENC_ROOT to PMTiles

Bake in-memory cells into one PMTiles archive, zoom-banded per cell by
Expand Down Expand Up @@ -237,6 +325,18 @@ int tile57_bake_assets(const char *catalog_dir, tile57_assets *out); /* 1 = o
void tile57_assets_free(tile57_assets *out);
```

`tile57_bake_sprite_mln` is a focused variant that fills only the `sprite_json` /
`sprite_png` fields with a MapLibre **sprite-mln** atlas: every S-101 symbol packed
into one PNG, each cell centered on its symbol's pivot, plus a JSON index of
`{name: {x, y, width, height, pixelRatio}}`. A GPU host loads this atlas once and
draws point symbols and area patterns as textured quads by name — the atlas the
[host-surface `draw_sprite`/`draw_pattern` callbacks](#render-to-a-host-surface-vector-callbacks)
hand back. Free it with `tile57_assets_free` as above.

```c
int tile57_bake_sprite_mln(const char *catalog_dir, tile57_assets *out); /* 1 = ok, 0 = error */
```

## Build a MapLibre style

`tile57_build_style` turns a MapLibre style template + the mariner's S-52 display
Expand Down
Loading
Loading