forked from 1b-agent/opencadstudio-upstream
Keep untouched constraint geometry fixed during grip edits
This commit is contained in:
1 parent
07d8d7eb00
commit
bfd0c30329
2 files changed
+41
-29
No files matched your search
@@ -6137,9 +6137,9 @@ mod selection_preview_tests {
|
|||||||
.normalize()
|
.normalize()
|
||||||
};
|
};
|
||||||
assert!(direction(current[0]).dot(direction(current[1])).abs() < 1e-8);
|
assert!(direction(current[0]).dot(direction(current[1])).abs() < 1e-8);
|
||||||
assert_ne!(
|
assert_eq!(
|
||||||
current[1], before[1],
|
current[1], before[1],
|
||||||
"constrained neighbor must follow each grip frame"
|
"geometry outside the grip edit must stay fixed"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
current[0][0], before[0][0],
|
current[0][0], before[0][0],
|
||||||
|
|||||||
@@ -2636,11 +2636,24 @@ impl Scene {
|
|||||||
if !is_touched {
|
if !is_touched {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let mut anchors = driven_refs.to_vec();
|
||||||
|
for reference in set
|
||||||
|
.constraints
|
||||||
|
.iter()
|
||||||
|
.filter(|constraint| constraint.enabled)
|
||||||
|
.flat_map(|constraint| &constraint.refs)
|
||||||
|
{
|
||||||
|
if !touched.contains(&reference.entity)
|
||||||
|
&& !anchors.iter().any(|anchor| anchor.entity == reference.entity)
|
||||||
|
{
|
||||||
|
anchors.push(ParametricRef::whole(reference.entity));
|
||||||
|
}
|
||||||
|
}
|
||||||
let Some((solved, _dof, _conflicts)) = solve_scope(
|
let Some((solved, _dof, _conflicts)) = solve_scope(
|
||||||
&self.document,
|
&self.document,
|
||||||
&self.named_parameters,
|
&self.named_parameters,
|
||||||
set,
|
set,
|
||||||
driven_refs,
|
&anchors,
|
||||||
retain_size,
|
retain_size,
|
||||||
&retained_before,
|
&retained_before,
|
||||||
)
|
)
|
||||||
@@ -2912,11 +2925,11 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `solve_parametric_constraints_preview` reports the constrained
|
/// A grip preview treats geometry outside the edit as a fixed reference,
|
||||||
/// neighbor's solved position without writing anything into the
|
/// so a parallel constraint projects the dragged endpoint without moving
|
||||||
/// document itself — the grip-drag caller owns applying it.
|
/// the other line or changing their separation.
|
||||||
#[test]
|
#[test]
|
||||||
fn preview_solve_reports_the_neighbors_new_state_without_mutating_the_document() {
|
fn preview_solve_keeps_untouched_parallel_geometry_fixed() {
|
||||||
let mut scene = Scene::new();
|
let mut scene = Scene::new();
|
||||||
let a = scene.add_entity(EntityType::Line(acadrust::entities::Line::from_points(
|
let a = scene.add_entity(EntityType::Line(acadrust::entities::Line::from_points(
|
||||||
Vector3::new(0.0, 0.0, 0.0),
|
Vector3::new(0.0, 0.0, 0.0),
|
||||||
@@ -2941,40 +2954,39 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let b_before = scene.document.get_entity(b).cloned();
|
let b_before = scene.document.get_entity(b).cloned();
|
||||||
|
|
||||||
let solved = scene.solve_parametric_constraints_preview(&[a], &[], false, &[]);
|
let solved = scene.solve_parametric_constraints_preview(
|
||||||
|
&[a],
|
||||||
|
&[ParametricRef::point(a, 0)],
|
||||||
|
false,
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
scene.document.get_entity(b),
|
scene.document.get_entity(b),
|
||||||
b_before.as_ref(),
|
b_before.as_ref(),
|
||||||
"preview must not mutate the document"
|
"preview must not mutate the document"
|
||||||
);
|
);
|
||||||
// The whole scope solves together (every registered entity's params
|
let moved_a = solved
|
||||||
// are free, not just `b`'s — see `register_entity`'s doc comment),
|
|
||||||
// so `a` itself may also have shifted slightly to reach the nearest
|
|
||||||
// mutually-parallel configuration; read whichever position `a` ends
|
|
||||||
// up at from `solved` too, rather than assuming it stayed exactly
|
|
||||||
// where the simulated drag put it.
|
|
||||||
let line_dir = |entity: &EntityType| {
|
|
||||||
let EntityType::Line(l) = entity else {
|
|
||||||
panic!("expected a Line")
|
|
||||||
};
|
|
||||||
(l.end.x - l.start.x, l.end.y - l.start.y)
|
|
||||||
};
|
|
||||||
let dir_a = solved
|
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(h, _)| *h == a)
|
.find(|(h, _)| *h == a)
|
||||||
.map(|(_, e)| line_dir(e))
|
.map(|(_, entity)| entity)
|
||||||
.unwrap_or((10.0, 6.0));
|
.expect("the dragged line should be projected");
|
||||||
let (_, moved_entity) = solved
|
assert!(solved.iter().all(|(handle, _)| *handle != b));
|
||||||
.iter()
|
let EntityType::Line(moved_a_line) = moved_a else {
|
||||||
.find(|(h, _)| *h == b)
|
panic!("expected a Line")
|
||||||
.expect("b should be reported as moved");
|
};
|
||||||
let dir_b = line_dir(moved_entity);
|
assert_eq!(moved_a_line.start, Vector3::new(0.0, 0.0, 0.0));
|
||||||
let cross = dir_a.0 * dir_b.1 - dir_a.1 * dir_b.0;
|
let EntityType::Line(fixed_b) = b_before.unwrap() else {
|
||||||
|
panic!("expected a Line")
|
||||||
|
};
|
||||||
|
let dir_a = moved_a_line.end - moved_a_line.start;
|
||||||
|
let dir_b = fixed_b.end - fixed_b.start;
|
||||||
|
let cross = dir_a.x * dir_b.y - dir_a.y * dir_b.x;
|
||||||
assert!(
|
assert!(
|
||||||
cross.abs() < 1e-6,
|
cross.abs() < 1e-6,
|
||||||
"the previewed positions should be mutually parallel: dir_a={dir_a:?} dir_b={dir_b:?}"
|
"the previewed positions should be mutually parallel: dir_a={dir_a:?} dir_b={dir_b:?}"
|
||||||
);
|
);
|
||||||
|
assert_eq!(fixed_b.start.y - moved_a_line.start.y, 5.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in new issue
Block a user