Merge pull request #1312 from ghost/ordered-tangent

This commit is contained in:
Hakan Seven committed 2026-09-17 07:08:54 +03:00
commit dfe7904308
7 files changed
+431 -9

No files matched your search

+31 -3
View File
@@ -3211,12 +3211,38 @@ impl Scene {
retain_size: bool,
retained_originals: &[(Handle, EntityType)],
) {
self.bump_entities_with_solve_policy(changes, driven_refs, retain_size, retained_originals, true);
self.bump_entities_with_solve_policy(
changes,
driven_refs,
retain_size,
retained_originals,
&[],
true,
);
}
/// Refresh display and associations after a solved grip or exact history restore.
pub(crate) fn bump_entities_after_parametric_solve(&mut self, changes: &[(Handle, ChangeKind)]) {
self.bump_entities_with_solve_policy(changes, &[], false, &[], false);
self.bump_entities_with_solve_policy(changes, &[], false, &[], &[], false);
}
/// Apply a newly-created ordered relation while temporarily anchoring
/// its reference side. The anchors guide only this first solve and are
/// never added to the persistent constraint graph.
pub fn bump_entities_with_initial_parametric_policy(
&mut self,
changes: &[(Handle, ChangeKind)],
fixed_refs: &[parametric_constraints::ParametricRef],
retain_size: bool,
) {
self.bump_entities_with_solve_policy(
changes,
&[],
retain_size,
&[],
fixed_refs,
true,
);
}
fn bump_entities_with_solve_policy(
@@ -3225,6 +3251,7 @@ impl Scene {
driven_refs: &[parametric_constraints::ParametricRef],
retain_size: bool,
retained_originals: &[(Handle, EntityType)],
fixed_refs: &[parametric_constraints::ParametricRef],
solve_parametric: bool,
) {
if changes.iter().any(|(handle, kind)| {
@@ -3260,11 +3287,12 @@ impl Scene {
}
}
if solve_parametric && !self.parametric_constraints.is_empty() {
for change in self.refresh_parametric_constraints_with_originals(
for change in self.refresh_parametric_constraints_with_initial_policy(
&changes,
driven_refs,
retain_size,
retained_originals,
fixed_refs,
) {
if !changes.iter().any(|(handle, _)| *handle == change.0) {
changes.push(change);
+85 -4
View File
@@ -1904,6 +1904,7 @@ fn solve_scope(
retain_size: bool,
retain_lengths: bool,
retained_before: &HashMap<Handle, std::sync::Arc<EntityType>>,
initial_fixed_refs: &[ParametricRef],
) -> Option<SolveResult> {
let params = if set.local_parameters.is_empty() {
drawing_params
@@ -1960,6 +1961,36 @@ 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 {
let temporary = ParametricConstraint {
id: ConstraintId::MAX,
kind: ConstraintKind::Fixed,
refs: vec![*reference],
driving_param: None,
enabled: true,
native_origin: None,
rigid_points: Vec::new(),
distance_direction_type: distance_direction_type::UNDIRECTED,
distance_direction: None,
angle_sector: angle_sector::PARALLEL_COUNTERCLOCKWISE,
};
for constraint in build_constraint(
document,
&mut sys,
&mut cache,
params,
&temporary,
None,
None,
) {
sys.add_constraint(constraint);
}
}
let has_smooth = constraints.iter()
.any(|constraint| constraint.enabled && constraint.kind == ConstraintKind::Smooth);
if cache.is_empty() && !has_smooth {
@@ -2090,6 +2121,12 @@ fn solve_scope(
// endpoints. Other edits can retain line lengths as well.
if retain_size {
for (handle, geom) in &cache {
if initial_fixed_refs
.iter()
.any(|reference| reference.entity == *handle)
{
continue;
}
match geom {
EntityGeom::Line(line) | EntityGeom::TextLine(line) if retain_lengths => {
let current_length = {
@@ -2145,12 +2182,32 @@ fn solve_scope(
sys.add_constraint(Rc::new(Equal::new(circle.rad, target, 1.0)));
}
EntityGeom::Arc(arc) => {
let (current_start, current_end) = {
let store = sys.store();
(store.get(arc.start_angle), store.get(arc.end_angle))
};
let radius = retained_before
.get(handle)
.and_then(|entity| retained_radius(entity))
.unwrap_or_else(|| sys.store().get(arc.circle.rad));
let target = sys.add_param(radius, true);
sys.add_constraint(Rc::new(Equal::new(arc.circle.rad, target, 1.0)));
let target_radius = sys.add_param(radius, true);
let target_start = sys.add_param(current_start, true);
let target_end = sys.add_param(current_end, true);
sys.add_constraint(Rc::new(Equal::new(
arc.circle.rad,
target_radius,
1.0,
)));
sys.add_constraint(Rc::new(Equal::new(
arc.start_angle,
target_start,
1.0,
)));
sys.add_constraint(Rc::new(Equal::new(
arc.end_angle,
target_end,
1.0,
)));
}
EntityGeom::Ellipse(ellipse) => {
let (focus_distance, minor_radius) = {
@@ -2954,6 +3011,23 @@ impl Scene {
driven_refs: &[ParametricRef],
retain_size: bool,
retained_originals: &[(Handle, EntityType)],
) -> Vec<(Handle, ChangeKind)> {
self.refresh_parametric_constraints_with_initial_policy(
changes,
driven_refs,
retain_size,
retained_originals,
&[],
)
}
pub(crate) fn refresh_parametric_constraints_with_initial_policy(
&mut self,
changes: &[(Handle, ChangeKind)],
driven_refs: &[ParametricRef],
retain_size: bool,
retained_originals: &[(Handle, EntityType)],
initial_fixed_refs: &[ParametricRef],
) -> Vec<(Handle, ChangeKind)> {
// An erased entity takes its constraints with it instead of leaving
// dangling references. Done first,
@@ -3023,11 +3097,17 @@ impl Scene {
retain_size,
true,
&retained_before,
initial_fixed_refs,
) else {
continue;
};
self.parametric_constraints[i].dof = Some(dof);
self.parametric_constraints[i].conflicts = conflicts;
if initial_fixed_refs.is_empty() {
self.parametric_constraints[i].dof = Some(dof);
self.parametric_constraints[i].conflicts = conflicts;
} else {
self.parametric_constraints[i].dof = None;
self.parametric_constraints[i].conflicts.clear();
}
for (handle, new_entity) in solved {
if let Some(before) = self.document.get_entity_arc(handle) {
self.record_undo_before(handle, Some(before));
@@ -3078,6 +3158,7 @@ impl Scene {
retain_size,
false,
&retained_before,
&[],
)
else {
continue;