Turn an object onto its Horizontal/Vertical axis before solving

The solver cannot start from an axis lying exactly across the datum: the
distance and direction equations are singular there, so a horizontal line,
a text at 0 degrees or an ellipse whose major axis is horizontal stayed
put when Vertical was applied. The command now turns the object about its
anchor (line start, picked segment's first vertex, text insertion point,
ellipse center) onto the axis first — a polyline moves only the segment's
end vertex — and lets the constraint hold what already fits. Two points on
one entity follow the same rule about the first point.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
ramox81andClaude Fable 5.1 committed 2026-09-18 22:40:39 +03:00
1 parent a7ab5314f2
commit 1f0b888833
2 files changed
+110

No files matched your search

+47
View File
@@ -3486,6 +3486,53 @@ impl OpenCADStudio {
}
}
}
// The solver cannot start from an axis lying exactly across
// the datum (its equations are singular there), so turn the
// object onto the axis about its anchor first — the
// reference turns it about that anchor too — and let the
// constraint hold what already fits.
if let Some((handle, anchor, end, vertex)) =
crate::scene::parametric_constraints::axis_alignment_target(
&self.tabs[i].scene.document,
&refs,
)
{
let axis = (end.x - anchor.x, end.y - anchor.y);
let length = axis.0.hypot(axis.1);
let target = if axis.0 * direction.x + axis.1 * direction.y >= 0.0 {
(direction.x, direction.y)
} else {
(-direction.x, -direction.y)
};
let angle = (axis.0 * target.1 - axis.1 * target.0)
.atan2(axis.0 * target.0 + axis.1 * target.1);
if length > 1.0e-9 && angle.abs() > 1.0e-9 {
match vertex {
Some(index) => {
let moved =
self.tabs[i].scene.document.get_entity(handle).cloned();
if let Some(mut entity) = moved {
if crate::scene::parametric_constraints::set_polyline_vertex(
&mut entity,
index,
anchor.x + length * target.0,
anchor.y + length * target.1,
) {
self.tabs[i].scene.update_entity(entity);
}
}
}
None => self.tabs[i].scene.transform_entities(
&[handle],
&EntityTransform::Rotate {
center: glam::DVec3::new(anchor.x, anchor.y, anchor.z),
axis: glam::DVec3::Z,
angle_rad: angle,
},
),
}
}
}
let id = self.tabs[i]
.scene
.parametric_constraint_set_mut(scope)
+63
View File
@@ -202,6 +202,69 @@ pub(crate) fn directional_axis_endpoints(
}
}
/// The axis a lone Horizontal/Vertical reference — or two points on one
/// entity — turns onto its datum: the entity, the anchor that stays, the
/// axis end that moves, and for a polyline the vertex to move instead of
/// turning the whole entity.
pub(crate) fn axis_alignment_target(
document: &acadrust::CadDocument,
refs: &[ParametricRef],
) -> Option<(Handle, Vector3, Vector3, Option<usize>)> {
let (handle, start_marker, end_marker) = match refs {
[reference] => {
let entity = document.get_entity(reference.entity)?;
if reference.directional_axis().is_some() {
let [start, end] = directional_axis_endpoints(entity, *reference)?;
return Some((reference.entity, start, end, None));
}
let index = reference.segment_index().map_or(0, |index| index as i32);
(reference.entity, index, index + 1)
}
[first, second] if first.entity == second.entity => {
(first.entity, first.marker?, second.marker?)
}
_ => return None,
};
let entity = document.get_entity(handle)?;
let start = resolve_point(entity, start_marker)?;
let end = resolve_point(entity, end_marker)?;
let vertex = matches!(
entity,
acadrust::EntityType::LwPolyline(_) | acadrust::EntityType::Polyline2D(_)
)
.then(|| usize::try_from(end_marker).ok())
.flatten();
Some((handle, start, end, vertex))
}
/// Moves one polyline vertex in its plane; `false` for any other entity.
pub(crate) fn set_polyline_vertex(
entity: &mut acadrust::EntityType,
index: usize,
x: f64,
y: f64,
) -> bool {
match entity {
acadrust::EntityType::LwPolyline(polyline) => polyline
.vertices
.get_mut(index)
.map(|vertex| {
vertex.location.x = x;
vertex.location.y = y;
})
.is_some(),
acadrust::EntityType::Polyline2D(polyline) => polyline
.vertices
.get_mut(index)
.map(|vertex| {
vertex.location.x = x;
vertex.location.y = y;
})
.is_some(),
_ => false,
}
}
/// Grabbed points are exact kernel inputs; the solver anchors the remaining
/// endpoint coordinates according to the line's directional constraints.
pub(crate) fn grip_solve_anchor_refs(