forked from 1b-agent/opencadstudio-upstream
Add bounding-box object alignment (ALIGNLEFT/TOP/etc.), ported from Mac2CAM
Six one-shot alignment commands for the current selection — ALIGNLEFT/ALIGNHCENTER/ALIGNRIGHT/ALIGNTOP/ALIGNVCENTER/ALIGNBOTTOM — distinct from the existing point-picking ALIGN/ALIGN3D command: each nudges every selected object (or complete group, via new Scene::selected_object_units) so its bounding-box edge/center lines up with the extreme edge/center already present in the selection, closer to a word processor's "align objects" than AutoCAD's 3-point ALIGN. Wired as plain one-click Tool entries in the Modify extension panel, next to the existing ALIGN entry, matching that panel's flat-list convention (no point prompts, so no dropdown submenu needed). Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
59ae38ae1a
commit
0f739b459d
10 files changed
+329
No files matched your search
@@ -203,6 +203,42 @@ impl Scene {
|
||||
self.bump_selection_set();
|
||||
}
|
||||
}
|
||||
|
||||
/// Partition selected entity handles into visual objects for operations
|
||||
/// such as edge alignment. A selectable CAD group is one object and must
|
||||
/// receive one shared transform; treating its member lines independently
|
||||
/// can collapse a rectangle or triangle onto its own edge.
|
||||
pub fn selected_object_units(&self, handles: &[Handle]) -> Vec<Vec<Handle>> {
|
||||
let selected: HashSet<Handle> = handles.iter().copied().collect();
|
||||
let mut claimed = HashSet::default();
|
||||
let mut units = Vec::new();
|
||||
for group in self.groups().filter(|group| group.selectable) {
|
||||
let members: Vec<_> = group
|
||||
.entities
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|handle| selected.contains(handle))
|
||||
.collect();
|
||||
if !members.is_empty()
|
||||
&& members.iter().all(|handle| !claimed.contains(handle))
|
||||
&& group
|
||||
.entities
|
||||
.iter()
|
||||
.all(|handle| selected.contains(handle))
|
||||
{
|
||||
claimed.extend(members.iter().copied());
|
||||
units.push(members);
|
||||
}
|
||||
}
|
||||
units.extend(
|
||||
handles
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|handle| !claimed.contains(handle))
|
||||
.map(|handle| vec![handle]),
|
||||
);
|
||||
units
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -247,4 +283,31 @@ mod group_expansion_tests {
|
||||
assert_eq!(both, [a, b, c, d, e].into_iter().collect::<HashSet<_>>());
|
||||
assert!(!both.contains(&lonely));
|
||||
}
|
||||
|
||||
/// `selected_object_units` is `Scene::selected_object_units`'s
|
||||
/// counterpart for operations like edge alignment (`ALIGNTOP`, etc.):
|
||||
/// a complete, fully-selected group collapses to one unit sharing a
|
||||
/// single transform, so aligning a triangle's edge to another object
|
||||
/// moves the whole triangle rather than collapsing its own lines onto
|
||||
/// each other.
|
||||
#[test]
|
||||
fn selected_object_units_keep_a_complete_group_together() {
|
||||
use acadrust::entities::{EntityType, Line};
|
||||
use acadrust::types::Vector3;
|
||||
|
||||
let mut scene = Scene::new();
|
||||
let mut line = |x: f64| {
|
||||
scene.add_entity(EntityType::Line(Line::from_points(
|
||||
Vector3::new(x, 0.0, 0.0),
|
||||
Vector3::new(x + 1.0, 0.0, 0.0),
|
||||
)))
|
||||
};
|
||||
let (a, b, c, standalone) = (line(0.0), line(1.0), line(2.0), line(10.0));
|
||||
scene.create_group("triangle".into(), vec![a, b, c]);
|
||||
|
||||
let units = scene.selected_object_units(&[a, b, c, standalone]);
|
||||
assert_eq!(units.len(), 2);
|
||||
assert!(units.iter().any(|unit| unit == &vec![a, b, c]));
|
||||
assert!(units.iter().any(|unit| unit == &vec![standalone]));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user