Commit Graph
1405 Commits
Author SHA1 Message Date
ramox81andClaude Opus 5.5 498a97df0f Xref attach: placement preview, relative paths on the first save, no-match messages
The reference follows the cursor while its insertion point, scale or
rotation is asked (preview-only PScale/PX/PY/PZ/PRotate included); a
relative path chosen before the drawing had a file becomes relative on the
first save, and relative paths start from "./" at or below the drawing's
folder; -XREF name options report "No matching xref names found.".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-23 15:34:06 +03:00
ramox81andClaude Opus 5.5 6bd55bc32e Xref attach: Attach dialog, reference prompts, and xref drawings other applications can open
Attach External Reference dialog after XATTACH / ATTACH; ATTACH routes
drawings, PDFs and images; placement prompts and messages follow the
reference (presets, Corner/XYZ, nonzero check, existing definition reuse,
missing file, self-reference); name keeps the file's case; overlay and
path type at attach; one undo step for definition and INSERT; -XREF option
prompt and `?` table; XREF opens the palette; ER alias; xref Properties
rows; dependent symbols flagged and remapped and resolved content left out
of the saved file; dialog_ok automation action; localized labels.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-23 14:44:12 +03:00
Hakan SevenandClaude Opus 5.5 f2d534bf71 fix: review fixes for the Aligned 2Lines placement and DCCONVERT
The 2Lines placement (#1459) wrote the second line's new ends back with
`set_resolved_point`, which was not the inverse of the `resolve_point` it
paired with. `resolve_point` reads polyline vertices in WCS through the
polyline's OCS; the writer put that world point straight into the OCS
vertex, so a mirrored polyline (normal -Z, common in real drawings) had
its vertex flipped, and the closing segment's end marker — which names
vertex 0 on a closed polyline — found no vertex and the placement was
silently dropped. The writer now converts back into the OCS, keeps the
polyline's own elevation, and wraps the closing marker the same way.

The same arm pushed `test_pline_line_then_arc` past the default test
thread's stack: its locals, a cloned entity among them, sat in the frame
of the command-result dispatcher, which is on the stack of every click.
The placement is built out of line and handed over boxed.

Merging #1447 had left nothing setting DIMCONSTRAINT's default to
Diameter; the Dispatch arm #1461 turned into a match now covers it with
Radius and Convert. DCCONVERT's result lines go through the translation
macros.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-23 13:00:28 +03:00
ramox81andClaude Opus 5 48b8689fc3 Aligned 2Lines: place the second line where the reference leaves it
The second line kept its midpoint and turned around it, so it settled at a
different offset than the reference, which keeps the line's start along the
first line and moves it to the distance the picked point had from it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-23 09:47:56 +03:00
Hakan Seven 0f02a74eb1 Merge origin/main 2026-09-22 23:26:44 +03:00
Hakan SevenandClaude Opus 5 41ecc0c458 Merge pull request #1423 from felixriestra:pr/python-host-model
The bundled `opencad.python` plugin drives OCS through plugin API v7: all
43 canvas entity kinds, the drawing tables, and OCS's own commands run
step by step. RustPython stays in the plugin process — the plugin crate is
outside the workspace with its own lock file, so the application's own
dependencies are unchanged.

Reconciled with the plugin command work that landed as #1425, which the
branch was written before:

- Both added `impl Drop for PluginProcessInteractiveAdapter`, which git
  merges silently into two impls that do not compile. One body now
  cancels the command and then drops it from the runner, in that order: a
  plugin waiting on a step has to be released before the runner forgets
  the command.
- `execute_command` (#1425) and `run_command` (#1423) are both routes from
  a script to the command line, and only the second was guarded. The
  SCRIPTCOMMANDS switch and the denylist are shared now, so neither route
  is the unguarded one.
- `run_command` dispatched with plugin dispatch still enabled, so a script
  naming a plugin command re-entered the plugin that was blocked waiting
  for it. It sets the suppression flag for the length of the dispatch, as
  `execute_command` does.

The denylist read the command as typed, while the dispatcher resolves
aliases: a user-defined `Q` for `QUIT` (ALIASEDIT writes those) walked
past it. It resolves the alias first.

Also: the active-command comparison ran on every message, a mouse move
included, allocating two strings for names that are already `&'static
str`; SCRIPTCOMMANDS' own messages go through the translation macros; and
`docs/.DS_Store` (now ignored) and an unrelated RTF note about constraint
commands are out of the tree.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-22 23:26:30 +03:00
Karim Jerbi 9264559fd3 fix(boundary): clip unbounded sources in associative hatch refresh path
Same unit-stub defect as the BOUNDARY tool fix: entity_boundary_source_on_plane
tessellated rays/XLINEs to a single base-point stub. Now takes clip bounds
derived from the full source map, sharing segments_bounds and
live_segments_for_curves instead of duplicating the logic.

Tests:
- xline_source_clips_to_given_bounds: clipped to the box, not the stub
2026-09-22 20:41:35 +01:00
Karim Jerbi 652f4d6bbc feat(blocks): implement Block Definition dialog (BLOCK/BMAKE) with on-screen sequencing
Implement the complete Block Definition dialog window launched via BLOCK
and BMAKE commands, featuring standard CAD layout, workflows, and entity
creation pipelines.

- Dialog UI & Layout:
  - 2-column compact CAD grid with synchronized vertical heights (580 × 390px).
  - Editable combobox with existing block names, invalid character validation,
    and inline redefinition badge.
  - Multi-line text editor widget for block descriptions (replacing single-line input).
  - Quick Select (QSELECT) button integration restoring dialog state upon completion.
  - Confirmation guard when attempting to redefine an existing block definition.
  - Centered radio buttons, clean coordinate inputs, and accessible keyboard shortcuts.

- Base Point & On-Screen Workflow:
  - BlockOnScreenCommand strictly enforces prompt sequencing: Base point is
    prompted before object selection.
  - Dynamically recalculates world_to_block and block_to_world affine transforms
    when base point is specified on-screen, preventing stale (0,0,0) normalization.
  - Supports Retain, Convert to block (INSERT placement), and Delete modes.

- Explode Enforcement:
  - Guarded EXPLODE command to prevent decomposing blocks flagged as non-explodable.

- Dynamic Themed Icons:
  - Added pick_point.svg, select_objects.svg, quick_select.svg, and warning_triangle.svg.
  - Routed all icons through the semantic theming engine so line work (#B4B6B9)
    and accents (#6DB7ED) adapt automatically to the active theme.

- Tests:
  - Added unit tests for retain/convert/delete modes, redefinition, explodable flag,
    on-screen base point transform recalculation, and command sequencing.
2026-09-22 17:05:15 +01:00
Hakan SevenandClaude Opus 5 889a19c362 Merge pull request #1447 from ramox81:codex/diameter-constraint-parity
#1447 branched from the same commit as #1443 and rewrote the radial
dimensional constraint a second time, so the two designs had to be
reconciled rather than stacked. #1447's is the one kept: it covers
DCRADIUS and DCDIAMETER through one command, places the dimension by the
direction the location was picked in rather than storing a text point,
and names the parameters `radN` / `diaN`. #1443's radial command result,
its entity builder call and its follow-up code are gone; its own
independent work stays — the ribbon's small-button grid and tool
descriptions, the magnitude reading of a negative radius, and the centre
anchor a parameter edit resizes a circle about.

The DIMCONSTRAINT default follows #1443: running DCRADIUS or DCDIAMETER
directly leaves it alone, and the Dispatch arm sets it when DIMCONSTRAINT
is what asked for the command.

The second commit's radial drawing — the dimension line broken around the
text, the elbowed leader, the centre-mark and arrowhead rules — applied to
every radius and diameter dimension, not just a constraint's, and three
layout-parity tests said the old layout was the measured one. It is now
scoped to a dimensional constraint's own dimension, as #1443 scoped its
version, and ordinary radial dimensions draw as they did.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-22 17:51:16 +03:00
ramox81andClaude Opus 5 368aa69f4e Radial dimensions: text on the line, a short leader when it moves out
The text of a radius or diameter constraint sits on its dimension line,
which breaks around it, and no centre mark is drawn while that line is
there. Text that no longer fits inside moves beside the circle at the end
of a short leader that leaves the rim, elbows and hooks under the text,
with the arrowheads keeping the circle. A dynamic dimension now keeps
only its text at a constant screen size; arrowheads and extension lines
keep the style's size in drawing units.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-22 17:11:09 +03:00
ramox81andClaude Opus 5 19204d1b2c DCDIAMETER/DCRADIUS: the reference flow with a real radial dimension
The commands ask for the circle or arc themselves, report the measured
size, take a dimension line location and then the value or name and
value, creating a radius or diameter dimension as the constraint's
dynamic dimension with a radN/diaN parameter, refusing a second radial
constraint on the same object and any non-positive value.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-22 15:21:11 +03:00
Hakan SevenandClaude Opus 5 4bef410abb fix: review fixes for the metric template and the radial constraint
A new drawing (#1441) recorded ISO-25 as the current dimension style by
name only. Every other place that changes it sets the header's handle as
well, and a DWG carries the current style by handle alone, so a saved new
drawing reopened on Standard. The style's handle now comes back from the
helper that adds it.

A radius constraint (#1443) anchored the circle's centre from the shape of
its references — one whole entity — but a single whole reference is also a
line's length, whose start is what stays. The entity decides now.

The two tests that encoded the replaced rules follow them: a negative
radius validates by its magnitude, as a negative distance already did, and
only zero is refused; a new drawing's dimension text reads at ISO-25's
comma separator.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-22 13:46:49 +03:00
Hakan Seven 6605a71fdd Merge pull request #1443 from ramox81:codex/radius-constraint-parity 2026-09-22 13:37:57 +03:00
Hakan Seven 22806e1310 Merge pull request #1441 from ramox81:codex/metric-new-drawing-defaults 2026-09-22 13:37:56 +03:00
Hakan SevenandClaude Opus 5 fcbab57627 fix: review fixes for the merged command-line, plugin and font changes
Direct distance entry (#1429) turned every bare number at a point prompt
into a distance, so ROTATE's angle and the values automation types were
read as coordinates. The step reads the token first now — `feed_command`
reports whether it claimed it — and only an unclaimed one becomes a
distance. That also puts the token back on the unified step driver, which
owns the P / L selection keywords and the dimension-snap bookkeeping, and
stops `on_text_input` running twice for the same token. A tracking ray
carries its own base, so it no longer demands an anchor that the first
point of a command does not have yet.

Plugin command execution (#1425) re-implemented the command line inside
the plugin host: it dispatched families directly, skipping the teardown
that starts a command (grip edits, selection gestures, `last_point`, the
accepted snaps), and made every `CmdResult::Dispatch` in the application
skip plugin dispatch to avoid its own re-entrancy. A scripted line now
runs through `run_command_line`, with the trailing Enter left off while
the plugin streams the command, and the re-entrancy is handled by a flag
set for the length of the plugin's call. A pause parks the line's Enter
with the rest of its tokens instead of dropping it.

A font reference (#1432) tried the family lookup before the face-file
index, and that lookup ends in a prefix match a longer file name loses to
(`ISOCPEUI.TTF` would take `ISOCP`). The exact file answers first.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-22 13:35:25 +03:00
ramox81andClaude Fable 5.1 aab42e2aa9 DCRADIUS: reference radial dimensional constraint with a dynamic radial dimension
Select arc or circle / Dimension text / dimension line location / value or
name prompts with a radN parameter, invalid-selection, miss, duplicate and
invalid-expression messages, negative values as magnitudes, a dynamic radial
dimension on the constraints layer drawn as the reference does (mid-radius
text on the center-to-rim line, no hook, no center mark, one text grip), the
stored text position following the radius, center-anchored resizing, the
Properties title and Constraint section, DIMCONSTRAINT's Radius default,
the small-button Dimensional ribbon grid and tooltip descriptions.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-22 13:09:47 +03:00
ramox81andClaude Fable 5.1 8820437fa4 Metric new drawings with the ISO-25 dimension style current
A new drawing is metric (measurement 1, millimetre insertion units) and
carries the reference's ISO-25 dimension style as its current style, so
dimensions and annotational constraints draw at readable metric sizes
instead of Standard's imperial 0.18 text.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-22 12:29:38 +03:00
Hakan Seven d37ce4d617 Merge pull request #1431 from ramox81:codex/angular-constraint-parity 2026-09-22 12:28:04 +03:00
Hakan Seven 5719fcc86f Merge pull request #1430 from yztink:fix/viewcube-visibility 2026-09-22 12:28:01 +03:00
Hakan Seven 4a8f8af36c Merge pull request #1432 from swit33:fix/text-fallback-empty-outline 2026-09-22 12:27:59 +03:00
ramox81andClaude Fable 5.1 b435af64cd Dynamic angular constraint: screen-size extension lines, reference grips, Properties parity
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]>
2026-09-22 11:58:59 +03:00
Karim Jerbi 874dd9debf perf(scene): optimize view render environment resolution and wire splitting
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)
2026-09-22 09:27:47 +01:00
ramox81andClaude Fable 5.1 160930b99a Angular dimension drawing: ray-based three-point frame, text beside the arc
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]>
2026-09-22 10:41:52 +03:00
ramox81andClaude Fable 5.1 4ad4041858 Angular dimensional constraint: reference flow, dynamic angular dimension, angN parameters
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]>
2026-09-22 10:02:40 +03:00
yztink dad9c725c2 fix(viewcube): restore ViewCube visibility across display scales and window bounds 2026-09-21 23:39:48 -07:00
Alexey Bagno c0dccff14a fix(text): never accept a fallback glyph that has no outline
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.
2026-09-22 09:39:43 +03:00
Alexey Bagno f954c7f495 fix(text): resolve a style's font file name against installed faces
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`
2026-09-22 09:39:43 +03:00
Karim Jerbi 960d51d955 fix(boundary): clip rays/XLINEs to finite extents for face detection
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
2026-09-21 23:30:57 +01:00
Karim Jerbi 4432f633e7 perf(glyphs): memoize constraint-glyph placements behind constraints_epoch
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.
2026-09-21 22:52:47 +01:00
Karim Jerbi dcc05f310d perf(statusbar): cache layout/scale derived data behind epochs (major)
Status bar rescanned the whole object map twice per frame (layout_names +
scale_picker_list), deep-cloned an already-cached Arc, and built two
undo/redo label vecs consumed ~never. Cache layout names (epoch key) and
scale picker (epoch + unit-factor + imperial-family + current-scale key)
as Arc behind choke-point invalidation (layout/scale impl methods,
undo/redo swaps, file-open, doc-reset); borrow across the widget boundary
(owned-Arc params; E0515 forbids &'a slices over call-site temporaries);
build undo/redo labels only when their dropdown is open.

Tests:
- cached_layout_names_returns_same_arc_until_bump: hit-path Arc identity
- cached_layout_names_invalidated_by_add_layout: bump yields new Arc + names
- cached_scale_picker_returns_same_arc_until_bump: hit-path Arc identity
- cached_scale_picker_invalidated_by_add_scale: bump yields new Arc + scale
- cached_scale_picker_invalidated_by_current_scale_change: key completeness

Bench (cargo bench ui_statusbar_derived_data, 3k entities): cached hit below
timer granularity vs ~1us uncached scans, PASS (50us threshold).
2026-09-21 22:31:16 +01:00
Felix Riestra b1e6d2a349 Merge remote-tracking branch 'origin/main' into plugin/host-model-api 2026-09-21 22:20:09 +02:00
Hakan SevenandClaude Fable 5.1 baba5bccd3 build: reach lopdf and encoding_rs through printpdf and the codec
#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]>
2026-09-21 22:55:04 +03:00
Hakan Seven 697ea4984b Merge pull request #1418 from Chrisyeh1983/fix/viewport-wide-polyline-plot-width
fix: wide-polyline plot width through viewports; CJK text (big fonts, fallback size, atlas growth, MTEXT wrap/indents); compressed PDF
2026-09-21 22:49:31 +03:00
Hakan SevenandClaude Fable 5.1 4f12087539 test(constraints): a negative distance value validates by its magnitude
#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]>
2026-09-21 22:49:31 +03:00
Hakan Seven 7490b2bebd Merge pull request #1415 from ramox81/codex/aligned-constraint-parity
Aligned dimensional constraint: Point & line, 2Lines, value and property parity

# Conflicts:
#	src/app/command_driver.rs
#	src/app/update/viewport.rs
#	src/entities/dimension.rs
#	src/scene/parametric_constraints.rs
2026-09-21 22:41:42 +03:00
Hakan Seven 0ee8b15511 Merge pull request #1413 from ramox81/codex/linear-constraint-parity
Linear dimensional constraints: point flow, dynamic dimension, driving parameter, persistence

# Conflicts:
#	src/scene/parametric_constraints.rs
2026-09-21 22:36:20 +03:00
Hakan Seven 2208be9506 Merge pull request #1409 from ramox81/codex/equal-followups
Equal follow-ups: one glyph per object, unshown Multiple bars, typed object picks
2026-09-21 22:35:00 +03:00
Hakan SevenandClaude Fable 5.1 e537a495c6 fix(constraints): point symmetry converges from any start
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]>
2026-09-21 22:21:19 +03:00
Hakan SevenandClaude Fable 5.1 ececb0ef8c ci: run the workspace tests on every change; drop the unused flake
- .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]>
2026-09-21 22:21:19 +03:00
Felix RiestraandClaude Sonnet 5 6b8056cfeb Merge origin/main into plugin/host-model-api
Conflicts were additive: main's AddLayer/ModifyLayer (LayerConfig, OptHandle)
alongside this branch's v7 system-variable, transaction, selection and solid
operations. Main's variants keep their positions in the IPC enums; the v7
additions follow them. src/io/mod.rs takes main's reworded Shape-rotation note.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-21 15:12:58 +02:00
ramox81andClaude Fable 5.1 5fab97a7d0 Aligned constraint: axis lines, zero collapse, Properties form/reference/description, screen-sized text
Point & line and 2Lines take a text baseline or an ellipse axis as the
line; a zero value folds the points onto the first one; 2Lines turns the
second line about its start. The Properties Constraint section edits
Constraint Form (Dynamic on the *ADSK_CONSTRAINTS layer, Annotational on
the current layer), Reference (driven constraint in parentheses whose
parameter follows the measurement) and Description (stored with the
parameter and the native variable). Dynamic dimensions keep a 12 px text
and 8 px arrows at any zoom, drop trailing zeros and show formulas as
`fx: len=d1*2`; the hidden constraints layer stays out of the layer lists.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 16:04:26 +03:00
Chris YehandClaude Opus 5 24f8e124d5 fix(text): re-lay-out cached glyph quads after the SDF atlas grows
Glyph quads bake the atlas UV of their tile. `grow_height` rescales every
cached entry's V and bumps the atlas generation, and the tessellation memo
folds that generation in — but the caches that hold finished wires do not:
the resident wire set is keyed by geometry epoch, block definitions by
block epoch, and a full build can grow the atlas part-way through (a CJK
sheet set bakes thousands of tiles on first sight). Text tessellated before
the growth then samples the wrong tiles for as long as those caches live,
which on a big drawing is forever: whole tables of scrambled glyphs, and
the block contents stayed scrambled even after a REGEN.

- `resident_wires_for` rebuilds (dropping the block definition cache) until
  the atlas generation is stable across the pass.
- `Scene::update` compares the live generation with the one the scene's
  caches were built against and bumps geometry once when it moved, so a
  later growth (opening a layout, editing text) re-lays-out everything.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 21:01:05 +08:00
Chris YehandClaude Opus 5 ff4981ffe6 feat(text): read unifont and big-font .shx files; pair a style's big font
`shx.rs` only accepted the classic `AutoCAD-86 shapes 1.x` container, so
every modern Autodesk font (`txt.shx`, `simplex.shx`, `romans.shx`, … are
`unifont 1.0`) and every Asian big font (`chineset.shx`, `hztxt.shx`,
`bigfont.shx` — `bigfont 1.0`) was rejected and text fell back to the LFF
substitutes and TrueType fallback glyphs. Drawings authored with SHX big
fonts (the norm for Chinese / Japanese / Korean sheets) never showed their
real glyphs.

- Parse all three containers. Unifont glyphs are keyed by Unicode; big-font
  glyphs by their double-byte code from a sparse hash table of
  (code, length, offset) entries. Big fonts reference subshapes with a
  two-byte `7, 0, hi, lo`.
- `bigfont_glyph` encodes a character with encoding_rs (Big5 / GBK /
  Shift_JIS / EUC-KR, most likely first for the file name) and takes the
  first code the font has.
- `Face::WithBig` pairs a style's big font with its primary face: full-width
  characters come from the big font, everything else from the primary —
  how a STYLE with a "Big Font" behaves in AutoCAD. `resolve_text_style`
  resolves `big_font_file` like the primary (next to the drawing, then the
  user fonts folder) and rides it along in the font name.
- The missing-fonts dialog lists unresolvable big fonts too.

Unit tests cover a synthetic big font (Big5 lookup, cap-height scaling, no
aliasing of unknown codes), a synthetic unifont, and the pairing.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 21:01:05 +08:00
ramox81andClaude Fable 5.1 3e28604752 Dimensional constraints: DCFORM, DIMCONSTRAINT options, -PARAMETERS, value prompt, save without display overrides
DCFORM sets the annotational or dynamic form and continues into the
DIMCONSTRAINT option prompt, whose wording and last-used default match the
reference. -PARAMETERS manages parameters from the command line; a
double-clicked constraint dimension opens its value prompt. Dynamic
dimensions rescale on every camera change and their display overrides stay
out of the saved file. A driving dimension between an entity's own ends no
longer has its length retained, so a new value moves the second point along
the dimension axis; parameter edits hold the first constraint point.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 15:08:44 +03:00
ramox81andClaude Fable 5.1 6969c19746 Aligned dimensional constraint: Point & line, 2Lines, value and property parity
DCALIGNED opens with `[Object/Point & line/2Lines] <Object>`. Point & line
asks `Specify constraint point or [Line] <Line>:` then `Select line:` (or the
line first and `Select constraint point:`), measures perpendicular to the
line from the constraint point to the line end nearest to it, and stores a
directed distance with the line as direction reference. 2Lines asks
`Select first line:` and `Select second line to make parallel:`, adds a
Parallel constraint solved with the first line pinned, then measures on the
solved geometry between the nearest ends. Unsupported line picks report the
reference's message; the same measurement again reports `The constraint
already exists on the selected objects.` and ends the command; an invalid
expression reports both reference lines and asks again.

The offered default and seeded expression carry twelve significant digits;
a typed value moves the second point (or the other object) while the first
point and its measured line stay, negative values keep their sign in the
parameter and use their magnitude, zero folds the points together, and the
dynamic dimension's origins follow every solve. The lock mark sits after the
text, a selected dynamic dimension shows triangle grips at its points and the
text square, and its Properties show the Constraint section (Name and
Expression editable) plus Text rotation.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 14:52:37 +03:00
ramox81andClaude Fable 5.1 aa51765a62 Dynamic dimensions: reference text format, horizontal text, lock after the text
A plain number shows as typed (d1=100), an expression under the fx: prefix
(fx: d2=d1*2); the text stays horizontal through DIMTIH/DIMTOH overrides
and the lock mark follows the text.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 13:57:06 +03:00
ramox81andClaude Fable 5.1 0b5c686371 Dynamic dimensions: constant screen size, transform keeps the second constraint point
Every dynamic dimension gets a DIMSCALE override for 12 px text, refreshed
per zoom band, at creation and after load. A SCALE/ROTATE of an entity whose
own end points a driving dimension measures now pins its second constraint
point so the dimension moves the first end, as the reference does.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 13:36:01 +03:00
Chris YehandClaude Opus 5 0b522ceb86 fix(text): size TrueType fallback ideographs by the em box, like a big font
When a text style's SHX / stroke font lacks a character, the glyph comes
from a system TrueType font through `ttf_glyph::fallback_glyph`. That
substitute was normalised to the Latin cap height (9 units = text height),
which is right for Latin, Greek or Cyrillic stand-ins but wrong for CJK:
an ideograph fills the whole em square, and 1 em is 1.3–1.5 cap heights in
every common CJK font (DFKai-SB has no OS/2 cap height at all, so it fell
to the 0.7 em guess). Every substituted Chinese character therefore came
out ~40 % taller and wider than the big font (`chineset.shx`, `hztxt.shx`,
…) the drawing was authored with, and whole lines of notes ran past their
frames.

A big font draws an ideograph one text height tall and one text height
wide, so size full-width / ideographic fallback glyphs by their em box
instead. Other scripts keep the cap-height normalisation. Real TrueType
styles (`Face::Ttf`, shaped runs) are unchanged.

Adds a unit test that a fallback `中` advances by about one text height and
its ink fits the text height (skipped on machines without a CJK font).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 18:14:26 +08:00
Chris YehandClaude Opus 5 52d766b488 fix(plot): scale wide polyline band width into paper units through viewports
`viewport_content_wires` projects model-space wires into paper coordinates
for plotting: the points and the linetype stations are multiplied by the
viewport scale, but `world_width` (the band width of a wide polyline) was
copied unchanged. The PDF / print exporter strokes that band at
`world_width` in paper millimetres, so a 15-unit-wide bus bar seen through
a 1:100 viewport came out as a ~21 mm black blob instead of 0.15 mm, and
every single-line diagram sheet plotted as solid black shapes.

Scale `world_width` by the viewport scale alongside the points, and keep
the per-point taper widths in step (or drop them when clipping changed the
vertex count, so they cannot misalign). Negative (fixed pixel) and zero
(lineweight) widths are left untouched.

Adds a regression test that plots a 15-unit-wide polyline through a 1:10
viewport and expects a 1.5-unit band in the projected plot wires.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 17:28:09 +08:00
ramox81andClaude Fable 5.1 971abe64a4 Dimensional constraints: dynamic dimension persistence, lock mark, display variables, DIMCONSTRAINT
The dynamic dimension of a dimensional constraint is now linked in the
drawing file (AcDbAssocDependency + AcDbAssocDimDependencyBody on the
constraint node), reread on load, hidden by DCHIDE/DYNCONSTRAINTDISPLAY
and never plotted; it carries a lock mark before its text. New
CONSTRAINTNAMEFORMAT and DYNCONSTRAINTDISPLAY variables and the
DIMCONSTRAINT option front end. A transformed entity whose own end points
a driving dimension measures keeps only the first constraint point, so a
scaled line returns to its dimension value.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 12:13:36 +03:00