Sync current main and companion codec changes before preserving unsupported frame rows, rendering in the entity plane, and sharing final layout metrics with placement preview.
Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.
Pin Iced 0.15 so dialogs can measure content before distributing the resolved frame across child panels. Keep the web plugin notice compact and non-resizable.\n\nCloses #582
Paper content viewports with a circle / spline / ellipse clip boundary
drew their model content to the full bounding rectangle instead of the
shape. Each paper content viewport is its own render into its own screen
rectangle, so clipping is done per viewport: the boundary polygon is
projected into that viewport's render-target NDC (paper shape mapped
through the same visible-sub-rect crop as the content, so it stays fixed
to the viewport frame) and stamped into the stencil buffer with an
even-odd Invert, which marks the interior of any shape, convex or not,
without triangulation. Every content pass then draws only where the
stencil is set, so hatch, wire, mesh, 3DFACE, text, image, wipeout and
selection are all clipped uniformly.
Because Invert turns the cleared 0x00 into 0xFF, clipped content draws
with stencil reference 0xFF (unclipped content keeps reference 0). The
shared depth buffer moves to Depth24PlusStencil8 across every pipeline.
Rectangular viewports keep clipping via their own render rectangle and
XCLIP keeps its existing CPU wire clip; neither needs the stencil path.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Command previews (MOVE, COPY, ROTATE, SCALE, STRETCH, MIRROR, ARRAY,
PASTE, INSERT) build ghost WireModels via WireModel::translated/rotated/
scaled/stretched/mirrored, but those helpers moved only `points`, never
`text_verts`, and the ghosts' glyphs were never uploaded — so text
vanished (or froze at its source) during the preview, same root cause as
the grip-drag case.
Transform text_verts alongside points in all five helpers (shared
map_text_verts, reconstructing + re-splitting the double-single world
position), and gather preview_wires' glyphs into the per-frame
preview-text buffer in the render path. This covers every command
preview automatically. The grip-drag else branch no longer sets
preview_text explicitly (the render gather handles it); the grip-slide
fast path keeps its own scene.preview_text channel (empty preview_wires,
so no double-draw).
MIRROR honours MIRRTEXT: on -> true glyph mirror via mirrored(); off ->
readable relocation via translated().
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Dragging text by a grip hid it until release. The dragged entity is
hidden from the base wire set and shown as a preview overlay, but its
SDF glyph quads (text_verts) were never uploaded for the preview, so
the text vanished mid-drag.
Add a per-frame preview_text GPU buffer (sibling to preview_wires). A
rigid whole-entity move of pure text (TEXT/MTEXT, Square insertion grip)
slides the drag-start glyphs by the drag delta instead of re-shaping the
run every move; reshape grips (MTEXT width), dimensions and wire
entities re-tessellate and feed the same buffer.
Also fold preview_text into render_signature: a pure-text slide leaves
preview_wires empty, so without it the scene-render cache froze the
dragged text at its first frame (grip moved but text stayed, then
jumped to place on release).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Groundwork to render CAD text as per-glyph SDF quads (box-quad snap,
perf, no LOD) instead of stroke-polyline wires. CPU side fully tested;
GPU side compiles but is not yet wired into the render loop.
- scene::text::sdf_atlas — GlyphAtlas: shelf packer + R8 SDF tiles baked
per (font, char). Unified field so one shader draws both kinds: TrueType
= signed distance to the filled outline, LFF stroke = pen band. Uses only
crates already in the tree (ttf-parser/lyon via the existing Glyph).
- scene::text::glyph_quads — layout_glyph_quads: lays a run out as per-glyph
quads using the exact tessellate_text_run transform, so quads land where
the strokes would. Per-char advance (TTF shaping is a follow-up).
- shaders/text.wgsl — SDF fragment shader (smoothstep over fwidth), RTE
vertex path mirroring image.wgsl, per-vertex colour + draw-order bias.
- scene::pipeline::text_gpu — TextVertex + double-single vertex builder,
R8 atlas texture upload, bind group, and the image-cloned pipeline.
13 unit tests across the three CPU stages, all green. Modules carry
#![allow(dead_code)] until the render-loop integration lands.
Co-Authored-By: Claude Opus 4.8 <[email protected]>