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(