forked from 1b-agent/opencadstudio-upstream
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
a24232842c
commit
79a5ac6fed
7 files changed
+153
-23
No files matched your search
@@ -42,6 +42,55 @@ impl Scene {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Split a MIRROR selection into plain ghost wires (reflected wholesale) and
|
||||
/// text ghosts paired with their bounding-box centre. Lets the preview match
|
||||
/// the commit for TEXT: MIRRTEXT on → true glyph mirror (full reflection, same
|
||||
/// as plain geometry); MIRRTEXT off → right-reading at the mirror-symmetric
|
||||
/// position (reflect the centre, translate) instead of hugging the axis.
|
||||
pub fn mirror_preview_parts(
|
||||
&self,
|
||||
handles: &[Handle],
|
||||
) -> (Vec<WireModel>, Vec<(WireModel, glam::DVec3)>) {
|
||||
let mut plain: Vec<WireModel> = Vec::new();
|
||||
let mut texts: Vec<(WireModel, glam::DVec3)> = Vec::new();
|
||||
for h in handles {
|
||||
let Some(e) = self.document.entities().find(|e| e.common().handle == *h) else {
|
||||
continue;
|
||||
};
|
||||
if matches!(e, EntityType::Text(_)) {
|
||||
let wires = self.tessellate_one(e);
|
||||
let mut lo = [f64::INFINITY; 2];
|
||||
let mut hi = [f64::NEG_INFINITY; 2];
|
||||
for w in &wires {
|
||||
for (i, p) in w.points.iter().enumerate() {
|
||||
if !p[0].is_finite() || !p[1].is_finite() {
|
||||
continue;
|
||||
}
|
||||
// Reconstruct f64 world from the double-single pair (the
|
||||
// low residual may be absent on some wires).
|
||||
let l = w.points_low.get(i).copied().unwrap_or([0.0; 3]);
|
||||
let (x, y) = (p[0] as f64 + l[0] as f64, p[1] as f64 + l[1] as f64);
|
||||
lo[0] = lo[0].min(x);
|
||||
lo[1] = lo[1].min(y);
|
||||
hi[0] = hi[0].max(x);
|
||||
hi[1] = hi[1].max(y);
|
||||
}
|
||||
}
|
||||
let center = if lo[0] <= hi[0] {
|
||||
glam::DVec3::new((lo[0] + hi[0]) * 0.5, (lo[1] + hi[1]) * 0.5, 0.0)
|
||||
} else {
|
||||
glam::DVec3::ZERO
|
||||
};
|
||||
for w in wires {
|
||||
texts.push((w, center));
|
||||
}
|
||||
} else {
|
||||
plain.extend(self.tessellate_one(e));
|
||||
}
|
||||
}
|
||||
(plain, texts)
|
||||
}
|
||||
|
||||
/// Boundary outline wire for a hatch, reconstructed from its cached
|
||||
/// `HatchModel` (offsets from `world_origin`). Used only for edit previews —
|
||||
/// the normal render shows the fill, not this outline.
|
||||
|
||||
Reference in new issue
Block a user