From 160930b99a916cdd60e42ac310cc34e3c2e7eb48 Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Tue, 22 Sep 2026 10:41:52 +0300 Subject: [PATCH] Angular dimension drawing: ray-based three-point frame, text beside the arc Three-point angular dimensions take their sweep from the two rays and the arc point (extension lines follow their own rays, straight and reflex angles draw), dynamic angular dimensions store no text point so DIMTAD places the text beside an unbroken arc, and dynamic dimensions carry a DIMGAP override of a quarter text height. Co-Authored-By: Claude Fable 5.1 --- src/app/command_driver.rs | 2 +- src/entities/dimension.rs | 48 +++++++++++++++++++++++------ src/modules/annotate/angular_dim.rs | 5 +-- src/scene/parametric_constraints.rs | 16 ++++++++-- 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index e147c5245..01624ccf7 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -1838,7 +1838,7 @@ impl OpenCADStudio { entity.common_mut().color = acadrust::types::Color::ByLayer; // The dynamic form's screen-size and horizontal-text // overrides go; the style draws it. - for code in [dov::DIMSCALE, dov::DIMTIH, dov::DIMTOH] { + for code in [dov::DIMSCALE, dov::DIMGAP, dov::DIMTIH, dov::DIMTOH] { dov::set_on_entity(&mut entity, code, None); } } else { diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index 310c4983a..880aeeab2 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -4749,14 +4749,14 @@ fn dimension_geometry( let first = lv(d.first_point); let second = lv(d.second_point); let arc_point = lv(d.definition_point); - let explicit_sweep = two_line_angle_frame( - vertex, - first, - vertex, - second, - arc_point, - ) - .map(|(_, start, end)| (start, end)); + // The sweep starts at whichever ray the arc point says; the + // extension lines swap with it, or they would cross the angle. + let (first, second, explicit_sweep) = + match three_point_frame(vertex, first, second, arc_point) { + Some((start, end, true)) => (second, first, Some((start, end))), + Some((start, end, false)) => (first, second, Some((start, end))), + None => (first, second, None), + }; append_angular_dimension( &mut g, vertex, @@ -5398,6 +5398,35 @@ fn two_line_angle_frame( Some((vertex, start, end)) } +/// The sweep of a three-point angular dimension: counter-clockwise from the +/// first ray to the second when the arc point lies in that sweep, otherwise +/// the other way round (`swapped`). Rays need no crossing, so a straight +/// angle draws, and a reflex one is kept as picked. +fn three_point_frame( + vertex: Vec3, + first: Vec3, + second: Vec3, + arc_point: Vec3, +) -> Option<(f32, f32, bool)> { + let angle_of = |d: Vec3| d.y.atan2(d.x); + let (r1, r2, at) = (first - vertex, second - vertex, arc_point - vertex); + if r1.length_squared() <= 1e-12 || r2.length_squared() <= 1e-12 || at.length_squared() <= 1e-12 + { + return None; + } + let tau = std::f32::consts::TAU; + let (a1, a2) = (angle_of(r1), angle_of(r2)); + let sweep = (a2 - a1).rem_euclid(tau); + if sweep <= 1e-6 { + return None; + } + if (angle_of(at) - a1).rem_euclid(tau) <= sweep { + Some((a1, a1 + sweep, false)) + } else { + Some((a2, a2 + (tau - sweep), true)) + } +} + pub(crate) fn arc_dimension_angles(dimension: &DimensionArc) -> Option<(f32, f32)> { let raw = dimension.arc_end_parameter - dimension.arc_start_parameter; let mut sweep = raw.rem_euclid(std::f64::consts::TAU); @@ -5668,8 +5697,7 @@ fn angular_dimension_frame(dim: &Dimension) -> Option<(Vec3, f32, f32, f32)> { let first = vec3_local(value.first_point); let second = vec3_local(value.second_point); let arc_point = vec3_local(value.definition_point); - let (vertex, start, end) = - two_line_angle_frame(vertex, first, vertex, second, arc_point)?; + let (start, end, _) = three_point_frame(vertex, first, second, arc_point)?; (vertex, start, end, arc_point) } Dimension::Arc(value) => { diff --git a/src/modules/annotate/angular_dim.rs b/src/modules/annotate/angular_dim.rs index 36a4395ab..7de21d8e1 100644 --- a/src/modules/annotate/angular_dim.rs +++ b/src/modules/annotate/angular_dim.rs @@ -1205,7 +1205,8 @@ pub(crate) fn angular_two_line_entity( dim.definition_point = v3(second_end); dim.dimension_arc = v3(arc_point); dim.base.definition_point = dim.definition_point; - dim.base.text_middle_point = dim.dimension_arc; + // No stored text point: the style places the text (DIMTAD) beside the + // arc at render time, as the reference draws a dynamic dimension. dim.base.insertion_point = dim.dimension_arc; dim.base.actual_measurement = dim.measurement_degrees(); crate::entities::dimension::set_dimension_text_override(&mut dim.base, text); @@ -1224,7 +1225,7 @@ pub(crate) fn angular_three_point_entity( let mut dim = DimensionAngular3Pt::new(v3(vertex), v3(first), v3(second)); dim.definition_point = v3(arc_point); dim.base.definition_point = dim.definition_point; - dim.base.text_middle_point = dim.definition_point; + dim.base.text_middle_point = Vector3::new(0.0, 0.0, 0.0); dim.base.insertion_point = dim.definition_point; dim.base.actual_measurement = dim.measurement_degrees(); crate::entities::dimension::set_dimension_text_override(&mut dim.base, text); diff --git a/src/scene/parametric_constraints.rs b/src/scene/parametric_constraints.rs index 7c20a9c36..3081eaf0c 100644 --- a/src/scene/parametric_constraints.rs +++ b/src/scene/parametric_constraints.rs @@ -2252,7 +2252,7 @@ impl super::Scene { if self.dimension_is_annotational(handle) { continue; } - for code in [ov::DIMSCALE, ov::DIMTIH, ov::DIMTOH] { + for code in [ov::DIMSCALE, ov::DIMGAP, ov::DIMTIH, ov::DIMTOH] { ov::set(&mut document, handle, code, None); } } @@ -2304,6 +2304,10 @@ impl super::Scene { use crate::entities::dim_override as ov; let xdata = &dimension.base().common.extended_data; let current = ov::real(xdata, ov::DIMSCALE); + // The gap between the text and its line is a quarter of the text + // height on screen, whatever the style's DIMGAP is in units. + let gap = text_height * 0.25; + let gap_set = ov::real(xdata, ov::DIMGAP).is_some_and(|value| (value - gap).abs() < 1e-12); // The reference draws dynamic distance text horizontally; an // angle's text keeps its style's alignment. let angular = matches!( @@ -2313,7 +2317,7 @@ impl super::Scene { ); let horizontal = angular || (ov::int(xdata, ov::DIMTIH) == Some(1) && ov::int(xdata, ov::DIMTOH) == Some(1)); - if current.is_some_and(|value| (value - scale).abs() < 1e-9) && horizontal { + if current.is_some_and(|value| (value - scale).abs() < 1e-9) && horizontal && gap_set { continue; } ov::set( @@ -2322,6 +2326,14 @@ impl super::Scene { ov::DIMSCALE, Some(acadrust::xdata::XDataValue::Real(scale)), ); + if !gap_set { + ov::set( + &mut self.document, + handle, + ov::DIMGAP, + Some(acadrust::xdata::XDataValue::Real(gap)), + ); + } if !horizontal { for code in [ov::DIMTIH, ov::DIMTOH] { ov::set(