forked from 1b-agent/opencadstudio-upstream
Merge pull request #1352 from ramox81/codex/symmetric-constraint-parity
Complete Symmetric constraint behavior
This commit is contained in:
commit
da02e38208
11 files changed
+755
-68
No files matched your search
@@ -231,13 +231,19 @@ pub(crate) fn grip_solve_anchor_refs(
|
||||
}
|
||||
acadrust::EntityType::Arc(_) => match grip_id {
|
||||
0 => vec![ParametricRef::center(handle)],
|
||||
1 => vec![ParametricRef::point(handle, 0)],
|
||||
2 => vec![ParametricRef::point(handle, 1)],
|
||||
1..=3 => vec![ParametricRef::whole(handle)],
|
||||
_ => Vec::new(),
|
||||
},
|
||||
acadrust::EntityType::Circle(_) => match grip_id {
|
||||
0 => vec![ParametricRef::center(handle)],
|
||||
1..=4 => vec![ParametricRef::whole(handle)],
|
||||
_ => Vec::new(),
|
||||
},
|
||||
acadrust::EntityType::Ellipse(_) => match grip_id {
|
||||
0 => vec![ParametricRef::center(handle)],
|
||||
1..=6 => vec![ParametricRef::whole(handle)],
|
||||
_ => Vec::new(),
|
||||
},
|
||||
acadrust::EntityType::Circle(_) if grip_id == 0 => {
|
||||
vec![ParametricRef::center(handle)]
|
||||
}
|
||||
acadrust::EntityType::Point(_)
|
||||
| acadrust::EntityType::Insert(_)
|
||||
| acadrust::EntityType::Text(_)
|
||||
@@ -304,11 +310,11 @@ pub enum ConstraintKind {
|
||||
/// For the supported entity types, equal curvature is already covered
|
||||
/// by the circle/circle radius branch of `Equal`.
|
||||
EqualDistance,
|
||||
/// Two circles/arcs are mirror images of each other across a line —
|
||||
/// `refs`: `[center(a), center(b), whole(mirror_line)]`. Scoped to the
|
||||
/// circle-center-pair case (unambiguous with whole-entity selection);
|
||||
/// point-symmetry about a point, and symmetry between two lines, aren't
|
||||
/// modeled.
|
||||
/// Two points or compatible curves are symmetric across a line.
|
||||
/// Point mode stores two point refs plus the axis. Object mode stores two
|
||||
/// whole/segment refs plus the axis and constrains the complete relevant
|
||||
/// geometry: line direction, circular center/radius, or ellipse
|
||||
/// center/axes.
|
||||
Symmetric,
|
||||
/// A circle/arc's diameter (twice `Radius`'s target) — `refs`:
|
||||
/// `[whole(circle_or_arc)]`. Same DWG class as `Radius`
|
||||
@@ -951,6 +957,10 @@ fn glyph_placement_for_reference(
|
||||
let anchor = arc.midpoint_wcs();
|
||||
Some((anchor, anchor - center))
|
||||
}
|
||||
(acadrust::EntityType::Ellipse(ellipse), None | Some(-3)) => {
|
||||
let anchor = ellipse.center + ellipse.major_axis;
|
||||
Some((anchor, ellipse.major_axis))
|
||||
}
|
||||
(acadrust::EntityType::Line(line), Some(marker)) => {
|
||||
let anchor = resolve_point(entity, marker)?;
|
||||
let direction = anchor - line_midpoint(line);
|
||||
@@ -983,7 +993,10 @@ fn glyph_placements(
|
||||
document: &acadrust::CadDocument,
|
||||
constraint: &ParametricConstraint,
|
||||
) -> Vec<(Vector3, Vector3)> {
|
||||
if constraint.kind == ConstraintKind::Parallel {
|
||||
if matches!(
|
||||
constraint.kind,
|
||||
ConstraintKind::Parallel | ConstraintKind::Symmetric
|
||||
) {
|
||||
return constraint
|
||||
.refs
|
||||
.iter()
|
||||
@@ -1659,7 +1672,7 @@ impl super::Scene {
|
||||
})
|
||||
.flat_map(|c| {
|
||||
let is_conflicting = set.conflicts.iter().any(|(id, _)| *id == c.id);
|
||||
let label = if show_values {
|
||||
let base_label = if show_values {
|
||||
glyph_label(c)
|
||||
} else {
|
||||
c.kind.glyph_symbol().to_string()
|
||||
@@ -1682,7 +1695,22 @@ impl super::Scene {
|
||||
.collect();
|
||||
glyph_placements(&self.document, c)
|
||||
.into_iter()
|
||||
.filter_map(|(anchor, outward)| {
|
||||
.enumerate()
|
||||
.filter_map(|(index, (anchor, outward))| {
|
||||
let label = if c.kind == ConstraintKind::Symmetric {
|
||||
if index == 2 {
|
||||
"S│".to_string()
|
||||
} else if c.refs.get(index).is_some_and(|reference| {
|
||||
reference.marker.is_some()
|
||||
&& reference.segment_index().is_none()
|
||||
}) {
|
||||
"S•".to_string()
|
||||
} else {
|
||||
"S◇".to_string()
|
||||
}
|
||||
} else {
|
||||
base_label.clone()
|
||||
};
|
||||
let screen = crate::scene::pick::grip::project_rte(
|
||||
glam::DVec3::new(anchor.x, anchor.y, anchor.z),
|
||||
view_rot,
|
||||
@@ -1706,7 +1734,7 @@ impl super::Scene {
|
||||
c.id,
|
||||
point,
|
||||
direction.to_array(),
|
||||
label.clone(),
|
||||
label,
|
||||
is_conflicting,
|
||||
hover_points.clone(),
|
||||
))
|
||||
|
||||
+127
-14
@@ -17,7 +17,7 @@ use cadkernel_constraints::constraints::curve_generic::{BoundedArcValue, CurveVa
|
||||
use cadkernel_constraints::constraints::point_line::{
|
||||
CenterOfGravity, Difference, Equal, EqualLineLength, MidpointOnLine, P2PDistance,
|
||||
Parallel as ParallelConstraint, Perpendicular as PerpendicularConstraint, PointOnLine,
|
||||
ProjectedDistance, ProjectedDistanceAlongLine,
|
||||
ProjectedDistance, ProjectedDistanceAlongLine, SymmetricLineDirections,
|
||||
};
|
||||
use cadkernel_constraints::constraints::Constraint;
|
||||
use cadkernel_constraints::geo::{
|
||||
@@ -1363,18 +1363,86 @@ fn build_constraint(
|
||||
let [a, b, m] = c.refs.as_slice() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let (Some(pa), Some(pb), Some(mirror)) = (
|
||||
point_ref(sys, cache, *a),
|
||||
point_ref(sys, cache, *b),
|
||||
whole_line(sys, cache, *m),
|
||||
let Some(mirror) = whole_line(sys, cache, *m) else {
|
||||
return Vec::new();
|
||||
};
|
||||
if let (Some(pa), Some(pb)) =
|
||||
(point_ref(sys, cache, *a), point_ref(sys, cache, *b))
|
||||
{
|
||||
let pair = GLine { p1: pa, p2: pb };
|
||||
return vec![
|
||||
Rc::new(MidpointOnLine::new(pair, mirror)),
|
||||
Rc::new(PerpendicularConstraint::new(sys.store(), pair, mirror)),
|
||||
];
|
||||
}
|
||||
let (Some(first), Some(second)) = (
|
||||
resolve_ref(document, sys, cache, *a),
|
||||
resolve_ref(document, sys, cache, *b),
|
||||
) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let pair = GLine { p1: pa, p2: pb };
|
||||
vec![
|
||||
Rc::new(MidpointOnLine::new(pair, mirror)),
|
||||
Rc::new(PerpendicularConstraint::new(sys.store(), pair, mirror)),
|
||||
]
|
||||
match (
|
||||
as_circle_or_line(first, *a),
|
||||
as_circle_or_line(second, *b),
|
||||
) {
|
||||
(CircleOrLine::Line(first), CircleOrLine::Line(second)) => {
|
||||
vec![Rc::new(SymmetricLineDirections::new(
|
||||
sys.store(),
|
||||
first,
|
||||
second,
|
||||
mirror,
|
||||
))]
|
||||
}
|
||||
(CircleOrLine::Circle(first), CircleOrLine::Circle(second)) => {
|
||||
let centers = GLine {
|
||||
p1: first.center,
|
||||
p2: second.center,
|
||||
};
|
||||
vec![
|
||||
Rc::new(MidpointOnLine::new(centers, mirror)),
|
||||
Rc::new(PerpendicularConstraint::new(
|
||||
sys.store(),
|
||||
centers,
|
||||
mirror,
|
||||
)),
|
||||
Rc::new(Equal::new(second.rad, first.rad, 1.0)),
|
||||
]
|
||||
}
|
||||
(CircleOrLine::Ellipse(first), CircleOrLine::Ellipse(second)) => {
|
||||
let centers = GLine {
|
||||
p1: first.center,
|
||||
p2: second.center,
|
||||
};
|
||||
let first_axis = GLine {
|
||||
p1: first.center,
|
||||
p2: first.focus1,
|
||||
};
|
||||
let second_axis = GLine {
|
||||
p1: second.center,
|
||||
p2: second.focus1,
|
||||
};
|
||||
vec![
|
||||
Rc::new(MidpointOnLine::new(centers, mirror)),
|
||||
Rc::new(PerpendicularConstraint::new(
|
||||
sys.store(),
|
||||
centers,
|
||||
mirror,
|
||||
)),
|
||||
Rc::new(SymmetricLineDirections::new(
|
||||
sys.store(),
|
||||
first_axis,
|
||||
second_axis,
|
||||
mirror,
|
||||
)),
|
||||
Rc::new(EqualMajorAxesConic::new(
|
||||
GConic::Ellipse(first),
|
||||
GConic::Ellipse(second),
|
||||
)),
|
||||
Rc::new(Equal::new(second.radmin, first.radmin, 1.0)),
|
||||
]
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
ConstraintKind::Fixed => {
|
||||
let Some(r) = c.refs.first() else {
|
||||
@@ -2332,10 +2400,28 @@ fn solve_scope(
|
||||
}
|
||||
|
||||
// Ordered two-object constraints use the first pick as a reference for
|
||||
// their initial placement. Build temporary Fixed equations into this
|
||||
// solve only; they are deliberately omitted from `owner` and from the
|
||||
// persistent constraint set/native graph.
|
||||
for reference in initial_fixed_refs {
|
||||
// their initial placement. Temporary Fixed equations stay out of `owner`
|
||||
// and the persistent native graph. Symmetric references are handled as
|
||||
// exact driven anchors below, so they are excluded from this approximate
|
||||
// equality-equation path.
|
||||
let symmetric_constraints = constraints
|
||||
.iter()
|
||||
.filter(|constraint| {
|
||||
constraint.enabled
|
||||
&& constraint.kind == ConstraintKind::Symmetric
|
||||
&& constraint.refs.len() == 3
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let temporary_fixed_refs = initial_fixed_refs
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|reference| {
|
||||
!symmetric_constraints
|
||||
.iter()
|
||||
.any(|constraint| constraint.refs.contains(reference))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for reference in &temporary_fixed_refs {
|
||||
let temporary = ParametricConstraint {
|
||||
id: ConstraintId::MAX,
|
||||
kind: ConstraintKind::Fixed,
|
||||
@@ -2375,6 +2461,18 @@ fn solve_scope(
|
||||
}
|
||||
|
||||
let mut anchors = driven_refs.to_vec();
|
||||
for constraint in &symmetric_constraints {
|
||||
if let Some(axis) = constraint.refs.get(2).copied() {
|
||||
if !anchors.contains(&axis) {
|
||||
anchors.push(axis);
|
||||
}
|
||||
}
|
||||
for reference in initial_fixed_refs {
|
||||
if constraint.refs[..2].contains(reference) && !anchors.contains(reference) {
|
||||
anchors.push(*reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !retain_lengths {
|
||||
let mut axis_lines: Vec<_> = line_axes.into_iter().filter_map(|(reference, vertical)| {
|
||||
let geometry = cache.get(&reference.entity)?;
|
||||
@@ -2501,6 +2599,9 @@ fn solve_scope(
|
||||
if initial_fixed_refs
|
||||
.iter()
|
||||
.any(|reference| reference.entity == *handle && reference.marker.is_none())
|
||||
|| symmetric_constraints.iter().any(|constraint| {
|
||||
constraint.refs.get(2).is_some_and(|axis| axis.entity == *handle)
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -2659,6 +2760,18 @@ fn solve_scope(
|
||||
let EntityGeom::Ellipse(el) = geom else {
|
||||
continue;
|
||||
};
|
||||
let symmetric = set.constraints.iter().any(|constraint| {
|
||||
constraint.enabled
|
||||
&& constraint.kind == ConstraintKind::Symmetric
|
||||
&& constraint.refs.get(..2).is_some_and(|references| {
|
||||
references.iter().any(|reference| {
|
||||
reference.entity == *handle && reference.marker.is_none()
|
||||
})
|
||||
})
|
||||
});
|
||||
if symmetric {
|
||||
continue;
|
||||
}
|
||||
let (cx, cy, fx, fy) = {
|
||||
let store = sys.store();
|
||||
(
|
||||
|
||||
Reference in new issue
Block a user