Target JSON (schema v5)
ringgrid.target.v5 is the canonical, compositional target schema. It mirrors
the target model one-to-one: a top-level object with
lattice, marker, coding, and optional fiducials sections. Loaders also
accept the legacy flat ringgrid.target.v4 schema and migrate it on the fly;
writers always emit v5.
Annotated example — plain rect with origin dots
Produced by TargetLayout::rect_24x24() (abbreviated to a 4×4 lattice with
two dots so the shape is easy to read):
{
"schema": "ringgrid.target.v5", // schema tag; dispatched before full parse
"name": "ringgrid_rect_r4_c4_p14.000_o5.600_i2.800", // human-readable name
"lattice": {
"kind": "rect", // "rect" | "hex"
"rows": 4,
"cols": 4,
"pitch_mm": 14.0 // center-to-center spacing
},
"marker": {
"outer_radius_mm": 5.6, // annulus bounds (plain) / centerline (coded)
"inner_radius_mm": 2.8
},
"coding": {
"kind": "plain" // plain annulus, no identity code
},
"fiducials": { // optional; present only when defined
"dot_radius_mm": 1.4,
"dots_mm": [ // dark dots in board mm, break lattice symmetry
[21.0, 21.0],
[7.0, 21.0]
]
}
}
Annotated example — coded hex
Produced by TargetLayout::default_hex() (shown here for a small 3-row lattice):
{
"schema": "ringgrid.target.v5",
"name": "ringgrid_hex_r3_c4_p8.000_o4.800_i3.200_w1.152",
"lattice": {
"kind": "hex",
"rows": 3,
"long_row_cols": 4, // markers in the long (even-offset) rows
"pitch_mm": 8.0
},
"marker": {
"outer_radius_mm": 4.8, // stroked-ring centerline radii
"inner_radius_mm": 3.2
},
"coding": {
"kind": "coded16",
"ring_width_mm": 1.152 // stroke width of the inner and outer rings
// "id_assignment": [ ... ] // optional; omitted ⇒ sequential 0,1,2,...
}
// no "fiducials" — coded markers anchor themselves via decoded IDs
}
Field reference
Top level
| Field | Type | Notes |
|---|---|---|
schema | string | "ringgrid.target.v5" (or legacy "ringgrid.target.v4" on input). |
name | string | Non-empty. Presets and CLI use a deterministic geometry-derived name. |
lattice | object | Tagged by kind. |
marker | object | Ring radii. |
coding | object | Tagged by kind. |
fiducials | object? | Omitted when the target defines no origin dots. |
Unknown top-level fields are rejected (deny_unknown_fields).
lattice
kind | Fields |
|---|---|
"hex" | rows, long_row_cols, pitch_mm |
"rect" | rows, cols, pitch_mm |
marker
outer_radius_mm, inner_radius_mm (both mm, inner < outer).
coding
kind | Fields |
|---|---|
"coded16" | ring_width_mm, optional id_assignment (array of codebook IDs, one per cell in generation order) |
"plain" | none |
A sequential id_assignment (0, 1, 2, …) is normalized back to the implicit
form and omitted on write.
fiducials
dot_radius_mm, dots_mm (array of [x_mm, y_mm] dot centers). See
Origin Fiducials.
v4 auto-migration
The pre-0.8 flat schema described a hex coded target with top-level
pitch_mm, rows, long_row_cols, marker_outer_radius_mm,
marker_inner_radius_mm, marker_ring_width_mm, and optional id_assignment:
{
"schema": "ringgrid.target.v4",
"name": "legacy",
"pitch_mm": 8.0,
"rows": 15,
"long_row_cols": 14,
"marker_outer_radius_mm": 4.8,
"marker_inner_radius_mm": 3.2,
"marker_ring_width_mm": 1.152
}
Every loader — TargetLayout::from_json_str / from_json_file, the CLI
--target flag, the CLI gen-target from-spec, and the Python / WASM detector
constructors — accepts this and migrates it to a Hex + Coded16 layout.
Writers only ever emit v5, so re-serializing a migrated target upgrades it:
#![allow(unused)]
fn main() {
let target = ringgrid::TargetLayout::from_json_str(v4_json)?; // accepts v4
let v5_json = target.to_json_string(); // emits v5
}
The checked-in tools/board/board_spec*.json fixtures are still v4 and load
unchanged, including their optimized id_assignment. An unknown or unsupported
schema tag is rejected with TargetValidationError::UnsupportedSchema.
Generating target JSON from the CLI
The maintainer-only ringgrid-dev gen-target writes target_spec.json (v5)
alongside printable SVG/PNG. (The published ringgrid CLI generates targets
with the recipe-driven ringgrid gen <recipe> instead — see
Target Generation.) It is a subcommand family:
# Classic hex coded target
ringgrid-dev gen-target hex \
--pitch_mm 8 --rows 15 --long_row_cols 14 \
--marker_outer_radius_mm 4.8 --marker_inner_radius_mm 3.2 \
--marker_ring_width_mm 1.152 \
--out_dir tools/out/target
# Rect plain target with origin dots
ringgrid-dev gen-target rect \
--pitch_mm 14 --rows 24 --cols 24 \
--marker_outer_radius_mm 5.6 --marker_inner_radius_mm 2.8 \
--dot_radius_mm 1.4 --dot_mm 161,161 --dot_mm 147,161 --dot_mm 161,175 \
--out_dir tools/out/target
# A built-in preset
ringgrid-dev gen-target preset default-hex --out_dir tools/out/target
ringgrid-dev gen-target preset rect24x24 --out_dir tools/out/target
# Re-render (and upgrade) an existing spec, v5 or legacy v4
ringgrid-dev gen-target from-spec --spec path/to/target_spec.json --out_dir tools/out/target
See Target Generation for the full flag reference and the equivalent Rust/Python paths.
Source: crates/ringgrid/src/target/schema.rs