With a working plane other than world XY, ROTATE and MIRROR handed a
non-ordinate dimension to acadrust's Entity::apply_transform, which only
translates those dimension types by where the transform sends the origin.
The definition points, plane and angles stayed unturned, so the dimension
jumped back once its block was baked again.
Send both through the existing Affine branch, which ALIGN already uses and
which transforms points, normal and stored angles together. The world-Z
paths are unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ROTATE and MIRROR in the drawing plane moved a linear dimension's points
but left its stored rotation (the axis it measures along) unchanged. A
horizontal dimension rotated 90 degrees then measured its now-vertical
points along X and its value dropped to 0; mirroring about a slanted line
did the same. Polar ARRAY goes through the same path.
Turn the axis by the rotation angle, and reflect it about the mirror line.
A zero-length mirror line still leaves the dimension untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DIMTMOVE=1 ("moves the text and adds a leader") draws the leader from the midpoint
of the dimension line to the text, so a text dragged outside the dimension line gets
a diagonal that starts in the middle of the line and crosses the extension lines.
Mechanical-drawing practice (GB/T 4458.4, and what AutoCAD itself draws) extends the
dimension line along its own axis so that it runs underneath the text. Linear and
aligned dimensions now do that whenever the text is outside the dimension-line extent;
when the text is still inside it (only moved up/down) the previous connect-to-text
leader is kept. Every other dimension type is unchanged, and only dimensions with
DIMTMOVE=1 are affected at all (the shipped default is 0).
Adds four unit tests covering the placements (outside right, outside left, inside,
radius draws none) plus an integration render smoke test driven through the real
Scene::entity_wires() -> export_pdf() pipeline, gated by OCS_DIM_LEADER_PDF so CI
stays side-effect free.
Switching to a Layout re-tessellated the whole model — `memo_miss=186455`,
2034 ms — to arrive at geometry identical to what was already in the memo.
The only thing that differed was colour.
`local_wires_for` already knows this: it stores the raw colour and drops
its background argument with `let _ = bg_color;`, noting that
`Batches::finalize` applies `adapt_to_bg` last, "so the cache no longer
has to rebuild on BACKGROUND / layout-switch". The tessellation memo's
guard had not been told, and still mixed the background in, so every
entry was discarded on a switch.
`finalize` now records what its colour resolution consumed — the raw
colour, the raw glyph colours, and the three flags it branches on — as
`WireModel::bg_adapt`, and `resolve_colors_for_bg` replays it for another
background. The guard drops the background, so a memo entry serves any
layout.
The resolution recomputes from the recorded raw colour rather than from
the resolved one, which makes it idempotent and therefore safe over a set
that mixes memo hits with freshly tessellated misses. Re-adapting the
resolved colour would not be safe: `adapt_to_bg` snaps a near-white colour
to pure black on a light background, and pure black comes back pure
*white* on a dark one, losing the tint. A test pins that asymmetry so the
raw colour is not optimised away later.
The contract is tested against the real `finalize` rather than a copy of
its logic: for every background pair, building under one and resolving for
the other lands exactly where building under the other would have.
`bg_adapt` is `None` for wires built anywhere else, and the resolution
skips them, so nothing outside the block cache changes colour. It is also
`None` when the resolution would be a no-op — a preserved colour with no
canvas fill looks the same under every background — which keeps the common
case at no cost.
Ordering is unchanged: the pass runs inside the builder, before
`append_scene_markers` and `apply_refedit_fade`, both of which have their
own separate background dependency.
`wires-build` gains `colors=` so the pass has a number of its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DIMAUNIT 4 is Surveyor's units, but format_angular_value matched only
0-3 and let 4 fall through the catch-all to decimal degrees. An angular
dimension in a drawing authored with surveyor units therefore read as
plain degrees, while AUNITS 4 in the same drawing writes a bearing for
the coordinate readout — the two disagreed on one angle.
Format the bearing from the dimension's own DMS path so it carries
DIMADEC, DIMAZIN and DIMDSEP, quantise before picking the quadrant so a
value just short of a cardinal does not quote a quarter turn, and offer
the option in the dimension-style dialog.
The Properties panel reads and writes ACAD_DSTYLE overrides for all three
dimension colours (DIMCLRD 176, DIMCLRE 177, DIMCLRT 178), but the
renderer consulted the override only for DIMCLRD and took DIMCLRE/DIMCLRT
straight from the dimension style. Editing 'Text color' on a dimension
therefore showed the new colour in the panel while the drawing kept
rendering the inherited style colour — which for the common
DIMCLRT = ByLayer default is the layer colour. (#898)
Route all three through one helper so the renderer and the panel read the
same value.
format_engineering split feet from inches before rounding to DIMDEC, so a
remainder that rounds up to a full foot printed out of range: 35.999" at
two decimals read 2'-12.00" instead of 3'-0.00". A magnitude that rounded
away to zero also kept a lone minus sign.
Round on the decimal grid first and split afterwards, matching the carry
handling already used by format_architectural and format_fractional.
Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.