Commit Graph
3782 Commits
Author SHA1 Message Date
Karim Jerbi 00e102607c fix(dist): respect units settings in DIST measurement output 2026-09-22 11:42:56 +01: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
Hakan Seven 0cbaf18551 Merge pull request #1425 from yztink:feat/plugin-run-command 2026-09-22 12:28:09 +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 ba8d828c3e Merge pull request #1424 from swit33:feat/trackpad-pan-pinch 2026-09-22 12:28:03 +03:00
Hakan Seven 925146134a Merge pull request #1429 from yztink:fix/direct-distance-entry-no-dyn 2026-09-22 12:28:02 +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 2db393155f CCONSTRAINTFORM system variable
Reads and sets the form new dimensional constraints take (0 dynamic,
1 annotational), the same state DCFORM sets.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-22 12:11:11 +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
yztink 33dfbf9230 fix(command): support direct distance entry when dynamic input is disabled 2026-09-21 23:00:04 -07:00
yztink 270b6102de fix(plugin): drive headless tasks, invalidate layer cache, and handle cancel event 2026-09-21 16:29:01 -07: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 2d49204c50 perf(grips): cap total selection grips at 4096
GRIPOBJLIMIT gates object count, but one dense polyline emits ~2
grips/vertex past it (~200K grips through the per-frame project + draw
path, ms-scale, no cache). Enforce a vertex budget once in
refresh_selected_grips: stable vertex-first priority, entity order
restored, handles truncated in lockstep. Below the cap selections are
byte-identical.

Tests:
- refresh_selected_grips_caps_total_grip_count: cap + handles lockstep
- refresh_selected_grips_prefers_vertex_grips_over_midpoints: vertices survive
- grip_budget_boundary_lengths: empty/cap/cap+1 edge cases

Bench (cargo bench grip, 200K-grip fixture): per-frame 6.8ms -> 0.13ms
(45-52x); select-time build 2.36ms full. A/B reference deleted.
2026-09-21 22:52:41 +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
Alexey Bagno 279d77a950 docs(trackpad): document the gestures and why a pinch bypasses iced
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.
2026-09-21 23:47:32 +03:00
Alexey Bagno 13b6a08e77 feat(viewport): two-finger trackpad pan and pinch zoom
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.
2026-09-21 23:47:32 +03: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
Chris YehandClaude Opus 5 4826cd7989 fix(pdf): Flate-compress the exported PDF streams
printpdf 0.9's `PdfSaveOptions::optimize` is a no-op — its `doc.compress()`
call is commented out — so every page's content stream (megabytes of vector
operators for a CAD sheet) was written uncompressed: a 16-sheet drawing set
exported as 280 MB. Serialise through `printpdf::to_lopdf_doc` and run
lopdf's `compress()` before writing; the same export is now 49 MB and
readers see ordinary FlateDecode streams.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 22:02:13 +08:00
Chris YehandClaude Opus 5 876513d009 fix(mtext): wrap between ideographs; paragraph indents and tabs in text-height units
Two layout defects that together broke every numbered note in a Chinese
drawing:

- Words were split on whitespace only, so a Chinese sentence — which has no
  spaces — was one unbreakable word. When it did not fit after "7.<tab>" the
  whole sentence dropped to the next line and still ran past the column.
  Ideographic text may break between any two characters except before a
  closing mark or after an opening one (the basic kinsoku rule); the atom
  splitter now emits those break opportunities.

- `\pxi…,l…,r…,t…;` paragraph indents and tab stops are multiples of the
  text height, and the first-line indent is relative to the left indent
  (a hanging list is `i-1.5,l1.5`). They were applied as raw drawing units,
  i.e. a fraction of a unit, so wrapped lines snapped back to the margin
  and the number/tab column collapsed. Scale them by the entity height and
  resolve the first line from the left indent.

Adds a layout test: a 40-ideograph paragraph in a 30-unit column wraps into
several lines, none wider than the column.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 22:02:13 +08:00
ramox81andClaude Fable 5.1 8e83b2f843 Aligned constraint: 2Lines turns the second line with the plain Parallel solve
Only the first line's ends are pinned while the second line is made
parallel, the way the Parallel constraint itself moves it; the distance
is then measured on the solved geometry.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 16:38:26 +03: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
ramox81andClaude Fable 5.1 72fd3fd35b Linear dimensional constraints: point flow, dynamic dimension, driving parameter
DCLINEAR, DCHORIZONTAL, DCVERTICAL and DCALIGNED follow the reference flow:
`Specify first constraint point or [Object] <Object>:`, `Specify second
constraint point:`, `Specify dimension line location:`, then `Dimension
text = …` and `Enter value or name and value <d1=100>:`. Linear reads
horizontal or vertical from the dimension line location the way a linear
dimension does; Object takes a line's, an arc's or a polyline segment's
ends. A miss reports `No valid constraint point found.` / `No object
found.` and asks again; another object type reports `Invalid selection for
Linear. Select a line, polyline segment or arc.`.

Each constraint creates a drawing parameter (`d1`, `d2`, …, or the typed
`name=expression`), drives the constraint through it, and draws a dynamic
dimension in constraint gray whose text follows CONSTRAINTNAMEFORMAT
(`d1=100.0000`); the dimension is associated with the constrained objects
and its text is refreshed after every solve. A typed value other than the
measured one moves the second point while the first stays, and so does a
later parameter edit. Deleting the constraint removes its dimension and,
when nothing else reads it, its parameter.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 11:53:19 +03:00
ramox81andClaude Fable 5.1 959d0011d6 Equal: show the bar of a Multiple pick at once, like the two-object flow
A fresh reference session shows the "=" bar of every object made equal
through Multiple right away; the earlier hidden state was not the rule.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 09:58:57 +03:00
ramox81andClaude Fable 5.1 842ae461fc Equal: ask for the second object again after a refused pick, name the kind it needs
A second object of the wrong kind (a circle after a line, a line after a
circle) or a pair that already carries Equal now reports and asks for the
second object again instead of ending the command. The mismatch message
names what the first object asks for — `Select a line segment, polyline
segment, text, MText, major or minor axis of ellipse or elliptical arc.`
after a length object, `Select a circle or arc.` after a radius object —
and the same-pick message carries the reference's double space.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 09:46:13 +03:00
ramox81andClaude Fable 5.1 ad6f902ed7 Equal follow-ups: one glyph per object, unshown Multiple bars, typed object picks
- An object that carries several Equal relations shows a single "="
  badge, as the reference does, instead of one stacked badge per relation.
- A pick made in the Multiple flow leaves its new constraint bar unshown
  until the object is selected or bars are shown, matching the reference;
  the single flow still shows the bar at once.
- A typed coordinate at the Fix `Select object:` prompt and at the
  Horizontal/Vertical `Select an object or [2Points]` prompt picks the
  object under that point, like a click, instead of being read as a point.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-21 09:18:40 +03:00
yztink bc6ad1a5fe fix(command): drain pending pause tokens on viewport click and coordinate input 2026-09-20 21:51:25 -07:00