Rigid Group counts picked instances and explains bodies in one component (#4790) (#4792)

This commit is contained in:
raykholo
2026-10-25 19:13:48 +00:00
committed by GitHub
parent 0252f0bf30
commit fbe778a1e8
3 changed files with 62 additions and 2 deletions
@@ -0,0 +1,14 @@
{
"en": {
"joint.rigid_group_one_component": "These {count} bodies are all in one component (or in the design itself): use {command} first.",
"joint.rigid_group_select_components": "Select two or more components."
},
"ja": {
"joint.rigid_group_one_component": "これらの {count} 個のボディは、すべて 1 つのコンポーネント内(またはデザイン自体)にあります。先に「{command}」を使用してください。",
"joint.rigid_group_select_components": "2 つ以上のコンポーネントを選択してください。"
},
"zh": {
"joint.rigid_group_one_component": "这 {count} 个实体都位于同一组件中(或直接位于设计中):请先使用“{command}”。",
"joint.rigid_group_select_components": "请选择两个或更多组件。"
}
}
+9 -2
View File
@@ -973,9 +973,16 @@ pub fn commands(app: &SolveApp, k: &Asm, inputs: &[SelInput]) -> Result<Vec<(Str
cmd("joint.origin", p)
}
Asm::RigidGroup => {
let occs = occurrences(s, sel_items(inputs, 0));
let items = sel_items(inputs, 0);
let occs = occurrences(s, items);
if occs.len() < 2 {
return Err("select two or more components".into());
if occs.len() == 1 && items.len() > 1 && items.iter().all(|x| matches!(x, Sel::Body { .. })) {
return Err(crate::i18n::format(
"joint.rigid_group_one_component",
&[("count", &items.len().to_string()), ("command", crate::i18n::tr("cmd.component_from_bodies"))],
));
}
return Err(crate::i18n::tr("joint.rigid_group_select_components").into());
}
cmd("joint.rigid_group", json!({ "occurrences": occs }))
}
@@ -455,6 +455,45 @@ fn picks_on_one_component_say_why_and_offer_components() {
assert!(text_at(&out, &why).is_none(), "nothing left to say");
}
#[test]
fn rigid_group_bodies_in_one_component_explain_how_to_separate_them() {
for component in [None, Some("Part")] {
let mut s = Session::default();
if let Some(name) = component {
s.execute("component.create", &json!({"name": name})).unwrap();
}
for x in [0, 30, 60, 90, 120] {
s.execute("solid.box", &json!({"length": 10, "width": 10, "height": 10, "corner": [x, 0, 0]})).unwrap();
}
let mut app = SolveApp::new(s, Services::default());
app.session.selection = (1..=5).map(|i| Sel::Body { name: format!("Body{i}") }).collect();
app.start("joint.rigid_group");
let d = app.dialog.take().unwrap();
assert_eq!(
apply_commands(&app, &d).unwrap_err(),
"These 5 bodies are all in one component (or in the design itself): use Create Components from Bodies first."
);
app.run("component.from_bodies", json!({"bodies": ["Body1", "Body2", "Body3", "Body4", "Body5"]})).unwrap();
assert_eq!(apply_commands(&app, &d).unwrap()[0].1["occurrences"].as_array().unwrap().len(), 5);
}
}
#[test]
fn rigid_group_preserves_two_instances_of_one_component() {
let mut s = Session::default();
let first = s.execute("component.create", &json!({"name": "Part"})).unwrap()["occurrence"].as_u64().unwrap();
s.execute("solid.box", &json!({"length": 10, "width": 10, "height": 10})).unwrap();
let second = s.execute("occurrence.copy", &json!({"occurrence": first, "translate": [30, 0, 0]})).unwrap()["occurrence"].as_u64().unwrap();
s.selection = [first, second].into_iter().map(|id| Sel::Body { name: s.occurrence_bodies(&[id], false).unwrap()[0].clone() }).collect();
let mut app = SolveApp::new(s, Services::default());
app.start("joint.rigid_group");
let d = app.dialog.take().unwrap();
let commands = apply_commands(&app, &d).unwrap();
assert_eq!(commands, vec![("joint.rigid_group".into(), json!({"occurrences": [first, second]}))]);
crate::dialogs::run_dialog(&mut app, &d).unwrap();
assert_eq!(app.session.doc.assembly.joints.len(), 1);
}
/// Two top faces mate face to face: B ends up upside down on A, not inside it;
/// Flip turns it back over (into A).
#[test]