Replace full-document snapshots with Arc first-touch deltas and batch cache invalidation. Reuse interaction and GPU category caches across unrelated edits.
Solid hatches carrying stale GradientColor1/2ACI EED (no ACAD_XREC_ROUNDTRIP
XRecord) no longer resurrect as a two-colour gradient — red / ACI-253 solids
now render solid instead of blue→yellow.
Closes#471
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Heavy 2D POLYLINE vertices with absent width codes now inherit the
polyline default while explicit-zero segments stay zero, so tapered
polylines (e.g. sample_AC1009 handle 0x35B) render variable-width
instead of constant — matching the DWG/LWPOLYLINE form.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Companion to the acadrust DXF-fidelity sweep (bump to 379ce12):
- Legacy LEADER hookline: underline the annotation using the leader's own
stored text width (DXF code 41) when the MTEXT has no computed extents,
so the landing spans the text instead of a short stub.
- SHAPE: resolve the glyph by name when the shape number is 0 (DXF SHAPE
carries only the name), and search shape-file text styles when the entity
has no direct style reference.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Selecting a proxy object we render from its cached graphics (an AEC
Wall/Door/Window and the like) showed "Unknown" as its type. The entity
now carries its real DXF class name from the CLASSES section (acadrust
bump), so surface it: ui_name_or_class title-cases the class ("AEC_WALL"
→ "Aec Wall") for the properties Type row and the status-bar label, and
still says "Unknown" only when the class truly can't be named.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Multilines read from an R13–R2017 DWG (e.g. an AutoCAD 2013 save) lost
their per-element linetypes and drew every element solid instead of
dashed / with embedded text; R2018 saves were fine. The crate now decodes
the pre-R2018 bit_short linetype index.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A wipeout defined inside a block never masked the geometry once the block
was inserted — wipeout_models only scanned top-level entities, so the
wipeout was invisible unless you entered block-edit (where it is
top-level). It also drew a stray mask at its raw block-local coordinates
because the top-level scan lacked the block-defn reject the hatch path
uses.
Descend visible inserts and place each block-internal wipeout in world
space, applying the accumulated insert transform via apply /
apply_rotation. Add the belongs_to_visible_block filter so block-defn
wipeouts only appear through the descent, in world space.
Bumps acadrust for the matching transform_wipeout fix: its u/v basis was
double-scaled (apply_rotation then another scale-factor multiply), so a
scaled insert grew the mask; the descent sidesteps that path but the
explode/copy commands need the crate fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pulls acadrust 022290a: recover_roundtrip_gradients is now gated to
pre-R2004. A native R2018 file whose hatch has its gradient turned off
(is_gradient=0, solid fill) but still carries stale GradientColor1/2ACI
round-trip EED no longer gets a spurious two-colour gradient painted over
it — the arrowhead / "Aluminium shading" hatches render solid as ODA and
libredwg decode them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Brings the pre-2.0 SAT parse (single-line header, numeric edge/coedge
sense) so R14's ACIS 1.6 solids mesh into a full box, and the R13/R14
dictionary-key trim so an XCLIPped block resolves its spatial filter and
clips instead of drawing whole.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Brings two R14 (AC1014) decode fixes: down-saved gradient hatches now
recover through the case-insensitive EED / garbage-suffixed dictionary
key, and the R13/R14 DimStyle field block is finally read (DIMASZ /
DIMTAD / DIMGAP were stuck at defaults). The latter lets a leader hook
its annotation text — leader 1298's underline now spans the MTEXT
extents, matching the AC1032 render.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Solid hatches carrying GradientColor1ACI/GradientColor2ACI EED plus an
ACAD_XREC_ROUNDTRIP gradient XRecord now decode back into a real
gradient, so R2000-down-saved gradient fills render blue→yellow instead
of a flat solid. Color::rgb() already resolves the ACI stops, so the
existing gradient path picks them up with no render change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Brings the pre-v700 (R2000) SAT edge decode fix: v400 edges omit the
start_param/end_param doubles, so reversed coedges were reading the
curve pointer as a vertex and every face with a reversed coedge loop
dropped out of the mesh. AC1015 solids/regions now tessellate to the
same triangle counts as the AC1018/AC1032 SAB decode (full box instead
of an open shell).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The horizontal attachment point was read from the entity-level field,
which only exists from R2010 on; older records (R2007 and earlier) left
it at the default and centre/right-attached leaders shifted. The
annotation context carries the same value in every version, so anchor
from there for both the render and the width-grip geometry.
acadrust bump also brings: the R2007 MULTILEADER tail gating (leaders no
longer render upside-down), the R2004-R2006 inline SAB payload read, the
v1-SAT wireframe point_present decode (3D solids/regions land in place
on R2000/R2004), and the XCLIPFRAME default of 2 so clip-frame borders
draw on files whose header omits the variable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The annotation context is AutoCAD's baked display state; the renderer
now trusts it instead of re-deriving:
- text_attachment_point (L/C/R) is the stored horizontal anchor — the
leader-side guess shifted centre/right-attached texts by their width.
- ctx.text_direction/text_rotation apply as stored; forcing the
Horizontal angle type to 0 un-rotated angled leaders. The conn.y
baseline snap now applies only to unrotated text.
- The dogleg runs along the leader root's stored direction (angled
leaders drew it world-horizontal), and vertically-attached leaders
draw no dogleg at all.
- Underline attachments (BottomLine / BottomOfTopLineUnderline…) draw
the leader-coloured rule under the attached line(s), full text width.
- Vertical text flow (ctx.text_flow_direction) feeds the shared
vertical layout; frame/background boxes wrap the laid-out glyph
bounds padded by ctx.landing_gap, and run-less decoration strokes +
inline \C colours reach the output instead of being dropped.
- The wrap-width grip rides the shared flow_grip_axis() (rotation, flow
and attachment side), and its drag projects on the same axis.
acadrust bump: every MULTILEADER decodes (has_ds peek), MTEXT
top-to-bottom direction reads correctly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The embedded solid display-cache wires are body-local (unplaced), and
R2013+ AcDs-backed solids used to misparse them into garbage points near
the origin — a clipped region rendered a stray fragment at (0,0,0). When
the ACIS payload parses, the mesh pipeline (fill + feature edges +
isolines, all body-placed) is the authoritative render, so the wires now
only serve as a last resort for unreadable ACIS data.
acadrust bump brings the decoded wireframe section and real reference
points: solids/regions report the file's stored bounding-box centre (or
a geometry-derived one) instead of (0,0,0), which feeds OCS grips,
Position properties and insertion snaps.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DWG stores no "hookline exists" flag (only the direction bit), so every
DWG-loaded leader kept hookline_enabled=false and drew no hook. Derive
it: a text-annotated leader hooks when its final segment leaves the
horizontal by more than 15°, shared by the render and snap/grip paths.
Hook length comes from the dim style like the arrowhead (DIMASZ ×
DIMSCALE), and with DIMTAD "text above" it extends under the annotation
to the far edge of the MTEXT's laid-out extents — newly exposed by
acadrust (lock bumped); the old text-height heuristic stays as the
no-style fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bump acadrust to a87435f: DXF encodes an off layer as a negative
code-62 colour index; the reader took 70 bit 2 ("frozen by default in
new viewports") as off instead. The AEC sample's GRIDLINES layer —
carrying that bit while fully visible in the DWG — imported hidden, so
every building axis line on it disappeared; genuinely off layers
imported visible. Second and final cause of #314, after the LTYPE
74/75 swap.
Closes#314
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bump acadrust to 6455610: the DXF LTYPE reader treated element code 74
as the shape number instead of the element-type flags and materialized
complex data on every plain dash — dashed linetypes like the AEC
sample's GRIDLINE turned "complex" with a null Shape{0} on each
element and rendered nothing. The DXF writer's swapped 74/75 emission
is fixed with it.
Closes#314
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bump acadrust to c5a579f: the DXF MULTILEADER reader now parses the
nested CONTEXT_DATA / LEADER / LEADER_LINE sections (leader lines and
text content used to come back empty, so DXF multileaders never
rendered), and the entity-level code map matches AutoCAD's real output
on both read and write (170 path type vs 172 content type).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bump acadrust to 1e79519: the DXF reader now skips the R2018+
"101 / Embedded Object" section of MTEXT (and multiline ATTRIB/ATTDEF)
instead of letting its group codes overwrite the entity's already-parsed
insertion point, rotation, attachment and line spacing.
Closes#345
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follow-up to the LINE/POINT WCS fix - the same bug class elsewhere:
- 2D SOLID rendered its OCS corners raw in both the wire path and the
shared fill builder (solid_hatch_model); SHAPE placed its marker at
the raw OCS insertion. Both now map through the arbitrary axis.
- Grips: central wrap in view/dispatch.rs shows planar entities' grips
at their world position (OCS→WCS) and converts drags back (WCS→OCS
via the new wcs_point_to_ocs) - per-entity grip code stays in raw
field space, INSERT keeps handling its own OCS.
- Pulls acadrust 81ebfee: OCS-aware bounding boxes, translate,
transform and mirror for the ten planar entity types, so MOVE /
ROTATE / MIRROR and culling work on mirrored entities too.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wgpu/naga report pipeline and shader failures through the log facade,
then leave the canvas empty - with no logger installed on wasm the
message was lost, so a broken GPU path looked like "draws nothing" with
a clean console. Install a web logger that mirrors Error-level records
to the browser console and into a fixed DOM overlay with selectable
text, a Copy button and repeat-collapsing; panics land in the same
banner via a chained hook. Windows Chrome/Edge reporters can now paste
the exact ANGLE/D3D11 error instead of a screenshot of a blank canvas.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bumps acadrust to 1b0a362: block headers no longer list handles of
deleted entities, so DWG saves from a session that removed entities
open with full content in TrueView/AutoCAD instead of empty.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OLE2FRAME entities whose compound file carries a metafile presentation
(Excel/Word previews, scanned WMF pages) drew only their border frame —
the old decoder scanned the raw blob for a contiguous BMP, which the
CFB sector layout almost never provides. acadrust now extracts the
picture bytes tagged by kind; the new scene/model/gdi player rasterizes
EMF and WMF record streams on the CPU (pens/brushes, polygons, paths,
DIB blits, clipping, transforms) with text drawn through the existing
system-font glyph cache, and image_model feeds the result into the
raster pipeline. Rasters (BMP/PNG/JPEG/GIF, CF_DIB) decode as before
via the image crate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Section marks and drawing-view borders are now real acadrust entity
types instead of Unknown-with-extras, so the renderer dispatches on the
variant directly, the arrow-direction resolver reads the active viewport
off the ViewBorder entity, and selection / properties show "Section
Symbol" and "View Border" instead of "Unknown". View borders are
non-plotting and tessellate to nothing; both count toward the lossy
Save-As warning since they round-trip via their raw records.
Bumps acadrust for the entity promotion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The section mark's appearance now comes entirely from file data instead
of invented conventions:
- Arrow direction: resolved through the drawing-view graph — the
symbol's parent AcDbViewRep, the section view (owner of the section
definition), each view border's active viewport — by projecting the
section camera's sight onto the parent camera's basis, using the same
yaw/pitch/twist convention the viewport renderer does. Only when the
chain is unavailable (DXF, older files) does a geometric fallback
apply.
- Broken cutting line: driven by the style's plane-line / end-segment
flags rather than hard-coded.
- Arrowhead: resolved through the shared dimension/leader arrow table
from the style's arrow block handle; a null handle gives the standard
ClosedFilled default.
- Label gap: the style's identifier offset.
Bumps acadrust for the style and view-graph decode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Draw decoded section symbols (AcDbSectionSymbol) as their "A-A" cut mark:
the cut line through both endpoints extended past each end by its signed
tick, a short perpendicular barb at each tip, and the identifier glyph.
The mark now appears on the layout alongside the drawing views instead
of being silently dropped.
Bumps acadrust to pick up the section-symbol decode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-evaluate AcDbField-driven MTEXT — date/login/document-property fields, table
formulas and object properties — against the current system instead of showing
the frozen last-save cache. The evaluation engine lives in `acadrust::fields`;
OCS supplies only the environment (system clock, OS login, env vars) through a
FieldContext implementation, and hooks the resolved text into MTEXT rendering,
falling back to the cached value for anything it can't evaluate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lights, camera views and the geographic-location marker had no on-screen
representation. Add display glyphs for all three:
- Lights render through a new EntityType::Light: a cone/flashlight for spot
lights, a wire sphere for point lights, a sun for distant lights, sized in
screen space (fixed-world fallback).
- Camera views (perspective VIEW records) and the GEODATA "daisy" marker are
synthesized into the model-space resident wire set from the document, since
they are not entities.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The editor preview mis-sized imported MTEXT badly (tiny, huge, or
collapsed). Two root causes:
- The entity's base height often isn't the visible text height: relative
`\H…x;` codes compound, so an entity of height 1 whose body is `\H0.2x`
renders at 0.2. Normalize the preview scale by the DOMINANT rendered run
height (the mode of glyph-box heights, skipping zero-width caret slots),
not `height_value()`, so the body lands at a fixed readable size and
headings keep their proportions. EM_PX = 15.
- The bug that made it "completely wrong" was in the serializer, fixed in
acadrust 389b31a (\H heights decayed on the parse→serialize→parse the
editor does via folded_value); bumped here.
Also render multi-column MTEXT with its columns side by side (as the
drawing does) and let the preview scroll horizontally to reach them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up the ezdxf-audited MTEXT parser fixes: raw LF as a paragraph
break, control chars rendered as spaces, and the corrected `\c`
true-color byte order (so `\c` colors no longer come out red/blue
swapped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two defects in tabbed fields inside a multi-column MText:
- Stop positions are relative to the paragraph's own left edge, but the
tab logic compared them against the column-absolute `cursor_x`. In a
second column `cursor_x` already carries the column offset, so no stop
ever matched and the field shot off to the default 4-em grid, way past
the page. Measure against the column's `box_left` instead.
- A field after a center/right/decimal tab aligns on the stop, reaching
only part of its width to the right, but word-wrap flushed it left and
wrapped it off the line by its full width — dropping a wide decimal
number onto a fresh, un-tabbed line. Don't wrap the field that follows
an aligning tab.
Picks up acadrust b16137f (decimal stop position parse).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A `\t` reaching a center, right, or decimal (`D`) tab stop now aligns the
field that follows it — the run of words up to the next space/tab — instead
of just advancing the pen:
- decimal: the field's first `.` lands on the stop (a column of numbers
aligns on the decimal point; a field with no `.` aligns its end),
- right: the field's end lands on the stop,
- center: its middle.
The stop wins even when the field is wider than the room before it, so the
dot/edge stays put and the field overruns to the left, keeping the column
aligned. Left stops and the default 4-em grid are unchanged. Picks up
acadrust 162f194, which carries the tab-stop kinds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`qj`/`qd` were stubbed to left. Now the layout fills the line to the
content width:
- Distribute (`qd`, and headings that reset with `q*` → justify) shares
the slack across every glyph gap — word gaps widen and letters spread
via tracking (LFF stroke fonts; TTF with zero letter-spacing spread
their word gaps only). Every line, including the paragraph's last.
- Justify spreads only the word gaps and leaves the last line ragged.
Bumps acadrust to 504281a, which parses `\pqd` as Distributed and
`\pq*` as Justified so these alignments reach the layout at all.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An IMAGE whose file can't be opened (a URL, or a missing/renamed file) now
draws its clip/frame outline plus the saved path as centred text in the
image colour — AutoCAD's broken-reference placeholder — instead of an empty
frame. OCS never fetches remote paths. Bumps acadrust so the entity's path
is populated from its ImageDefinition.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picks up the ATTDEF flags/geometry fix so a CONSTANT block attribute
renders instead of being skipped as a template.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A CONSTANT ATTDEF has no per-insert ATTRIB — its value belongs to the block
itself — so block expansion must draw it instead of skipping every ATTDEF.
Non-constant templates are still skipped (the insert supplies their ATTRIB).
Also bumps acadrust to decode the embedded MTEXT of R2018 multiline
attributes, so a value like "A\PB" renders both lines.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>