Design format¶
Artists can contribute vector artwork and placement fixtures without access to robot hardware.
Artwork¶
Supply licensed raster or SVG source artwork to a version 3 DBV3 acquisition.
Finished drawing artwork must be acquired InkmapArtwork/2 with
dbv3-batik-paths/1 and a frozen recipe identity. SVG exports and previews remain
valid; the former public SVG importer is retired.
Placement¶
tatbot.surface-placement/2 expresses target-independent placement intent.
Its shared fields bind the artwork digest, physical size, rotation, mirror,
review and provenance. The target is one of:
body: the existing body identity, surface digest, barycentric anchor, semantic site and supported faces;plane: a metric canvas, anchor(u,v)and boundary margin;cylinder: the same metric chart plus its radius. Theuaxis runs along the cylinder;vis circumferential arc length. The chart origin lies on the crest, not at the cylinder axis.
The readers refuse a rotated artwork canvas outside the target margin and
a cylinder chart covering a full circumference. Analytic placement currently
requires warp: null. These are nominal authoring surfaces: no measured
robot pose, contact height or execution permission is implied.
The JSON Schema is config/human-representation/surface-placement-v2.schema.json.
Browser and Python constructors produce the same digests. Body v1 imports
have an explicit migration preserving their binding and constraints; existing
v1 artifacts retain their original interpretation. Material-stroke planning
accepts both versions through the same decomposition logic. InkProgram/2
supports both body coordinates and target-bound metric chart coordinates.
Plane/cylinder curves retain the placement digest and their arc length is
verified from chart coordinates; readers reject mixed coordinate types and
rehashed target or length mismatches. Body v2 compilation preserves the same
material events as v1. Material planning is shared across targets, including
initial charge, replenishment and color changes. A charged program can resolve
without a palette; an exhausted charge still requires available supply.
This contract alone does not enable drawing: the robot compiles plane designs
through tatbot ros compile (see portable design handoff).
Use config/inkmap/placement.schema.json for the placement record. Keep the
design identifier, surface/frame, anchor, scale, rotation, mirror state, schema
version, and provenance together. Validate records before rendering or mapping.
InkLang owns the path from placement description to the exact anchor. A PlacementFile v6 embeds the fixed model specification and identity, the original description, normalized intent, surface-bound resolution, and any explicitly accepted candidate. Consumers must reject provenance whose body digest, face, barycentric weights, actual site, or region UV disagrees with the placement and current atlas.
Placement v6 distinguishes two surface hashes:
asset_sha256identifies the complete GLB bytes for provenance.surface_sha256identifies canonical non-indexed, Z-up XYZ quantized to little-endian signed integer 10-micrometre units behind face/barycentric anchors. A rig or material export may change the asset hash without changing what an anchor means.
Pose and robot-relative positioning do not belong in a placement. A compiled
legacy boundary-trace realization uses config/inkmap/tattoo-scenario.schema.json, which
records the resolved design, rig pose, support, transforms, declared tool, and
derived surface trace needed for deterministic replay.
Typed bundle compilation uses config/inkmap/tattoo-scenario-v3.schema.json
and retains the bound source bundle and InkProgram; see below for its gates.
The preview is an interchangeable front end. A placement file is not a motion program and must not be treated as evidence of physical reachability.
Compiling a scenario¶
Portable editor projects¶
tatbot.inkmap-project/3 encloses an embedded-artwork PlacementFile v6, editor
pose/appearance/camera settings, bounded placement history, a recoverable
pending-edit base, and the paper/cylinder editor’s working draft. Its content
digest covers the entire document. Editor-only settings do not alter the
enclosed placement body/anchor contract. Project readers in TypeScript and
Python validate the same strict JSON Schema and canonical digest, reject
unknown versions/fields and missing artwork, and check all history frames.
Loading a project is not simulation compilation or permission to execute it.
config/inkmap/project.schema.json defines the format; the offline reader is
tatbot_sim.inkmap.project.load_project.
Projects store the shared artwork records directly in their body and chart
registries. chart and chart_parked are required and nullable. Earlier project
and placement schemas, and records from the retired tracer, require explicit
DBV3 regeneration and import; readers do not rebuild artwork
from their original SVG. Rotation stays in radians so reopening does not move
an unchanged design.
Placement-to-scenario compilation¶
The offline compiler takes a self-contained placement file with frozen artwork, binds the requested pose and tool in a typed bundle, and emits a v3 scenario through the same compiler as the editor’s simulation export:
tatbot sim compile config/inkmap/examples/forearm-placement-v6.json -- \
--pose reclined-left-arm-supported --output /tmp/forearm-scenario.json
The compiled scenario retains the bundle, resolved body and pose checksums, placement provenance, tool identity, support and deterministic typed trace. Historical v2 boundary-trace scenarios remain readable for replay.
Typed artwork conversion¶
tatbot.inkmap-artwork/2 contains a frozen TattooProgram/1, source hash and
provenance, acquisition adapter, optional recipe digest and metric decoding
error bound. Its program is authoritative for geometry, physical dimensions,
pen widths, path order and ink assignment. Previews derive from that geometry.
Readers validate nested hashes and source binding without re-running the
original conversion. They never fetch source identifiers.
The installed native DBV3 worker emits ordered centerlines directly. Inkmap’s
Import artwork accepts acquired JSON records only. Generic paint conversion
remains source tooling and contract fixtures; it cannot enter the drawing editor.
A record contains no embedded original SVG or independently stored preview.
See config/inkmap/artwork.schema.json, tatbot_contracts.artwork for the
stdlib path reader, and tatbot_sim.inkmap.artwork for public paint programs.
Project save/recovery is available independently. Export simulation bundle
downloads tatbot.inkmap-sim-bundle/1: the v6 placement file, immutable artwork
records, ordered typed surface placements, pose/support request, camera/skin
settings, seed, nominal tool ID, and pinned local body-asset manifest. The
manifest contains local keys and license-source hashes, never fetch URLs.
Unknown artwork licensing stays explicit; export is not redistribution approval.
This artwork record alone does not constitute a compiled scenario.
tatbot sim compile FILE -- --output SCENARIO recognizes a bundle and produces
a distinct v3 scenario with its source bundle and deterministic InkProgram.
For multiple placements, add --placement-id ID; no implicit first-placement
selection occurs. Request values are immutable: pose, tool, seed, support, and
world-placement CLI overrides refuse even when equal to a default. Author a
new bundle to change them. --created-at and --git-sha still describe the
compilation provenance. V2 remains the older boundary-trace format and is not
silently reinterpreted. V3 validation recompiles the typed program and checks
the derived trace, placement, pose/world realization, and local asset bytes.
V3 scene materialization derives a deterministic metric reference raster from
the bound TattooProgram/1 and SurfacePlacement/1; it never falls back to the
v2 SVG boundary trace. At 4 px/mm with 4x supersampling it writes effective
soft coverage and unassociated sRGB color separately from nearest-sampled
integer layer/placement IDs. Row zero is chart negative-y and column zero is
chart negative-x. Placement rotation belongs to the intrinsic surface frame
and is not baked into the texture a second time; mirror is a horizontal texture
operation on both browser and simulator. target-reference.png is review evidence only: the runtime
patch starts as the requested bare skin color and deposited pigment remains a
separate InkField. The editor’s Open compiled preview reader validates
the embedded bundle, typed-program hashes, derived trace, and immutable request
bindings before showing the scenario. It explicitly does not replace the CLI’s
local tool-profile and compiler reconstruction checks. Cross-renderer Gate D
qualification remains required before the bundle milestone is complete.
Run python -m tatbot_sim.inkmap.parity_evidence --output DIR from the
python/tatbot_sim environment to reproduce the cross-renderer qualification
packet. The Gate D mask comparison uses 12 px/mm with 4x supersampling so the
smallest stipple fixture is not dominated by one-pixel quantization; the
runtime reference target retains its documented 4 px/mm minimum. The report
aligns browser row zero (TattooProgram positive-y) to simulator array row zero
(chart negative-y) explicitly before comparing masks. It also exercises the
browser and Python flattenability checks, including complete-wrap refusal.
The TattooProgram/1 SVG adapter uses the shared TypeScript paint materializer
in web/inkmap/src/core/human-representation/svg-program.ts, invoked by the
Python human_rep.tattoo_program API through a bounded, local Node subprocess.
This adapter preserves painted metric regions: fills, compound-path holes,
stroke widths/caps/joins, nested transforms, colors, and layer order. It does
not infer centerlines from filled shapes. Existing typed InkProgram fill
planning remains responsible for turning regions into tool-intent strokes.
That planner unions equal-width/deposition paint within each ink layer before
adding connected inset contours. It never inks internal tessellation edges.
Paths are inset by the finite footprint radius, including around holes;
ideal footprint coverage must retain at least 98% of every connected paint
component, or the artwork/width pair refuses. This is geometric qualification,
not calibrated deposition or actual tool-width validation. Capacity splitting
and ink changes still use the shared ink-supply planner.
Planar set operations are pinned to Shapely 2.1.2; its runtime and GEOS versions are bound into the compiler digest. A fixed 0.1 nm snap grid and 10 nm seam closure reconcile floating-point tessellation T-junctions before planning. The union is snapped again after buffering to remove sub-grid numeric slivers. A 0.1 micrometre conservative inset guard compensates for buffer roundoff. Short links between contours must lie wholly within eroded paint; holes cannot be bridged. These are explicit geometry tolerances, not permission to bridge visible negative space. Source artwork and its canonical program are not rewritten.
Fill styles¶
The compiled InkProgram may carry an optional top-level fill_style naming the
paint planner it was compiled with, following the legacy operating_budget_s
precedent: absent means concentric (the inset rings above, the default), and
hatch is contour-then-hatch — two boundary rings (the depth-0 outline and one
inner ring at 0.45 Ă— width), then the centre domain eroded by two ring pitches
is filled with parallel rows at the same pitch along the long axis of each
connected region’s minimum-area bounding rectangle, so a feather becomes a few
long rows rather than a stack of nested slivers. Row pieces join into a
serpentine only when the joining segment lies wholly inside the centre domain
and is at most 2 × width long — the ring linker’s own admissibility test — so
visible negative space is never bridged; a hole splits a row into pieces that
continue separate chains. Hatch rows are reported at inset depth 3 and so
schedule as tier 2 behind the rings. Both styles pass the same spill, narrow
and per-component coverage gates. The style is a compile option
(design check --fill-style and sim qualify-artwork --fill-style); it never enters the artwork record’s
conversion; the frozen program retains the acquisition geometry.
The stroke schedule¶
The planner’s strokes are then scheduled before capacity splitting, by one
pass the simulator-side consumers share (design check and the simulator’s
rehearsal corpus call scheduled_material_strokes; tatbot_ink ports the
same three stages). Within each consecutive (source layer, ink)
group — so colour changes, dips and barriers keep their sequence — three
stages run:
Acquired DBV3 paths bypass this ordering and deduplication pass: native sequence, direction, closedness and repeated traversals are retained. The following pass applies to generic source/fixture paint programs.
Tier. Every stroke carries a tier: explicit elements (
path,cubic_bezier,dots,stipple) and the fill planner’s boundary rings (inset depth 0) are tier 0; insets 1–2 are tier 1; deeper insets are tier 2. Tier 0 draws first, so a run stopped early has drawn the silhouette and a full run fills detail last.Dedup at the tool’s footprint. Walking in tier order with a union of the footprints kept so far, a fill ring whose own footprint adds less than 25 % of its area is dropped, unless the drop would take its paint component below the coverage floor or cost it more than 1 % of its area in total (both re-checked before every drop against the coverage the component has with every ring kept: a drop is a deliberate loss and may not spend the qualification budget). The footprint is the fitted tool’s recorded line width when the datasheet has one, else the planning width, so an over-fine planning width stops doubling ink. Explicit elements are never dropped. Every drop is reported with its index, source primitive, length and new-area fraction.
Order within a tier. Greedy nearest neighbour from the previous stroke’s end, ties to the lower original index. A closed ring is rotated to start at the vertex nearest the previous end; an open stroke is reversed when its far end is nearer. The point arrays are the truth;
start_choiceanddirectionare unchanged.
The compiled InkProgram carries the schedule only through its stroke order and
each stroke’s ordering_rationale, which keeps its source layer N, prefix
and appends ; tier T depth D (and ; rotated to vertex K / ; reversed
where the order pass edited the points). Nothing new enters the document
schema; content_sha256 of a compiled program reflects the order. The pass is
covered by the compiler digest. The audit — per-tier counts and lengths, the
dropped list and the pen-up travel before and after — is the schedule field
of design check output.
The source/fixture SVG paint parser accepts opaque, self-contained geometry with presentation attributes or inline styles. Text, external references, filters, gradients, translucent paint, unknown style operations, and malformed syntax refuse with an element path. Nonuniformly scaled strokes require explicit outline conversion. Curves are adaptively flattened with a maximum requested chord error of 0.1 mm. Source SVG bytes and their digest remain review provenance.
The shared program preview renders fill regions without an extra outline and negative-space masks as transparency rather than white pigment. New placement files use the typed v3 bundle compiler. Do not infer filled-ink episode support from a faithful artwork preview alone.
The shared web/inkmap/public/designs/manifest.json collection supplies acquired
CC0 examples to Inkmap, normal body/flat simulation and the perception pilot.
It binds artwork JSON hashes, source metadata, acquired physical dimensions and
pen widths, and disjoint artwork-family train/validation/test splits.
The editor’s picker and every catalogue ID contain only three native DBV3
acquisitions: orbit, sprout and ridges. The original finished SVG designs,
hidden showcase artwork and compiler fixtures have been removed. Each example
is acquired at 30 Ă— 30 mm; a different physical size requires regeneration.
Normal sampling emits typed v3 scenarios. The versioned spiral-v1 is an
explicit calibration control. Historical geometric generators remain available
only through explicit legacy tasks/test APIs; normal sampling cannot fall back
to them. Random procedural SVGs are never normal artwork.
tatbot sim materialize and Inkgen batches produce source imagery and source
traces. Their directories cannot supply finished simulator artwork. Acquire
those sources through native DBV3 first; --artwork-dir accepts complete native
acquisition directories with matching result.json, recipe and artwork hashes.
tatbot sim qualify-artwork -- --output-dir DIR checks every starter artwork at
its size-domain endpoints against source/preview paint, canonical simulator
target, finite-footprint coverage, complete trajectory duration, and runtime
InkField deposition. It writes source/program/target/deposition views and retains
all rejected cases. See simulation.
Portable design handoff¶
tatbot.inkmap-design/1 contains a name, frozen InkmapArtwork/2 records, and
an ordered list of SurfacePlacement/2 records with unique placement IDs and
explicit artwork references. The registry contains exactly the used artwork.
The content digest binds the order, original artwork, conversion, and placement
intent. Readers validate the frozen artwork, nested hashes and cross-references;
rehashing an envelope cannot detach a placement from its source program.
The same design format admits body, plane and cylinder targets. It contains no robot pose, tool, palette, simulation request, or execution authority. The robot compile binds the selected tool and the registered page separately. Nominal chart dimensions are not measured pad dimensions.
The body editor’s Export design action uses the same artwork materializer and
body-placement derivation as simulation export, explicitly upgrading the latter’s
v1 body bindings. Generated source provenance is retained. Existing placement
and simulation bundle imports remain versioned interfaces; their contents are
not silently reinterpreted. Plane/cylinder design construction and Python reading
are available through the shared design API and the Paper and Cylinder
workspaces. Each edits its own analytic canvas — a project holds a pad draft
and a cylinder draft that never mix — with ordered artwork placements,
rotation and mirroring, choosing artwork from the same library and
generator as the body and placing it the same way — a ghost under the pointer,
a click, then drag and handles. Both are previewed in 3D on the bench’s
own fixtures — the 7.5 × 11 in paper pad, 1 cm thick, and the ⌀85 mm × 7.5 in
paper cylinder, each white with a faint blue ¼ in grid — with the artwork bent
onto the surface through the same chart frame the placement is checked with;
u follows the cylinder’s axis and v is arc length from its crest. The
fixture sizes are the editor’s defaults, not measurements of any bench
setup. Portable design and Open design (under File) use the same
portable handoff as body export; Artwork SVG exports the selected artwork
as the editor draws it. Bounds and margins are validated before saving. The
in-memory draft survives switching workspaces and a reload; save a design file
before clearing the browser.
tatbot ros compile design.json turns a plane design into the ROS 2 stack’s
program.json and a preview.svg (contact by schedule tier, dashed pen-up
travel). It accepts plane charts only; a 100 Ă— 150 mm chart is the stencil
page. The compile, its refusals and the program format are in
ros/README.md section 4.2. A preview is not a deposition prediction.