Expand description
MBA-1440: import Bero’s “Ventum” reticle spec into the engine’s ReticleDescription.
A reticle drawn in Bero’s Ventum tool is a single JSON object: reticle-level metadata
(name, focal plane, calibration magnification, angular unit) plus a spec list of
drawing elements. This module is the one-way transform from that authoring format into
the engine’s own ReticleDescription, so a Ventum reticle can be hold-solved by the
existing hold_point_in_reticle without any of the CLI/FFI/WASM surfaces having to
learn a second schema. It is a converter, not new physics — every coordinate is carried
straight through, only unit-normalized to milliradians and repeat-expanded.
§What is kept, what is dropped
The Ventum spec mixes hold-bearing marks with decoration:
dotandtickare the holdable marks. Adotbecomes aMarkKind::Dot; atickbecomes aMarkKind::Hashwhose hold point is its own(x, y)anchor.textis a standalone label positioned in reticle coordinates. Following the schema’s recommended “option (b)”, each text is bound to the nearest hold-bearing mark withinDEFAULT_TEXT_BIND_MILand becomes that mark’slabel; unattached text is dropped.line,circle,rect,grid, and any unknown future element type are pure decoration and are dropped.
§Coordinate and unit conventions
Ventum uses +x = right, +y = down, origin at the reticle center — identical to the
engine’s ReticleMark::right_mil / ReticleMark::down_mil, so no sign flips are
needed. All coordinates are expressed in the reticle’s own unit; MOA input is
converted to milliradians with 1 MOA = 0.2908882 mil (MOA_TO_MIL). Auto-numbered
ladder labels are NOT unit-converted — a label is the reticle-unit value it names.
Confirmed by the format’s author (2026-08-01): +y is down because a reticle is numbered
downward (a 4-unit holdover is y: 4; a stadia mark 5 up is y: -5), which is plain SVG
screen convention and happens to match shooter intuition.
§Arc angles (not yet consumed — recorded so the mapping is not guessed later)
Ventum draws arcs as a circle element carrying start/end angles. Those angles are
measured from 3 o’clock (0° = +x, right) and sweep CLOCKWISE:
0° = right 90° = down 180° = left 270° = UPThe one counterintuitive consequence, worth stating because it is the opposite of a
compass: since +y is down, the TOP of the reticle is 270°, not 90°. A horseshoe opening
downward is therefore start: 200, end: 340. This is the same system as the coordinates
(+x right, +y down, 0° at 3 o’clock, clockwise), so the format never mixes conventions.
A point on an arc of radius r about the reticle center is therefore
x = r * cos(θ) y = r * sin(θ) (θ in degrees, +y DOWN)Note the PLUS on the sine: in a y-up math convention this term is negated, and that single
sign is the whole trap. Verified against the author’s reference diagram — its 0/90/180/270
markers and the start: 200, end: 340 horseshoe endpoints reproduce exactly under the
formula above, and the arc’s 140° clockwise sweep passes through 270° (the top), leaving the
gap at the bottom.
This module currently drops circle as decoration (see above), so nothing here depends on
these angles yet; the note exists so that whoever makes arcs hold-bearing does not have to
re-derive the convention.
§Safety
The mark cap (crate::reticle::MAX_RETICLE_MARKS) is enforced during repeat
expansion, so a hostile repeat.n (or a huge mirrored ladder) can never allocate an
unbounded vector: expansion stops the instant the running instance count would exceed
the cap, having materialized at most one instance past it. This is the same defense as
the 0.31.0 reticle generator size guards.
Constants§
- DEFAULT_
TEXT_ BIND_ MIL - Largest
(x, y)distance (in milliradians) at which atextelement binds to a hold-bearing mark and becomes its label. Text farther than this from every mark is treated as free-floating decoration and dropped. - MOA_
TO_ MIL - Milliradians per minute of angle (
1 MOA = 0.2908882 mil). Ventum reticles authored in MOA are converted to the engine’s milliradian marks with this factor.
Functions§
- import_
ventum_ reticle - Import a Ventum reticle spec into a
ReticleDescription.