Merge pull request #771 from gianlucafiore/fix/overlapping-grips
Fix overlapping grips moving independently
This commit is contained in:
commit
3f989fe94c
1 changed files with 54 additions and 21 deletions
|
|
@ -489,19 +489,19 @@ impl OpenCADStudio {
|
||||||
.zip(self.tabs[i].selected_grips.iter())
|
.zip(self.tabs[i].selected_grips.iter())
|
||||||
.find(|(owner, grip)| *owner == handle && grip.id == grip_id)
|
.find(|(owner, grip)| *owner == handle && grip.id == grip_id)
|
||||||
.and_then(|(_, grip)| grip.axis);
|
.and_then(|(_, grip)| grip.axis);
|
||||||
if !self.tabs[i].hot_grips.contains(&(handle, grip_id)) {
|
|
||||||
self.tabs[i].hot_grips.clear();
|
|
||||||
let mut edit = GripEdit::single(handle, grip_id, is_translate, world);
|
|
||||||
edit.axis = axis;
|
|
||||||
return edit;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut targets: Vec<GripTarget> = self.tabs[i]
|
let clicked_is_hot = self.tabs[i].hot_grips.contains(&(handle, grip_id));
|
||||||
|
|
||||||
|
let mut targets: Vec<GripTarget> = if clicked_is_hot {
|
||||||
|
// Explicit hot grips keep their existing multi-grip behaviour.
|
||||||
|
self.tabs[i]
|
||||||
.selected_grip_handles
|
.selected_grip_handles
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.zip(self.tabs[i].selected_grips.iter())
|
.zip(self.tabs[i].selected_grips.iter())
|
||||||
.filter(|(owner, grip)| self.tabs[i].hot_grips.contains(&(*owner, grip.id)))
|
.filter(|(owner, grip)| {
|
||||||
|
self.tabs[i].hot_grips.contains(&(*owner, grip.id))
|
||||||
|
})
|
||||||
.filter(|(_, grip)| grip.id != crate::app::visibility::VIS_GRIP_ID)
|
.filter(|(_, grip)| grip.id != crate::app::visibility::VIS_GRIP_ID)
|
||||||
.map(|(owner, grip)| GripTarget {
|
.map(|(owner, grip)| GripTarget {
|
||||||
handle: owner,
|
handle: owner,
|
||||||
|
|
@ -509,17 +509,45 @@ impl OpenCADStudio {
|
||||||
is_translate: grip.is_midpoint,
|
is_translate: grip.is_midpoint,
|
||||||
last_world: grip.world,
|
last_world: grip.world,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
|
} else {
|
||||||
|
// A normal click on coincident grips stretches all selected grips that
|
||||||
|
// share the same geometric point. Use a tiny world-space tolerance:
|
||||||
|
// visually-near grips caused by zoom must remain independent.
|
||||||
|
self.tabs[i].hot_grips.clear();
|
||||||
|
|
||||||
|
const COINCIDENT_GRIP_EPSILON: f64 = 1.0e-9;
|
||||||
|
let epsilon_sq = COINCIDENT_GRIP_EPSILON * COINCIDENT_GRIP_EPSILON;
|
||||||
|
|
||||||
|
self.tabs[i]
|
||||||
|
.selected_grip_handles
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.zip(self.tabs[i].selected_grips.iter())
|
||||||
|
.filter(|(_, grip)| grip.id != crate::app::visibility::VIS_GRIP_ID)
|
||||||
|
.filter(|(_, grip)| {
|
||||||
|
(grip.world - world).length_squared() <= epsilon_sq
|
||||||
|
})
|
||||||
|
.map(|(owner, grip)| GripTarget {
|
||||||
|
handle: owner,
|
||||||
|
grip_id: grip.id,
|
||||||
|
is_translate: grip.is_midpoint,
|
||||||
|
last_world: grip.world,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
// A midpoint/centre grip translates its whole entity. If that entity also
|
// A midpoint/centre grip translates its whole entity. If that entity also
|
||||||
// has hot point grips, applying both would move it twice; one translate
|
// has coincident point grips, applying both would move it twice; one
|
||||||
// target owns the entity in that case.
|
// translate target owns that entity in that case.
|
||||||
let translate_handles: rustc_hash::FxHashSet<_> = targets
|
let translate_handles: rustc_hash::FxHashSet<_> = targets
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|target| target.is_translate)
|
.filter(|target| target.is_translate)
|
||||||
.map(|target| target.handle)
|
.map(|target| target.handle)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut used_translates = rustc_hash::FxHashSet::default();
|
let mut used_translates = rustc_hash::FxHashSet::default();
|
||||||
|
|
||||||
targets.retain(|target| {
|
targets.retain(|target| {
|
||||||
if translate_handles.contains(&target.handle) {
|
if translate_handles.contains(&target.handle) {
|
||||||
target.is_translate && used_translates.insert(target.handle)
|
target.is_translate && used_translates.insert(target.handle)
|
||||||
|
|
@ -527,12 +555,17 @@ impl OpenCADStudio {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if targets.is_empty() {
|
if targets.is_empty() {
|
||||||
let mut edit = GripEdit::single(handle, grip_id, is_translate, world);
|
let mut edit = GripEdit::single(handle, grip_id, is_translate, world);
|
||||||
edit.axis = axis;
|
edit.axis = axis;
|
||||||
return edit;
|
return edit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An axis constraint belongs to a single grip. When several coincident
|
||||||
|
// grips participate, let the common cursor delta drive all of them.
|
||||||
let axis = (targets.len() == 1).then_some(axis).flatten();
|
let axis = (targets.len() == 1).then_some(axis).flatten();
|
||||||
|
|
||||||
GripEdit {
|
GripEdit {
|
||||||
handle,
|
handle,
|
||||||
grip_id,
|
grip_id,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue