Commit Graph
15 Commits
Author SHA1 Message Date
Karim Jerbi ddf348af22 fix(text): stroke aliases txt/monotxt must ignore a same-named TTF
Face::resolve routes a name to a system TrueType face when installed_alias
reports that an alias names a real TTF face (the #1764 escape hatch, added so
an installed ISOCPEUR draws that face instead of its LFF stand-in).

That hatch also caught TXT/MONOTXT: on a machine with a font family matching
"txt" (e.g. a "Txt Regular"), the DXF stroke text font resolved to that
TrueType face instead of the embedded stroke substitute, contradicting the
documented contract that a missing .shx draws with the stroke substitute and
never with a similarly-named TrueType face.

TXT/MONOTXT are stroke aliases, so exclude them from the hatch. ISOCPEUR keeps
its #1764 behaviour.

Eight tests failed on such a machine and now pass: the four font-face/LFF
assertions (shx_name_stays_lff, lff_falls_back_to_ttf_for_uncovered_glyph,
big_font_pairing_routes_ideographs_and_leaves_latin_alone,
lff_tessellation_golden) and the four searchable-PDF assertions that route
through the same Face::resolve. Adds a regression test for the contract.
2026-10-17 05:56:14 +01:00
ramox81andClaude Opus 5.5 416ec43c38 fix(pdfshxtext): .shx-only font resolution, oversized-object rule, mean text height
A listed font is compared only from its .shx file or the embedded stroke
font of that name; no TrueType or alias stand-in. Objects with a diagonal
over five times the selection's mean diagonal join no group. The created
MTEXT height is the mean of the character sizes. Text styles look a bare
font name up as .shx first, and a missing .shx name never draws with a
TrueType face. TXT2MTXT measures TEXT to its last inked stroke.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 13:53:36 +03:00
Hakan SevenandClaude Opus 5.5 1037be2d53 Text: an installed font beats its LFF stand-in (#1764)
ISOCPEUR, ISOCTEUR and the other aliased names are real fonts; with one
installed, its face draws the text instead of the stroke substitute,
which has no Cyrillic.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:32:09 +03:00
Karim Jerbi 9cf9ff2c5c style: apply cargo fmt across workspace 2026-10-11 03:14:29 +01:00
Hakan SevenandClaude Opus 5.5 864c644222 review(#1629): one direction mark, no dead RTL paragraph path, tests follow the new layout
- One visible-named LRM constant (ttf_glyph) instead of two invisible
  literals. Stroke and SDF text skip it, so a right-to-left script the
  shaper does not handle no longer gets a 6-unit gap per mark; vertical
  MTEXT does not insert it at all.
- Shaped cells keep a cluster the shaper merged into the leading mark.
- The always-false RTL paragraph flag, its indent/anchor branches and the
  unused reorder_line_atoms are gone; its test now covers reorder_rtl_runs.
- Underline, overline and strike follow each drawn run of a mixed word.
- Justify/distribute count only the gaps the measured width holds.
- The two RTL alignment tests assert the new left-to-right behaviour;
  glyph_boxes keeps its own doc comment.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 20:49:01 +03:00
Hakan SevenandClaude Opus 5.5 3769bb59e3 review(#1631): drop the repeated ROMANS table; tests follow the new advances
- The ROMANS advance list was pasted twice; once is enough.
- The stroke-font golden checksum follows TXT's new advances (segment and
  vertex counts unchanged).
- The tolerance compartment test checks ink against pen only for symbol
  cells: text fonts now advance by the reference's pen, not their ink.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 20:36:07 +03:00
ramox81andClaude Opus 5.5 c83d37b24d Text: stroke font character advances of the reference
txt, simplex and romans keep the reference fonts' printable ASCII pen
advances, so lines measure, wrap and align like the reference.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 15:17:39 +03:00
ramox81andClaude Opus 5.5 de65cd7e50 Text: stroke font space widths of the reference
The SHX names the embedded LFF fonts stand in for (txt, monotxt,
simplex, romans, romand, romanc, complex, italic, gdt, isocp) use the
space advance of the reference's fonts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 14:33:59 +03:00
Hakan SevenandClaude Opus 5.5 89512c2f20 fix(text): no stroke-font tracking after big-font and fallback glyphs (#1470)
Every glyph advanced by the face's letter spacing, including CJK glyphs a
style's big font or the TrueType fallback supplies. Their advances already
carry the gap (big font) or side bearings (TrueType), so with an LFF primary
such as iso each ideograph took 1.33 heights instead of one.
Face::spacing_after(ch) gives the tracking only to glyphs the stroke font
draws itself; every layout path uses it.

The 1.8-height widths in the report were the TEXT bounds estimate counting
UTF-8 bytes; opencadcodec 1109d52 counts characters.

Pins opencadcodec 1109d52, opencadkernel 8a246b0, opencadgraph 3c4af1c.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 21:27:17 +03:00
Hakan Seven 53cf31f33d feat(web): unify fonts and refine viewcube
Preload one language-aware font resource across the interface, drawing text, and navigation labels.\n\nRender the navigation cube, ring, outlines, and labels through a 4x MSAA target.
2026-08-03 15:28:51 +03:00
Hakan SevenandClaude Opus 4.8 c10e8dae44 fix(tolerance): draw the feature control frame properly
A tolerance drew one row of ASCII soup — "RUN | ANGtol(M) | ..." — about
a tenth of its proper size. Six defects, each independent.

Rows were split on the two-character string "^J". A DWG stores a raw
newline, and the text-DXF reader rewrites the on-disk ^J to one before we
see it, so that split never fired on either mainstream path and every row
collapsed into the first. It now normalises all four spellings the break
arrives in — including the \P our own DXF writer emits, which made
open-DWG → save-DXF → reopen asymmetric.

Symbols were replaced with ASCII labels from a table that was itself
wrong: it read r as circular runout, n as angularity, p as position. The
font's own encoding says concentricity, diameter, projected tolerance
zone, and the benchmark's pre-exploded reference agrees. That table was
an inversion of acadrust's, which is self-contradictory — it binds n to
two different meanings. Do not re-derive from it.

The frame is a proportion of the character height: a compartment spans
-h..+h about its row's centreline, so it is 2h tall and the margin falls
out of that. It was built as height + 2*DIMGAP instead, which is a
different entity's geometry — DIMGAP sizes the dimension line's break and
the rectangle around a basic dimension, where it genuinely applies twice
per axis. Even fed a self-consistent style that can only ever produce
1.5h, so it was wrong in kind rather than mis-fed.

Height came from the entity's own field, which a DWG never sets — the
reader carries only the style's handle, leaving a small non-zero default
that a "> 0" test happily used. It now resolves the style by handle first
and name second: a DWG records the handle and defaults the name, a DXF
the reverse, so matching on the name alone would have picked the wrong
style for every DWG-read tolerance. An entity may also override single
style variables on itself, and this one halves its character height that
way; ignoring that drew the frame several times too large.

Rows stacked upward, every row inherited the widest row's columns, and
one full-width border boxed them all — none of which showed while the
parse only ever produced a single row. The insertion point sits on the
first row's centreline; anchoring the frame's bottom-left there offset it
by its own height, and by an amount that changed with the row count.

Cell text ran together because the glyph run asked for no letter spacing
while the width measurement assumed the font's own, so the characters
crowded inside correctly-sized boxes. Compartments are also measured by
what they draw now: the pen advance stops one letter-space past the last
glyph, and counting that made every one of them too wide.

Symbols come from a bundled stroke font, restored from this repository's
own history: it shipped QCAD's gdt.cxf until the text engine moved from
CXF to LFF, which removed every .cxf and left nothing in their place —
the ASCII labels date from exactly there. Converted to LFF, keyed by the
letter its \Fgdt;X escape carries, and registered under that name like
any other font, so a switch to it resolves through the ordinary lookup:
an MTEXT carrying the escape, or a text style named gdt.shx, reaches it
too, with no code of their own. The geometry is public domain by explicit
waiver; provenance is in the file's header.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-15 22:22:07 +03:00
Hakan SevenandClaude Opus 4.8 d528a3e2a5 feat(text): parse TEXT via acadrust + extend SDF text to MTEXT
Two related steps for the text pipeline:

- TEXT parsing now goes through acadrust's parse_plain_text (like MTEXT
  uses parse_mtext). text::acad_text_encode parses the value's %% codes —
  including %%u/%%o underline/overline and %%nnn — and re-encodes to the
  stroke tessellator's \L/\O + resolved-Unicode grammar, so OCS no longer
  tokenizes TEXT %% codes itself. (ATTRIB/TOLERANCE/DIMENSION still use the
  local tokenizer for now.) Bumps the acadrust pin to the commit that
  completes the TEXT/MTEXT control-code set.

- SDF text (OCS_TEXT_SDF) now covers MTEXT and every text-producing
  entity, not just top-level TEXT. TextStroke carries a GlyphRun (layout
  params); the collector walks convert()'s TruckObject::Text runs and
  applies annotation scale exactly as tessellate does for strokes.
  layout_glyph_quads shares lff::tokenize_run so %% specials resolve and
  \L/\O decorations are skipped (decoration LINES in SDF are still TODO).

Verified: stroke rendering unchanged with the flag off; SDF glyphs render
correctly for TEXT and MTEXT with OCS_TEXT_SDF=1. 154 lib tests green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-07 00:53:19 +03:00
Hakan SevenandClaude Opus 4.8 79a5ac6fed fix(scene): correct MIRROR of TEXT under MIRRTEXT (commit + preview)
Root cause: the glyph tessellator advanced the pen by advance*width_factor
while the transform already scales the pen position by the width factor, so
a backward (negative width factor) run kept its word order and grew on the
same side of the anchor instead of mirroring across it. Advance in unscaled
glyph units to match text_local_bounds; the transform applies the factor once.

On top of that:
- The renderer honours the entity's own text-generation flags (DXF 71: bit 2
  backward / bit 4 upside-down) XOR the text style's flags, and signs the
  horizontal anchor by the width factor so Center/Right backward text mirrors
  about its anchor.
- MIRRTEXT on: a MIRROR toggles both group-71 bits so the reflected rotation
  renders a true glyph mirror (backward-upright for a vertical axis, flipped
  for a horizontal one).
- MIRRTEXT off: the text flips left<->right and lands mirror-symmetric to the
  source (right-reading) instead of hugging the axis.
- The MIRROR preview re-derives text ghosts to match the commit; mirrored()
  now reflects the double-single residual too.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-03 12:42:55 +03:00
Karim Jerbi 627761b629 fix(text): render TTF glyphs as solid fills, fix font fallback, correct Z-order
Text elements previously rendered as hollow wireframe outlines and sometimes
fell back to the wrong system font due to case-sensitive exact matching. Also,
the viewport background grid drew on top of solid entities.

Case-insensitive and partial-name fallback matching (e.g., mapping "arialn"
to "Arial Narrow") is now added to `sysfont::canonical_family_name`, ensuring
text resolves to the correct installed system font.

TrueType font contours are now triangulated into solid fills (`fill_tris`)
using `lyon_tessellation` in `ttf_glyph.rs`. To prevent the GPU from misclassifying
this 2D text as a 3D mesh (which broke flat shading), text tessellation splits
the output into separate outline and fill `WireModel`s.

The background grid is extracted from `SelectionCanvas` into `GridCanvas` and
moved beneath the 3D viewport to ensure proper layering. Finally, `DepthBiasState`
is enabled on all face/fill pipelines to apply a polygon offset, perfectly
resolving Z-fighting between coplanar wireframe lines and solid faces.
2026-06-25 18:17:01 +01:00
Hakan SevenandClaude Opus 4.8 7fe7e0230d refactor(scene): group modules into role-based subfolders
Move the 30 flat modules under src/scene/ into six folders by role:
convert (entity→geometry), text (fonts/shaping), model (render models),
pick (hit-test/selection/grips/xclip), view (camera/transform/render),
cache (block + property caches). pipeline/ is unchanged.

Full path migration — all `scene::X` references become
`scene::<group>::X`, no compatibility re-exports. render.rs items that
were `pub(super)` (parent was scene) become `pub(in crate::scene)` to
keep their original scene-wide visibility.

No behavior change; build and tests are green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 11:33:46 +03:00