Dynamic dimensions carry DIMEXO/DIMEXE/DIMASZ overrides in text-height
ratios, a two-line angular dimension draws extension lines only beyond
its sides, a selected dynamic angle shows triangle grips at the arc ends
with squares at the arc point and text, and the constraint dimension's
Properties follow the reference: kind titles, General before Constraint,
angular-precision Value and Measurement, drawn text position, reference
row order for Fit, Primary Units and Tolerances, and text/dimension line
alignment rows reading On as the aligned state.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Eliminate major performance bottlenecks in the per-frame view render path:
- Memoize document render environment resolution using (geometry_epoch, len) keying, replacing per-viewport O(N) linear scans over document.objects.
- Index render_environments and render_settings handles in ObjectDataCache during the single build_cache pass.
- Cache resolved background image lookups in memory to eliminate repeated path.is_file() filesystem syscalls per frame.
- Add has_face3d fast-path to bypass wire splitting when no Face3D entities exist, and use FxHashSet<u64> handle indexing when present.
- Invalidate all cached structures across bump_geometry, bump_geometry_no_blocks, and bump_entities.
- Add comprehensive correctness unit tests and before/after benchmarks.
Performance impact (10k objects):
- P95 latency reduced from 341.00 µs to 173.00 µs (-49.3%)
- Throughput increased from 4,700 FPS to 6,000 FPS (+27.7%)
- Direct render environment evaluation: ~85 µs down to <50 ns (>1,700x)
Three-point angular dimensions take their sweep from the two rays and the
arc point (extension lines follow their own rays, straight and reflex
angles draw), dynamic angular dimensions store no text point so DIMTAD
places the text beside an unbroken arc, and dynamic dimensions carry a
DIMGAP override of a quarter text height.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
DCANGULAR picks two lines, an arc or three points, takes the dimension
line location to choose the sector and the value or name, and creates the
dynamic two-line or three-point angular dimension driven by an angN
parameter. Reference messages for invalid, repeated and parallel sides,
existing names and invalid names; angle values display within one turn at
the angular precision; the first side (or vertex and first point) stays
when the value changes; the ribbon Dimensional panel follows the reference
order.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
cosmic-text's fallback search can land on a face that claims the character and
outlines to nothing: macOS's system font does that for every glyph, because
ttf-parser reads no contours out of it. The code took the first non-.notdef
glyph id and stopped, so the character drew nothing while believing it drew
something — invisible text, and no warning either.
An empty outline is now treated as no answer: the search moves on to the next
face, and when cosmic-text's own list has nothing that draws the character, the
installed families are walked for one that does. That list is the platform's own
handful of names (on macOS `.SF NS`, `Menlo`, `Apple Color Emoji`, `Geneva`,
`Arial Unicode MS`) and covers neither Cyrillic nor Greek, however many fonts the
machine has that do.
`cargo test --lib a_fallback_glyph_is_never_empty`, and the Turkish letters in
`lff::tests::fonts_parse_and_resolve` render on macOS now instead of failing.
A DWG text style stores its TrueType font as a *file* name — `GOST2304_TypeA_italic.ttf`,
sometimes with a directory — which is what AutoCAD resolves. The family lives
inside the file and is usually a different string (`GOST2304 Type A`), so looking
the reference up as a family missed an installed font and fell through to the
embedded stroke fonts. Those carry no Cyrillic at all (`simplex`, `standard`,
`romans`, `iso3098` hold no U+04xx glyph), so a Russian drawing came out with
every word missing: the letters went on to the system-font fallback, which
returns nothing on macOS.
Installed faces are now indexed by file name, and a reference — a style's font
file, or an inline `\f` code, which carries either form — resolves by family
first and by file name second. An embedded stroke font still wins, so
`romans.shx` keeps rendering as it did.
`cargo test --lib face_files_resolve_to_their_family`
Infinite construction lines tessellated to a single unit stub at the base
point, which can never close a face (and overwrote the real wire segments),
so XLINEs were silently ignored by BOUNDARY. Clip unbounded curves to the
finite extents via Liang-Barsky; lines missing the geometry contribute
nothing. Same shared helper fixes RAYs.
Tests:
- xline_splits_rectangle_into_two_faces: 10x10 rect split in two
- ray_splits_rectangle_into_two_faces: upward ray splits in two
- xline_missing_the_geometry_contributes_no_face: distant xline is a no-op
Placements (entity lookups, intersections, String labels, projections)
recomputed per view() and per mouse-move hit-test, with overlay re-clones
and 2-3x offset recomputes. New constraints_epoch bumped on every set
mutation/visibility/undo/DWG-load (plus new set_constraint_enabled API);
single-slot Arc<[GlyphEntry]> memo on an 11-field key (hover/selection
excluded); overlay de-cloned/de-duped to the single geometry source;
hit paths reuse the cache.
Tests:
- cached_glyph_placements_hit_returns_same_arc: same-key Arc identity
- cached_glyph_placements_invalidated_by_metadata_edit: edit + selection change yield new Arc
- glyph_hit_test_entries_matches_precomputed_boxes: shared hit-test helper
Bench (cargo bench glyph, 60 constraints): 8.18us -> 0.05-0.15us (~55x).
A/B reference deleted.
The pinch is read from AppKit rather than through iced, which is the kind of
decision that reads as arbitrary without the reason next to it. The file also
records what the pixel/notch split costs: a smooth-scrolling mouse pans with a
trackpad.
A trackpad could only zoom: every scroll delta, wheel notch or not, went
into the same zoom step, and a pinch never arrived at all. iced's winit
layer converts only the window events it has an iced equivalent for, so
winit's `PinchGesture` was dropped before the app saw it.
Scroll deltas now split by what produced them: a wheel reports notches and
still zooms, a precise-scrolling device reports pixels and pans. The same
pixel split keeps working everywhere else — the browser build reports every
wheel notch as pixels, so there they keep zooming.
The pinch is read straight from AppKit with a local `NSEvent` monitor, since
the app cannot see the gesture through iced at all, and handed to the app
through a subscription shaped like the SpaceMouse bridge. It maps 1:1 onto
the camera: fingers 5% further apart leave the view 5% closer.
The pan and zoom bodies are now shared by the mouse and the gestures, so the
middle-button drag, the wheel, two fingers and a pinch all move the view
through one path — in model space, in a tiled pane and inside a floating
viewport alike.
Remove personal names, local paths and fork-only process notes from code comments,
docs and the plugin manifest (repository now points at the upstream project), and
drop the handoff section from the plan.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
What it is, how to check it on Linux or Windows without a Mac, the reading order,
the small edits to existing code, what each area proves, the decisions that are the
maintainer's, and what is deliberately not done.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
#1418 added both as direct dependencies although each was already in the
tree: printpdf's to_lopdf_doc returns the lopdf document whose compress
and save_to are plain methods, and the codec's encoding_from_code_page
hands back the Big5 / GBK / Shift_JIS / EUC-KR encodings by their DXF
code-page names. Cargo.toml keeps its dependency list.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
#1415 made a dimensional Distance keep a negative value's sign in the
parameter while the geometry uses its magnitude (and a zero fold the
points together), but left the value-domain test asserting the old
refusal. Assert the new reading, and keep the radius refusal covered.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Runs the plugin API tests, the plugin crate and Python model tests, and the host's
real-IPC plugin tests against a freshly staged plugin and built runner, and fails
if either environment variable target is missing so the tests cannot skip silently.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Symmetric held the connecting direction perpendicular to the axis with
the kernel's angle-based Perpendicular, which locks its turn side at
construction: a point starting far from its mirror image had to swing
the pair through the axis direction, the residual wrapped, and the
solver stalled with the point unmoved. The perpendicularity is now the
projection of the pair onto the axis direction being zero, a polynomial
residual the solver follows from anywhere. Circle centers and ellipse
centers use the same equation. tests/parametric_constraints_solve.rs
symmetric_constraint_mirrors_one_circles_center_across_the_axis_line
passes again.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
- .github/workflows/ci.yml runs `cargo test --workspace --locked` on
pushes to main and on pull requests, with the Linux libraries the
release build installs. No formatter check: the tree is not
rustfmt-clean and stays that way.
- flake.nix, flake.lock and the direnv hook are removed. Nothing used
them, and their pinned dependency hashes were already stale after the
codec and kernel bumps.
- Under cfg(test) the "system" language resolves to en-US. The unit
tests compare English text through one process-wide loader, and every
apply_config of a default config asked for the system language, so a
Turkish locale failed 22 tests and parallel tests raced each other
through a language switch (the thickness-row test found its section
under one language and looked it up under another).
- The arc grip test follows #1352: the start, end and midpoint grips
drive the whole arc.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
On Windows the temp directory has backslashes, which a Python string literal reads
as escape sequences (C:\Users is a unicode escape), so the RasterImage and OLE
lifecycle tests could not even create their entity. Windows accepts forward slashes.
Found by the first Windows run of the fork build workflow; Linux passed all 60.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Portable builds (executable plus plugins/opencad.python where the host looks for
it) and the real-IPC plugin tests on ubuntu and windows. Started by pushing a
fork-build-* tag or by hand; publishes nothing. release.yml stages the plugin
only in the macOS bundle.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
A saved user preference (on by default) toggled by the SCRIPTCOMMANDS command
gates HostApi::run_command; Cancel stays allowed so a waiting command is never
stranded. The runner refuses the SCRIPTCOMMANDS command itself, so a script
cannot change it. Documents why no helper-thread or stack-growing dependency was
added: native builds link with at least 8 MiB, the nesting depth is fixed, and the
deepest step found needs 2 to 4 MiB in a debug build.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Drop two unneeded mut bindings in tests and mark the generated dict/enum
converters allow(dead_code), since a kind can leave one unused.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Rewrite the host-model design note for the current architecture (entities, tables,
commands, nested validation, known limits), refresh the plan's current-state header,
the ledger and gaps-doc status lines (cadcodec PR #48), the README status and the
superseded ocs.command notes, and drop dangling references to a sibling project that
is not in this repository.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
polyline_close/open/width/reverse/join (a line or arc is turned into a polyline
first), array_path and array_3d, written against observed prompts and checked
for exact geometry in the real-IPC audit. Polylines default their pick point to
the first vertex. The audit runs on a large-stack thread because PEDIT's
line-to-polyline step needs more than a test thread's 2 MiB in a debug build.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
The generated dict-to-struct conversions for nested records defaulted every
absent field and ignored unknown keys, so an LwPolyline built from
{'x': .., 'y': ..} vertices became zero-coordinate vertices. They now refuse an
unknown key (naming the keys the record takes) and a manifest list,
required_struct_fields, enforces the fields that define a nested record's
geometry (vertex locations, hatch edge ends and centres, section symbol points).
Regression test over real IPC; every existing lifecycle audit still passes.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Each is written against the prompt sequence observed in the exploration harness
and checked for exact geometry in the real-IPC audit. HATCH is left to
create_entity because its command needs an interactive boundary pick.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
HostApi::run_command drives the real OCS command through the same primitives
the automation channel uses, synchronously on the host thread: run a line,
start a command and answer point/text/token/entity/selection/enter prompts, or
cancel (closing anything the command opened). Refused while another command is
active, for a denylist (QUIT, NEW, OPEN, SAVE*, PY_* ...) and off the active tab.
Python: doc.command, doc.start_command and doc.modify (offset, trim, extend,
fillet, move, copy, rotate, scale, mirror, erase). Real-IPC audit checks exact
geometry for every wrapper plus guards and undo; IPC and Python unit tests; docs.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
TableOperation gains simple-linetype create/modify/rename/delete (standard names
taken, rename follows layers and entities, guarded delete) and paper-space
layout create/rename/delete/set-current/set-page (default page setup and sheet
viewport, the application's own layout switch, Model protected). CTAB now reports
the current layout. Real-IPC audit with DWG/DXF persistence and undo, IPC and
Python unit tests, docs.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
BlockEntityAdd adds a validated entity to a block definition (owner set by the
host, same validation as a model-space entity, self-nesting inserts refused),
reusing the scene's block-editor routing. Deleting a block member now also
drops its handle from the block record. Real-IPC audit with an attribute
definition inside a block, DWG/DXF persistence and undo; IPC and Python unit
tests; docs.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
TableOperation gains BlockCreate (copy entities into a definition shifted by the
base point, optionally erasing originals), BlockModify, BlockRename (inserts
follow) and BlockDelete (refused while referenced; removes members, markers and
orphaned draw-order tables). Real-IPC audit with DWG/DXF persistence and undo,
IPC and Python unit tests, docs; the DXF loss of the block description is pinned
by a canary.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Extends TableOperation with text style create/modify, dimension style
create/modify (DimStyle fields as a validated JSON object), rename, delete and
set-current. Reuses OCS's style in-use scan and reference-following rename
(four style_ops methods widened to pub(super)); a renamed text style also
updates dimension styles that name it. Real-IPC audit with DWG/DXF persistence
and undo, IPC and Python unit tests, docs. Four cadcodec findings are pinned by
canaries.
Co-Authored-By: Claude Sonnet 5 <[email protected]>