From 177ae20be48b06f73b284131125964ad2bd9a4a8 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 25 Jun 2026 18:04:20 +0300 Subject: [PATCH] fix(dimension): keep DIMCONTINUE chain collinear with the base (#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The continued dimension line was positioned with an offset measured from the base dimension's *first* extension origin, then re-applied to each new chain point (the previous dim's second origin). When the two extension origins sat at different perpendicular positions — i.e. the extension lines had different lengths — that offset placed the new dim line at `D + (p2 - p1)·perp` instead of the base line's `D`, so the chain drifted off the base line. Equal-length extension lines have `(p2 - p1)·perp = 0`, which is why it only misbehaved sometimes. Store the base dim line's absolute perpendicular coordinate and project each extension origin onto it, so the whole chain stays collinear regardless of extension-line lengths. The preview uses the same projection. Co-Authored-By: Claude Opus 4.8 --- src/modules/annotate/dim_continue.rs | 43 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/modules/annotate/dim_continue.rs b/src/modules/annotate/dim_continue.rs index 7ae3ec92f..bf4f22046 100644 --- a/src/modules/annotate/dim_continue.rs +++ b/src/modules/annotate/dim_continue.rs @@ -31,9 +31,12 @@ pub struct DimContinueCommand { chain_p1: Vec3, /// Direction along the dimension axis (0.0 = horizontal, PI/2 = vertical). rotation: f64, - /// Perpendicular distance from the extension-line axis to the dimension line. - /// Preserved from the base dimension. - dim_offset: f32, + /// Absolute perpendicular coordinate (dot with `perp`) of the base + /// dimension line. Each continued dim line is projected onto this exact + /// coordinate so the whole chain stays collinear — even when the extension + /// origins sit at different perpendicular positions (extension lines of + /// different lengths). (#151) + dim_line_perp: f32, /// Direction of "up" perpendicular to the dim axis (points toward the dim line). perp: Vec3, /// True once we have a base dimension loaded. @@ -46,7 +49,7 @@ impl DimContinueCommand { Self { chain_p1: Vec3::ZERO, rotation: 0.0, - dim_offset: 0.0, + dim_line_perp: 0.0, perp: Vec3::Y, ready: false, } @@ -54,20 +57,24 @@ impl DimContinueCommand { /// Build from the last placed dimension. /// - /// `p1` / `p2` — extension line origins of the base dim. - /// `definition_point` — where the dim line was placed (defines perpendicular offset). + /// `p2` — second extension line origin of the base dim (the chain starts + /// here). `p1` is unused — the dim line position comes from + /// `definition_point` alone — but kept for signature parity with the + /// shared `find_last_linear_dim` tuple (DIMBASELINE uses p1). + /// `definition_point` — where the dim line was placed; its perpendicular + /// coordinate is the line the whole chain stays collinear with. /// `rotation` — 0.0 = horizontal dim, PI/2 = vertical dim. - pub fn from_base(p1: Vec3, p2: Vec3, definition_point: Vec3, rotation: f64) -> Self { + pub fn from_base(_p1: Vec3, p2: Vec3, definition_point: Vec3, rotation: f64) -> Self { // Axis unit vector along the measurement direction — the base dim's // rotation angle (any angle, incl. a UCS-aligned one), not a world H/V. let axis = Vec3::new(rotation.cos() as f32, rotation.sin() as f32, 0.0); // Perpendicular unit vector toward the dim line. let perp = Vec3::new(-axis.y, axis.x, 0.0); - let dim_offset = (definition_point - p1).dot(perp); + let dim_line_perp = definition_point.dot(perp); Self { chain_p1: p2, rotation, - dim_offset, + dim_line_perp, perp, ready: true, } @@ -98,11 +105,15 @@ impl CadCommand for DimContinueCommand { let mut dim = DimensionLinear::new(v3(p1), v3(p2)); dim.rotation = self.rotation; - // Dim-line position: same perpendicular distance as base. - let dim_line_pt = p1 + self.perp * self.dim_offset; - dim.definition_point = v3(dim_line_pt); - dim.base.definition_point = v3(dim_line_pt); - dim.base.text_middle_point = v3((dim_line_pt + (p2 + self.perp * self.dim_offset)) * 0.5); + // Dim line stays collinear with the base: project both extension + // origins onto the base dim line's absolute perpendicular coordinate. + // (A fixed offset from p1 drifts off the base line whenever p1 and p2 + // sit at different perpendicular positions.) (#151) + let d1 = p1 + self.perp * (self.dim_line_perp - p1.dot(self.perp)); + let d2 = p2 + self.perp * (self.dim_line_perp - p2.dot(self.perp)); + dim.definition_point = v3(d1); + dim.base.definition_point = v3(d1); + dim.base.text_middle_point = v3((d1 + d2) * 0.5); dim.base.insertion_point = dim.base.text_middle_point; dim.base.actual_measurement = dim.measurement(); @@ -121,8 +132,8 @@ impl CadCommand for DimContinueCommand { return None; } let p1 = self.chain_p1; - let dim_line_pt = p1 + self.perp * self.dim_offset; - let dim_line_pt2 = pt + self.perp * self.dim_offset; + let dim_line_pt = p1 + self.perp * (self.dim_line_perp - p1.dot(self.perp)); + let dim_line_pt2 = pt + self.perp * (self.dim_line_perp - pt.dot(self.perp)); Some(WireModel { name: "dimcont_preview".into(), points: vec![