The toolpath library is an experimental path-geometry layer, not a machine program or a collision/clearance check. Its geometry remains library-owned; the mesh preview uses the general dependency-tracked computation runtime.
The 3D preview's program accepts either one zero-argument callable or an
ordinary nested list of such callables. Lists group execution occurrences;
there is no imposed operation/orientation/tool hierarchy. sequence paths
runs the same structure directly into the current output sink, for callers
that do not need a recording. Each leaf is an independent program and starts
its own paths. An absent stops the sequence and fuel is shared across leaves.
The example supplies two top-level groups (Op 1 and Op 2), tool-use groups,
faces, crossing directions, and individual knurl lines. The chamfer branches
contain rings, individual edges, and individual crosswise strokes (or one contour
cut). These are final-encoded tree builders: authored group lists run
their child builders, and loops emit deferred leaf programs. Grouped mapping,
rotation, and tool-scoping helpers wrap leaf emission instead of recursively
rebuilding lists. The leaves retain their existing explicit small captures.
The tree program cursor control retains the emitted hierarchy once, builds the
cursor from it, and returns ordinary items for the preview. Each native node's
source connects group notches to grouping code and fine notches to leaf producers.
The controls don't discover structure in already-generated geometry or impose
names on it. Dependency-tracked collection excludes viewport dimensions, so
orbiting, resizing, or moving the sliders does not reconstruct the program tree.
Definition edits invalidate it normally; widgets and handlers are not memoized.
Playback's optional focus is a half-open leaf-index range [start, end].
The existing progress fraction traverses cutting distance within that range.
Alternatively position uses an absolute leaf index plus a distance fraction
within that leaf. The example uses the latter, matching the equal-width grouping
controls and preserving the actual tool position across focus changes when it
remains in range. An out-of-range position resets to the first selected cut.
These numeric ranges/positions are derived for this recording. The controls
retain list-position paths and explicit All intent, not those numeric offsets;
see controls for clipping and generated-list lifetime limits.
All earlier parts have already cut the stock; only unfinished paths within the
focus are displayed, and later parts are omitted entirely. At zero progress,
the tool is at the first selected cut, not the end of the preceding operation.
Changing focus retains the camera. It changes view
state, not document history. Op 2 consequently retains the work of Op 1.
The recording retains small segment-boundary ranges for its program leaves. Focus is a playback input, separate from path generation, so selector changes reuse the observed recording. Stock meshing and implicit rendering use the existing dependency-tracked async pipeline. Focusing an early group can avoid much rendering work; focusing a late group still needs the earlier stock cuts. No source-hover linkage or keyboard interaction for these controls is added yet.
Rust generators call paths::Sink::start_at(point, axis) and line_to(point)
with finite 3D tool-tip coordinates. The normalized axis points from the tip
toward the spindle and stays fixed until the next start (3+2 positioning, not
simultaneous orientation interpolation). A new start begins a separate path: it is neither a cutting link
nor a rapid move from the previous endpoint. A line requires a preceding start.
Calling generators in sequence composes their output. MapPoints adapts another
sink, so translation, reflection, surface mapping, and further adapters compose
without recording intermediate paths. It maps positions only, preserving axes;
an arbitrary surface mapping does not define a cutter orientation.
Recording is an optional initial representation: native start/line commands
and nested tool groups. Playback uses it for total distance and seeking; tests also
replay it into other sinks. The line preview instead consumes
emitted points directly into one projected vector path and its bounds; the 3D
voxel preview consumes them into native Fidget fields. The mesh preview retains
an observed recording, then emits tube vertices and indices from it. There is
no GID command list or opaque Rust program passed through Grap. Final-encoding
generators can still run directly against any sink without recording.
Grap's start at accepts an optional tool axis x/y/z record, defaulting
to +Z. Explicit zero or nonfinite axes fail without emitting a command.
Grap uses ordinary calls to scoped start at, line to, with tool,
map points, and map axes functions. do supplies sequencing; lambdas supply reusable
generators. Emitters outside an installed output scope return toolpath output required. map points takes a callable under mapping and an unevaluated
expression. For each emitted point, it calls the mapping with x, y, z;
the mapping returns an ordinary record with those fields (the point function
constructs one). Nested mappings run inside-out and restore the surrounding
mapping on exit, including on failure. Mapping functions are intended to
compute coordinates, not emit more paths.
map axes has the same scoped mapping/body interface, but maps the spindle-facing
direction on start at, leaving positions untouched. The resulting axis must be
finite and nonzero and is normalized before emission. Nested axis maps apply
inside-out; both kinds restore their scope on success, absence, and evaluator
halt. Maps should be pure. Mapping a path's axis does not change the axis of an
already-started path: a new orientation requires a new start at, as before.
The example's ordinary Grap rotate paths(orientation, program) composes both
maps with the same rotation callable. Its input must be a linear rotation about
the origin, not a point mapping with translation or arbitrary deformation.
Translate the resulting path separately with map points; translation must not
affect a direction vector. Neither operation records intermediate paths.
with tool takes a tool expression and an unevaluated expression body.
It evaluates and validates the tool once, then runs the body in that tool's
scope. Nested scopes override locally; leaving restores the enclosing tool,
including after a returned absent or evaluator halt. The body's result is
returned unchanged. Entering and leaving a scope end the current path: the next
line needs a new start at. There is no inferred rapid, linking cut, or physical
tool-change motion. Unscoped paths remain drawable, but stock simulation rejects
segments without a tool, including upcoming ones.
Native code uses paths::with_tool(sink, tool, body). The sink's balanced
enter/leave notifications let streaming consumers interpret scopes without
recording; point-only consumers just break the path. The recording interpreter
retains nested groups and replay preserves them. These notifications are not
separate Grap commands or a persistent global current-tool setting.
The scoped output owns its mapping chain and sink. No mutable state is held by
the library between evaluations. Each emitted sample consumes evaluator fuel,
including samples generated inside Rust. Invalid commands return ordinary absents;
do stops at the first one. An enclosing match can recover and continue emitting,
with earlier output retained. No failure latch can override that recovery.
A consumer needing atomic results must stage its sink and discard it on a failed
final result; the preview does. Native generators propagate their sink's errors
immediately.
The example uses the presentation library's outline to organize its ordinary
root fields: Cutting parameters, Geometry, Tools, Operations, Strategies, and Path
combinators. The editable outline list sits at the top; clicking
one of its field references toggles that section using ordinary view history.
Several sections can be visible together. Panes stays outside the outline as a
normal editable root field, initially hidden by its usual default fold. Unlisted
fields follow in an ordinary record. Nothing is moved into a hidden workspace
configuration or a separate CAM document model.
The example's Grap diagonal passes and sample pass functions follow
ToolPathHelpers.DiagonalUVs in
rhino-cube-plugin/src/models/Paths/ToolPath.cs. It distributes nondegenerate
interior diagonals across the unit square and samples each including both
endpoints. Its maximum sample spacing is in UV coordinates, before mapping;
it is not a world-space tolerance, stepover, or scallop-height guarantee.
The row loop uses ordinary list unfold to construct one deferred program per
line. Each line's point loop uses iterate, emitting start at and line to
without collecting a list of points. Sampling
policy is document code, not a toolpath FFI. General min, max, ceil,
hypot, and is finite operations belong to the f64 library. The example
rejects nonfinite or fractional row counts and nonpositive/nonfinite spacing;
these checks are a flat sequence of ordinary require guards. Loops explicitly
return the list library's iteration finished absent at their endpoint rather
than relying on a failed match. An excessive sampling request is bounded by
evaluator fuel.
The example's cube function owns its size, chamfer, and control-point depth.
It returns an ordinary geometry record: field constructs the implicit solid,
top face maps UV points to that surface, normal gives the outward surface
normal at a contact point, and chamfer strip describes a canonical planar
chamfer. The strip contains a centerline curve(t), its length, its width
(c√2), the outward normal, and the unit across direction. These outputs
share the underlying dimensions. Paths and implicit geometry are constructed
from that common description; straightforward paths are not recovered from the
implicit field or mesh.
The implicit field is constructed only when requested; path
generation does not construct or mesh a solid it does not consume. Dimensions
and path arithmetic use f64; f32 from f64 explicitly rounds the constants at
the Fidget construction boundary.
crosshatch takes the top-face mapping, rigid orientation, setup-up, and a
compensation function from the current fixed axis to a point mapping.
face passes obtains the surface and normal functions from the cube, then
composes surface mapping, ball-radius compensation, orientation, and the
ball-center-to-tip shift.
Neither CAM function contains cube dimensions or a duplicate
surface formula. The geometry's analytic normal is still authored alongside its
surface, not automatically differentiated. Tests check both against the actual
implicit field after edits to each cube parameter.
For size s, chamfer c, and control-point depth d, the top patch is:
x = (s − 2c)(u − 0.5)
y = (s − 2c)(v − 0.5)
z = s/2 − 4d u(1 − u)v(1 − v)
The defaults s = 1, c = 0.1, d = 0.5 give a face-center depression of
0.125. The second sweep reflects
the unit-square y coordinate before the same surface mapping. Unlike Rhino's
finishing program, this example leaves the passes disconnected.
The crossing family reverses its row order, as Rhino does. The separate Grap ball center mapping
offsets each contact point along the analytic surface normal by ball radius;
ball tip then applies the orientation and subtracts radius times the tool
axis. The editable tool diameter is 0.125 inches; this mapping and the
ball-tool constructor share that cell. Op 1 · top and four sides calls that same generator for
+Z, +X, +Y, −X, and −Y. The separately callable Op 2 · bottom uses
the same generator for −Z, rotating the top-face coordinates 180° about X.
The six signed-axis rotations and all machining policy are ordinary Grap
functions in the document. ball-tip passes remains the top-only generator.
Preview · Op 1 + Op 2 sequences the two programs for direct execution;
the viewport consumes the equivalent Preview groups tree,
retaining Op 1's removed stock into Op 2. It is a preview composition, not a
single machine program: eventual export should target Op 1 and Op 2 separately.
Both currently use part coordinates; there is no simulated stock flip,
work-offset definition, or connecting move between setups.
Each operation sequences a with tool: ball tool indent group and a sibling
with tool: square tool chamfer group; playback supplies no global cutter.
Op 1 cuts the four top and four vertical chamfers. Op 2 cuts the four
bottom edges. The square tool switches in at those group boundaries without
inventing a tool-change motion.
The example keeps algorithms as ordinary Grap functions, not a strategy enum or a record containing the union of every strategy's settings:
straight cut(start, end, tool axis)emits one disconnected straight path.extend straight stroke(start extension, end extension, stroke)returns anotherstroke(start, end)callable. It moves the start backward and the end forward along the original segment's unit direction before calling the supplied stroke. Distances are independent; zero leaves that end unchanged. The wrapper neither selects a tool nor emits moves itself, and can wrap another extended stroke. It requires a nonzero segment: the unit-vector calculation returns absent when there is no direction.unit vectoris a separate Grap helper using the existing scalar math and point functions.evenly spaced(length, stepover, action)callsaction(t)at normalized positions including both endpoints. Length and maximum stepover must be finite and positive; intervals aremax(1, ceil(length/stepover)). It streams calls, not a list. A consumer using a curve must supply its arc length and an arc-length-normalized parameterization; the current strip has a straight curve.parallel strokes(curve, length, offset, stepover, stroke)uses that sampler, callingstroke(start, end)fromcurve(t) - offsettocurve(t) + offset. It knows nothing about the cutter or its axis.strokecan be a straight cut or a different supplied computation.square side contact offset(diameter, contact height, normal, tool axis)computes the contact-to-tip displacement for a cylindrical side contact. The unit normal and unit axis must be perpendicular. This is specific contact geometry, not a general compensation solver for arbitrary profiles.contour chamfer(diameter, contact height)andcrosswise chamfer(stepover, diameter)return ordinary closures accepting astrip. Configuration needs no output scope; calling the configured closure emits paths. Neither recipe knows the cube's angle, edge rotations, or global parameter cells.four rotated copies(program)(binderchamfer_ring) executes a supplied zero-argument program at four quarter-turns. It has no cutter/spacing/geometry parameters.cube chamferssupplies the configured program: first orient the canonical strip's cut, then repeat that oriented cut around world Z.
end-cut chamfer pass (chamfer_pass) and side-cut chamfer pass
(contour_pass) are the two configured recipes exposed in the source list.
The cut strip binding in cube chamfers selects one, before repetition.
Change that binding's subject from the end-cut pass to the side-cut pass to
switch recipes. The contour configuration shares diameter and cutting-length
cells with the square profile and explicitly chooses half the cutting length
as contact height. The end-cut configuration reads stepover and the same square
diameter, using half the diameter for each end's stroke extension. Tool selection
remains the enclosing with tool group, separate from these contact functions;
choosing a different tool shape still requires a matching contact recipe.
The recipes do not infer tool compatibility from an arbitrary profile.
The cube's chamfer strip curve(t) returns (s/2 − s*t, (s−c)/2, (s−c)/2):
the top/front chamfer centerline, extended to the stock's ends. contour chamfer
puts the side of the square mill tangent to this plane, at the configured
contact height. With outward normal n = (0,1,1)/√2, spindle-facing axis
a = (0,−1,1)/√2, cutter radius r, and cutting length L, the tip is
contact + r*n − (L/2)*a. It emits one straight start/line pair. Feed is
a × n, following the example's clockwise climb convention. Repetition and
rotation are composed outside the recipe, as described above.
There is no Rust chamfer generator or mesh-derived path.
The square profile is a Grap quote with spliced square tool diameter and
square cutting length cells. The contour compensation reads those same
cells; the tapered non-cutting neck and wider shank remain explicit profile
data. Its defaults are 0.125-inch diameter and 0.22-inch cutting length.
The canonical pass has sufficient diameter and cutting length for the default
0.1-inch chamfer. This is a single full-length finishing pass, not a roughing
strategy or an automatic check that arbitrary dimensions cover the chamfer.
Changing the profile to a different cutting shape would also require changing
this square-side compensation. The vertical-edge tool axes are horizontal;
there is no fixture/holder clearance claim.
Tests compare the actual Fidget swept subtraction for all twelve edges with the beveled cube's planes, and check edge coverage, tool scopes, compensation, and updates from edited cube/tool dimensions.
The example defaults to the alternative crosswise chamfer callable. The square
mill's axis is the strip's outward normal n, with its flat tip directly on the
plane. At each centerline sample, parallel strokes supplies endpoints
center − (width/2)*a and center + (width/2)*a, where
a = (0,−1,1)/√2 is the across-chamfer direction. Its stroke is composed as
extend straight stroke(radius, radius, straight cut with axis n), so the emitted
tip path starts one cutter radius before the strip and finishes one radius after
it. For the cube this puts the cutting cylinder entirely outside the stock at
both ends (at most tangent), rather than starting with half the cutter engaged
or stopping before it has exited. This is cutting-stroke overtravel, not a lead,
retract, or general fixture-clearance calculation. Both endpoints and the axis
receive the ring's rigid orientation. The tip plane remains the contact plane;
the extension does not offset the cut depth.
chamfer stepover defaults to 0.05 inches. For a straight edge of length s,
the program uses max(1, ceil(s/stepover)) intervals and includes both endpoints.
The actual spacing is therefore no greater than the requested maximum. Every
row cuts in the same direction; progression along the edge is
feed × normal, using the same row-order convention as the indent passes.
Each row starts a disconnected path: no return stroke, rapid, or linking cut
is inferred. The row loop is Grap iterate; emission uses the existing
start at, line to, and map points capabilities.
Stepover must be finite and positive, and the computed interval count finite. Finer requests remain bounded by evaluator fuel, not silently capped. Stepover is not clamped to the tool diameter: oversized steps leave real uncut strips in the subtraction. Both recipes assume a straight planar strip with constant, orthonormal normal/across directions and a square cutting profile. The cube supplies a 45-degree strip, but that angle is not part of the recipes. They simulate the ideal revolved envelope, not tooth marks, runout, or surface-finish physics. Tests cover both swept solids, all edge orientations, clear start/end poses, width/spacing/diameter changes, invalid spacing, visible gaps from wide steps, and dependency invalidation when the callable, spacing, or diameter changes. Separate tests compose the extension with a data-returning callback, without a tool or path-output scope. Independent-strip tests also exercise both recipes away from the cube's 45-degree frame; the shape and direction are explicit inputs rather than hidden constants.
The editable tilt (degrees) defaults to 45 and accepts any finite angle;
there is no machining-policy range guard. Zero points along the face normal,
90 degrees along the chosen feed direction, and other angles use the same
trigonometric calculation. The example assumes the usual clockwise spindle
rotation, viewed from the spindle toward the tip. Each operation passes an
explicit setup up vector: +Z for Op 1, −Z for the flipped Op 2, expressed in
part coordinates.
The ordinary Grap pull direction function selects the sign of each diagonal
so its lean points toward setup-up. A horizontal tie keeps the positive
diagonal direction. The axis is
cos(tilt) * face_normal + sin(tilt) * feed_direction.
For positive tilts below 90 degrees, motion runs along that lean, pulling the
cutter, with row progression along
feed_direction × face_normal for the example's clockwise climb policy.
Choosing the opposite feed direction reverses both points within each pass and
the row sequence; the reflected crossing family also reverses its row sequence. All of this is
editable Grap, not a rule in the path sink or renderer.
Other angles need not preserve the upward-lean or pulling assumptions; accepting
the resulting geometry is not a collision-clearance or machining-safety check.
Tests check the spindle-facing hemisphere, pull direction, row progression, and unchanged sampled geometry on all six faces. These are reference-face rules, not a claim of collision clearance or verified engagement throughout every curved cut. Tilt changes the tool and swept volume, not the ball-center compensation. The example does not plan indexing/retracts, model a holder/fixture, or perform collision checks. The example interprets one model unit as one inch; the runtime still uses ordinary numeric coordinates, not a unit-aware value type or machine setup.
preview paths is an opt-in viewport function with value (a callable program),
width, height, and optional fuel (otherwise Grap's default). It returns an
ordinary preview declaration containing that program and these parameters. The
native widget runs the program with this explicit budget. It fits a fixed
isometric projection inside the assigned pane, with a 16-logical-unit margin,
reduced in tiny panes.
All paths are green; there is no inferred cut/link classification.
preview paths 3d combines value (an ordinary Fidget field or colored scene)
with program (a callable path generator). It also takes an explicit positive
f64 line radius and opaque color, plus the same width, height, and bounds
as Fidget's preview 3d and an optional path-evaluation fuel budget. Its result
is an ordinary preview declaration, not an opaque native value.
The native sink constructs a capsule for each line and unions the segments of
each continuous path into its own Fidget scene object. start at separates
objects without adding a connecting move. A zero-length line is a sphere; an
isolated start draws nothing. Points convert to Fidget's f32 coordinates, with nonfinite
or out-of-range inputs rejected. The preview uses the existing Fidget camera,
depth testing, colors, and lighting. Paths precede the model for exact depth
ties; there is no screen-space overlay or displaced geometry. line radius
is visual thickness in model units, not a cutter radius. A failed final
program result discards the entire preview, including partially emitted paths.
preview paths mesh takes the same arguments, plus Fidget's u64 mesh depth
(1 through 8, default 6). It meshes only the model's implicit fields. The path
sink emits an indexed capsule mesh for each segment directly: twelve sides and
three latitude intervals per round cap. Segments overlap at joints; they are
visual tubes, not a boolean-unioned or manufacturing-ready solid. Zero-length
lines draw spheres and isolated starts draw nothing. No Fidget expression or
meshing step is involved in the path geometry. Paths and model share the triangle
renderer, camera, lighting, and depth buffer; no overlay or depth bias is used.
Failed path generation discards the whole preview before meshing the model.
preview paths refined takes the mesh preview's arguments and composes a mesh
fallback with a streamed final-quality software implicit image. It shares one observed path
evaluation between the two interpretations. Implicit work waits for the current
stock mesh; a geometry change cancels obsolete image work while meshing runs.
A current implicit surface replaces the stock/model mesh tile by tile; unfinished regions of
the first pass retain the available mesh at the current camera. Coverage is
explicit, so completed transparent pixels erase the mesh rather than revealing
it. Orbiting after an implicit result therefore
returns to a current mesh, never an older stock result that it had overtaken.
An outdated stock mesh is desaturated, while tool/path geometry updates immediately.
Paths and the visible cutter stay triangle meshes throughout. Each finished
Fidget tile supplies both color and normalized depth. The triangle renderer first
draws the draft surface in unfinished regions, replaces completed pixels (including
empty pixels) with the implicit color/depth, then depth-tests the paths and tool
against that surface. This requires no Fidget patch. Cutter sweeps used to subtract
stock remain implicit geometry; only the displayed cutter moves to triangles.
The mesh supplies the draft image. Implicit tiles render directly at native XY
resolution with four-times depth sampling, skipping intermediate resolutions and
the native-depth pass. The progress bar covers this single pass. Unfinished regions
remain mesh; each completed region is already at final quality.
Standalone mesh and progressive implicit functions remain available. The raster
API's explicit Passes choice retains the multi-resolution sequence for comparison
without adding a render-mode control to the example.
Command+4's example uses this refined preview with a Model / Stock radio group,
a playback slider and a stack
of grouping-range sliders: 504 paths (6,588
segments), with a blue reference cube when stock is disabled and a 3,000,000-fuel
budget including Grap ball-radius compensation. Its memo graph
retains the shared path recording and both renderers' expensive results.
Model (the default) shows the blue target part and the initial stock wireframe.
Stock shows material remaining at playback. The example's ordinary Grap controls
select whether its playback record includes stock; camera, selected ranges,
and cursor are preserved. Tool and remaining-path visibility are the same in both
modes. Model's surface-mesh inputs exclude playback and path data, so moving the
tool neither remeshes nor rerenders the unchanged part. Path color and width also
affect only the mesh layer. Stock-mode playback changes still invalidate the surface.
Op 1 finishes its five indented faces and eight chamfers before Op 2 cuts the
bottom indent and four remaining chamfers. Slider intervals follow cutting
distance, including the chamfer passes. With the side-contour strategy selected,
there are instead 264 paths (6,348 segments). The operation boundary
adds no travel or cut.
Implicit requests a new software image in the background when inputs change,
including the camera. Mesh retains geometry across camera changes; its optional
mesh depth defaults to 6 (the previous mesh-only fixture used 7).
It uses GPU triangle
drawing on native builds, with a CPU triangle renderer for web/headless fallback.
The example's Grap view calls preview paths refined; Model / Stock chooses
the geometry, not the renderer. Orbiting immediately returns to the retained mesh, then
matching implicit images take over when ready. Geometry changes request both a
new stock mesh and new images. There is no special handling of drag events or
inactivity delay. Both interpretations share camera framing and camera-space
lighting (including conversion from Fidget's downward-pointing sample Y axis).
Drag to orbit; scroll or pinch the Mac trackpad
over the viewport to zoom. Pinch needs no modifier and shares the existing
per-view camera state in both mesh and implicit previews. The document
contains its own copy of the cube definition so it is self-contained. Its
cube parameters drive both the reference solid and the toolpath's contact points
and normals. The initial stock bounds remain independent: making a smaller part
does not silently shrink the block being machined.
In the voxel preview, many sampled segments produce much larger Fidget expressions than the model alone. This is a first static visualization, not a performance claim for large machine programs. The opt-in toolpath orbit canary in performance checks exercises the real document and preview. The initial single-field version's headless sandbox check (2026-09-13, CPU fallback, 800 × 1200 raster) took a median 1.60 seconds across three measured frames after five warm-up frames. That is not a measurement of interactive native GPU performance.
The initial single-field version failed on the native GPU backend: a 2026-09-13
trace at 599 × 1280 × 640 measured 1.4–1.9 seconds per render and zero path
pixels before depth merging. Its VM tape contains 6,736 register-spill
load/store operations. The pinned Fidget shader's OP_MEM handler is explicitly
unimplemented and returns without a result, although RenderShape::new accepts
the bytecode. The CPU backend supports these operations. CPU screenshots and
successful bytecode construction therefore do not establish native rendering
support. Keeping each continuous path separate avoids spilling in this example;
the regression test checks every path for load/store instructions. A sufficiently
large individual path can still encounter this upstream limitation. There is no
spill emulation or automatic CPU fallback for it. Before shipping, follow up
upstream on implementing or rejecting these instructions; no issue has been filed
yet. The user confirmed that the split version renders the paths and accepts
orbit drags, but remains choppy. A subsequent native trace of 50 renders at
599 × 1280 × 640 averaged 234 ms (196–279 ms), with 215 ms on average in
wait/readback. This is rendering time, not a complete input-to-display latency
measurement. The cube meshing experiment motivated
the current mesh viewport, which bypasses the GPU VM entirely. It now retains
geometry through the general dependency graph.
All three volume previews accept an optional playback record with progress (f64,
0–1) or position (absolute leaf index plus fractional distance within that leaf),
optional focus, profile tolerance, and stock minimum / stock maximum (f64
x, y, z records). position takes precedence if both cursor fields are present.
These are ordinary data, not control state. The example supplies its position and
focus from the reusable controls library.
The recording computes total cutting-segment length, then visits segments again,
splitting the current segment at the requested distance. Completed segments are
hidden, upcoming segments use the requested path color, and the tool is orange.
The current segment is split exactly at the playback position: only its upcoming
portion is drawn. Path starts do not contribute
distance: the cursor jumps between disconnected passes rather than inventing
linking moves. Progress is not machining time. Empty paths have no tool; zero
length segments are well-defined. Paths locate the tool tip. The recorded group
supplies its validated cutter::Tool to mesh display, implicit display, and
stock subtraction; non-cutting sections are gray and do not remove material.
The cursor uses the tool of its current segment. At an exact segment endpoint it
stays with that segment until progress advances; scope boundaries add no distance.
Different tool groups subtract from the same stock in program order.
Tool geometry does not inherit dimensions from the path-line drawing style.
See tool profiles for its line/arc representation, exact
linear-profile sweeps, and explicit arc approximation tolerance. The required
profile tolerance is part of playback settings, not the tool definition;
it controls curved-profile approximation for subtraction and tool display.
Playback is generic over the consumer's error type, with conversion from path
validation errors. The preview translates invalid geometry to ordinary absents.
Without stock, the stock bounds draw a wire envelope. With that field, its
record supplies an opaque color. The preview's ordinary mesh depth determines
the stock mesh resolution too. Command+4 starts with a one-inch cube, bounded
by −0.5…0.5 on all three axes, so the two operations carve six indents and twelve chamfers
without first removing an oversized stock allowance.
Stock replaces the reference solid while enabled, avoiding coplanar surfaces
where their boundaries coincide. It shares the paths' depth buffer, so intact
material hides any path beneath it. There is no x-ray overlay or depth offset.
Slider changes retain the unchanged path recording. In the mesh preview,
path color and line thickness affect only the path/tool geometry layer; the stock
expression and mesh are reused.
Orbiting retains mesh geometry; every new view still produces a fresh raster image.
The implicit preview renders only stock/model in a progressive async request;
paths and the cutter are meshes drawn against its depth. The tool moves immediately.
The standalone implicit preview omits an outdated surface until a current image
arrives, rather than combining old-camera depth with current-camera meshes.
An unlabelled progress bar overlays
the top edge, resetting per refinement and disappearing when the final pass completes.
The first pending frame shows the available paths/tool and empty progress track. Refinement
starts at at most 128 physical pixels on the longest edge, then doubles toward
native resolution with a fixed camera and render volume, then performs one
native-size pass with four times the depth samples to reduce sharp-edge artifacts.
Completed tile batches progressively replace the normal native-resolution image
during that last pass. Publications are limited to one per 100 ms, plus completed
levels; completed empty pixels replace earlier pixels too. The standalone
implicit preview has no mesh fallback, so its first pass fills an empty image.
New input cancels the old sequence. The controls overlay the bottom of the
full-size view.
Implicit CAM rendering explicitly uses the software voxel renderer, directly
on the existing background executor. It does not attempt GPU evaluation first.
On Apple Silicon macOS it uses Fidget's JIT with its recommended 64/16/8-pixel
tiles; other platforms retain the VM with 32/16/8-pixel tiles. Meshing stays on
the VM. The local Fidget patches fix large AArch64 JIT
tapes and check cancellation within raster subtiles. Software scenes render all
objects within each image tile, retaining separate expressions and preserving
first-object wins on equal depths. One prepared scene retains root interval
tapes across the request's refinement levels; view-specific simplifications
stay worker-local. Workers reuse a fixed-size per-object tile buffer.
Progress counts pixels in completed scene
tiles, excluding padding at image edges, rather than giving every object equal
weight. Its fixed total is the image's pixel count, not an estimate of time
remaining. Root compilation is outside that count; streaming consumers shade
and assemble each finished tile before it is counted.
Cancellation still cannot
interrupt a single tape compilation/evaluation already in progress.
Lighting corrects sample-space gradients for unequal axis spacing, so
depth refinement does not change the light direction or relative axis weighting.
Web/default headless contexts use inline execution.
Experimental limitation (2026-09-14): the CPU implicit captures complete and
show correct playback. A native Metal run of the stock-removal example waited
over a minute in GPU readback and was terminated. Small GPU color/constant-field
tests pass. The bytecode diagnostic after accepting the updated Xcode license
confirmed unsupported memory instructions in the stock expression: at progress
0 it has 15 instructions and no memory operations; at 0.35 it has 17,473
instructions including 794 loads/stores; at 1.0 it has 53,228 instructions
including 12,914 loads/stores. The pinned GPU interpreter and tape simplifier
both leave OP_MEM unimplemented. Async scheduling cannot make that bytecode
valid on this backend. A further native submission of this known-unsupported
program was deliberately avoided. Command+4's implicit refinement uses the explicit
software path; GPU implicit CAM was blocked by that missing implementation.
No GPU timeout was added. The later GPU spill experiment
implements spill instructions behind an opt-in build feature, but is not used by
the app. Bounded external scratch now lets the full stock match CPU geometry
without exceeding Metal's private stack, but the measured GPU path remains much
slower than the CPU. The GPU work is paused; CAM remains on the CPU renderer.
Ordinary Fidget voxel previews retain their existing backend
selection; unsupported spilling tapes are now rejected before GPU submission,
allowing the existing CPU fallback. The ignored
editor_toolpath_implicit_async_svg_captures test captures the checked-in
example without opening a window; standalone-renderer captures substitute the
preview function in the test fixture.
editor_toolpath_progressive_svg_captures captures intermediate resolutions
through the full editor's normal async polling and frame pipeline.
editor_toolpath_refined_svg_captures captures mesh/image handoffs, orbit fallback,
and stale stock during playback with the production refined declaration.
toolpath::stock::Stock starts with a box-shaped Fidget field. Each completed
segment, including the completed portion of the current segment, subtracts the
continuous swept solid of the tool's cutting sections: stock.max(-sweep).
The mesh preview uses Fidget's CPU mesher and the shared triangle renderer.
The implicit preview renders the same expression directly with the voxel renderer.
There is no
heightfield, sampled stock grid, or fallback stock algorithm. The target model
does not participate in removal: a bad path can cut past the intended surface.
Stock mode does not mesh or draw that reference solid.
The ball-profile lowering unions the moving ball with the moving finite cylinder above its equator. The ball uses squared distance to a segment. For the cylinder, each query's cutter-local Z restricts which portion of the motion can contain that point; radial distance is minimized over that interval, with the overall end planes bounding it vertically. These are closed-form Fidget expressions, not sampled tool placements or a loop executed for each queried point. Horizontal, vertical, sloping, and zero-length segments share the same solid semantics. The tool is fixed along each path's explicit axis; it is not inferred from the tangent. The same sweep is evaluated in an orthonormal cutter frame for tilted tools; it is not approximated by discrete placements.
The remaining stock is a full 3D solid, so through cuts and material over a cavity are representable. Meshing still approximates the implicit surface, and coarse depths can visibly distort narrow grooves. Seeking backward reconstructs the expression from the initial block. The memo graph retains the latest result, not simulation history or a collection of meshes for earlier slider positions. In the native mesh preview, expression construction and stock meshing run in the general graph's background executor. Playback updates tool/path triangles immediately; old stock is desaturated until its replacement is ready. The first pending frame shows available tool/path geometry and an ellipsis. Cancellation uses Fidget's octree token, and superseded results are discarded. See the async boundary and limitations. Disconnected starts still have no linking cut. No holder or collision model is implied, and this models the programmed polyline, not controller-specific motion blending or physical cutting behavior.
The example demonstrates indent and chamfer finishing in two operations. Op 1 leaves the bottom indent and its four chamfers for Op 2. It is not yet a roughing program or a manufacturing simulation for the entire cube.
Variable orientation, collision checking for the non-cutting profiles, explicit links, and machine/postprocessor output remain separate next steps. Render-quality controls can use the controls library later; path-generation tolerance belongs to the program and is distinct from mesh/raster resolution. Upcoming-path windows and transparency are also deferred. In particular, the existing Fidget cube field is not an exact signed distance; subtracting a cutter radius from it would not implement a geometric offset.