Fix diameter dimension grip and text alignment

This commit is contained in:
codallos committed 2026-09-16 19:21:03 -06:00
1 parent ca0ee37d26
commit 1f71a70580
3 files changed
+177 -10

No files matched your search

+100 -10
View File
@@ -930,6 +930,23 @@ fn capture_dim_text_relative_position(dim: &Dimension) -> Option<DimTextRelative
dy / len, dy / len,
) )
} }
Dimension::Diameter(d) => {
let dx = d.definition_point.x - d.angle_vertex.x;
let dy = d.definition_point.y - d.angle_vertex.y;
let len = (dx * dx + dy * dy).sqrt();
if len <= 1e-12 {
return None;
}
(
d.angle_vertex,
d.definition_point,
d.angle_vertex,
dx / len,
dy / len,
)
}
_ => return None, _ => return None,
}; };
@@ -989,6 +1006,23 @@ fn restore_dim_text_relative_position(
dy / len, dy / len,
) )
} }
Dimension::Diameter(d) => {
let dx = d.definition_point.x - d.angle_vertex.x;
let dy = d.definition_point.y - d.angle_vertex.y;
let len = (dx * dx + dy * dy).sqrt();
if len <= 1e-12 {
return;
}
(
d.angle_vertex,
d.definition_point,
d.angle_vertex,
dx / len,
dy / len,
)
}
_ => return, _ => return,
}; };
@@ -1211,6 +1245,26 @@ impl Grippable for Dimension {
}; };
if grip_id == text_grip { if grip_id == text_grip {
apply_to_v3(&mut self.base_mut().text_middle_point, &apply); apply_to_v3(&mut self.base_mut().text_middle_point, &apply);
// A diametric dimension must remain collinear when its text/leader
// grip is moved. Rotate both diameter endpoints around the fixed
// center so the leader and diameter share the same radial axis.
if let Dimension::Diameter(d) = self {
let center = d.center();
let radius = d.measurement() * 0.5;
let offset = d.base.text_middle_point - center;
if offset.length_squared() > 1e-24 && radius > 1e-12 {
let radial = offset.normalize() * radius;
// Keep the endpoint nearest the text on the text side.
d.angle_vertex = center + radial;
d.definition_point = center - radial;
d.base.definition_point = d.definition_point;
d.base.actual_measurement = d.measurement();
}
}
// Dragging the text grip pins it to a user-defined location, so it // Dragging the text grip pins it to a user-defined location, so it
// no longer follows the style (DIMTAD). See #94. // no longer follows the style (DIMTAD). See #94.
self.base_mut().text_user_positioned = true; self.base_mut().text_user_positioned = true;
@@ -4637,9 +4691,9 @@ fn dimension_geometry(
&mut g, &mut g,
chord, chord,
far_chord, far_chord,
lv(d.base.text_middle_point),
arrow1, arrow1,
arrow2, arrow2,
d.leader_length as f32,
params, params,
suppress, suppress,
); );
@@ -5025,9 +5079,9 @@ fn append_diameter_dimension(
g: &mut DimGeom, g: &mut DimGeom,
chord: Vec3, chord: Vec3,
far_chord: Vec3, far_chord: Vec3,
text_anchor: Vec3,
arrow1: &ArrowKind, arrow1: &ArrowKind,
arrow2: &ArrowKind, arrow2: &ArrowKind,
leader_length: f32,
params: DimLineParams, params: DimLineParams,
suppress: SuppressFlags, suppress: SuppressFlags,
) { ) {
@@ -5036,9 +5090,14 @@ fn append_diameter_dimension(
if diameter <= 1e-6 { if diameter <= 1e-6 {
return; return;
} }
// Text projected outside the diameter requires inward-pointing arrowheads.
let text_along = (params.text_position - chord).dot(axis);
let text_outside = text_along < 0.0 || text_along > diameter;
let arrows_outside = if params.ticks || params.arrow_len <= 1e-6 { let arrows_outside = if params.ticks || params.arrow_len <= 1e-6 {
false false
} else if text_outside {
true
} else if diameter < 2.0 * params.arrow_len { } else if diameter < 2.0 * params.arrow_len {
true true
} else if diameter < params.text_width + 2.0 * params.arrow_len { } else if diameter < params.text_width + 2.0 * params.arrow_len {
@@ -5062,7 +5121,7 @@ fn append_diameter_dimension(
.unwrap_or(line_length * 0.5); .unwrap_or(line_length * 0.5);
let split_point = first + axis * split; let split_point = first + axis * split;
let draw_inside_line = !arrows_outside || params.dimtofl; let draw_inside_line = text_outside || !arrows_outside || params.dimtofl;
if draw_inside_line && !suppress.dim1 && split > 1e-6 { if draw_inside_line && !suppress.dim1 && split > 1e-6 {
add_segment_with_text_break(&mut g.dim_lines, first, split_point, params.text_break); add_segment_with_text_break(&mut g.dim_lines, first, split_point, params.text_break);
} }
@@ -5087,18 +5146,25 @@ fn append_diameter_dimension(
append_arrow(g, far_chord, -axis, arrow2); append_arrow(g, far_chord, -axis, arrow2);
} }
let text_along = (params.text_position - chord).dot(axis);
if params.text_movement == 0 { if params.text_movement == 0 {
let (tip, suppressed) = if params.text_position.distance_squared(chord) let (tip, direction, suppressed) = if params.text_position.distance_squared(chord)
<= params.text_position.distance_squared(far_chord) <= params.text_position.distance_squared(far_chord)
{ {
(chord, suppress.dim1) (chord, -axis, suppress.dim1)
} else { } else {
(far_chord, suppress.dim2) (far_chord, axis, suppress.dim2)
}; };
if !suppressed && leader_length.abs() > 1e-6 { if text_outside && !suppressed {
let direction = normalized_or(params.text_position - tip, axis); let leader_length = (text_anchor - tip).dot(direction);
add_segment(&mut g.dim_lines, tip, tip + direction * leader_length.abs());
if leader_length > 1e-6 {
let text_extension = params.text_width * 0.5;
add_segment(
&mut g.dim_lines,
tip,
tip + direction * (leader_length + text_extension),
);
}
} else if text_along < 0.0 && !suppress.dim1 { } else if text_along < 0.0 && !suppress.dim1 {
add_segment(&mut g.dim_lines, chord, params.text_position); add_segment(&mut g.dim_lines, chord, params.text_position);
} else if text_along > diameter && !suppress.dim2 { } else if text_along > diameter && !suppress.dim2 {
@@ -6976,6 +7042,30 @@ fn dimension_text_pos_f64(
p.x * p.x + p.y * p.y + p.z * p.z > 1e-16 p.x * p.x + p.y * p.y + p.z * p.z > 1e-16
}; };
if use_saved { if use_saved {
if let Dimension::Diameter(d) = dim {
let dx = d.definition_point.x - d.angle_vertex.x;
let dy = d.definition_point.y - d.angle_vertex.y;
let len = (dx * dx + dy * dy).sqrt().max(1e-12);
// Match the readable orientation used by text_on_dim_line().
let (mut nx, mut ny) = (dx / len, dy / len);
if nx < 0.0 || (nx == 0.0 && ny < 0.0) {
nx = -nx;
ny = -ny;
}
let px = -ny;
let py = nx;
let perp_sign = if dimtad == 4 { -1.0 } else { 1.0 };
return Vector3::new(
base.text_middle_point.x + px * perp_off * perp_sign,
base.text_middle_point.y + py * perp_off * perp_sign,
base.text_middle_point.z,
);
}
return base.text_middle_point; return base.text_middle_point;
} }
+76
View File
@@ -2157,6 +2157,82 @@ impl Scene {
} }
refreshed refreshed
} }
pub(crate) fn sync_diameter_association_angle(&mut self, dimension: Handle) {
let chord = match self.document.get_entity(dimension) {
Some(EntityType::Dimension(Dimension::Diameter(diameter))) => {
diameter.angle_vertex
}
_ => return,
};
let association_handle = self.document.objects.iter().find_map(|(handle, object)| {
let ObjectType::Associative(object) = object else {
return None;
};
let AssociativeData::DimensionAssociation(association) = &object.data else {
return None;
};
(association.dimension == dimension).then_some(*handle)
});
let Some(association_handle) = association_handle else {
return;
};
// Resolve the radial source while the association is borrowed immutably.
let angle = {
let Some(ObjectType::Associative(object)) =
self.document.objects.get(&association_handle)
else {
return;
};
let AssociativeData::DimensionAssociation(association) = &object.data else {
return;
};
let Some(reference) = association.references[0].first() else {
return;
};
let Ok(walked) = chain::walk_chain(&self.document, &reference.xrefs) else {
return;
};
let Some(entity) = self.document.get_entity(walked.entity) else {
return;
};
let Some(radial) = radial_source_for_marker(entity, reference.main_gs_marker) else {
return;
};
let Some(radial) = chain_map(self, &walked).map_radial(radial) else {
return;
};
radial.angle_at(dpoint(chord))
};
let Some(ObjectType::Associative(object)) =
self.document.objects.get_mut(&association_handle)
else {
return;
};
let AssociativeData::DimensionAssociation(association) = &mut object.data else {
return;
};
let Some(reference) = association.references[0].first_mut() else {
return;
};
reference.osnap_distance = angle;
reference.osnap_point = chord;
}
} }
/// Average displacement of the dimension's resolved definition points. /// Average displacement of the dimension's resolved definition points.
+1
View File
@@ -1399,6 +1399,7 @@ impl Scene {
if let Some(entity) = self.document.get_entity_mut(handle) { if let Some(entity) = self.document.get_entity_mut(handle) {
view::dispatch::apply_grip(entity, grip_id, apply); view::dispatch::apply_grip(entity, grip_id, apply);
} }
self.sync_diameter_association_angle(handle);
if let Some((annotation_handle, old_landing)) = leader_landing_before { if let Some((annotation_handle, old_landing)) = leader_landing_before {
let new_landing = self.document.get_entity(handle).and_then(|entity| { let new_landing = self.document.get_entity(handle).and_then(|entity| {
let EntityType::Leader(leader) = entity else { let EntityType::Leader(leader) = entity else {