forked from 1b-agent/opencadstudio-upstream
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:
1 parent
a7ab5314f2
commit
1f0b888833
2 files changed
+110
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in new issue
Block a user