Feat/Sketch: associative pattern tools with independent directions and Flip/Center controls (#1601)
Thanks @Karl-VonRichter.
This commit is contained in:
1 parent
44968dbfe6
commit
371578a06f
36 files changed
+1394
-87
No files matched your search
@@ -456,7 +456,7 @@ fn open(cmd: &str) -> (crate::scenario::Harness, String) {
|
||||
|
||||
fn rect(h: &mut crate::scenario::Harness, count: &str, spacing: &str, count2: &str, spacing2: &str) {
|
||||
let d = h.app.dialog.as_mut().expect("the window opened");
|
||||
if let Kind::SketchModify(Mod::PatternRect { count: c1, spacing: s1, count2: c2, spacing2: s2 }) = &mut d.kind {
|
||||
if let Kind::SketchModify(Mod::PatternRect { count: c1, spacing: s1, count2: c2, spacing2: s2, .. }) = &mut d.kind {
|
||||
*c1 = count.into();
|
||||
*s1 = spacing.into();
|
||||
*c2 = count2.into();
|
||||
@@ -472,12 +472,58 @@ fn rectangular_pattern_window_opens_instead_of_a_click_tool() {
|
||||
assert!(h.app.tool.is_none(), "the click tool still takes the command");
|
||||
let d = h.app.dialog.as_ref().expect("the window opened");
|
||||
assert!(matches!(d.kind, Kind::SketchModify(Mod::PatternRect { .. })), "{:?}", d.kind);
|
||||
assert_eq!(d.inputs.iter().map(|i| i.label).collect::<Vec<_>>(), vec!["Entities", "Direction"]);
|
||||
assert_eq!(d.inputs.iter().map(|i| i.label).collect::<Vec<_>>(), vec!["Entities", "Direction", "Direction 2"]);
|
||||
// Nothing is committed by opening the window.
|
||||
let sid = h.app.session.active_sketch.unwrap();
|
||||
assert_eq!(h.app.session.model.state().sketch(sid).unwrap().sketch.curves.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rectangular_columns_flip_and_center_independently_and_restore_even_counts_when_editing() {
|
||||
use solidcraft_engine::sketch::PatternAlignment;
|
||||
let (mut h, id) = open("sketch.pattern.rectangular");
|
||||
h.app.dialog.as_mut().unwrap().inputs[0].items = vec![Sel::SketchCurve { id }];
|
||||
rect(&mut h, "4", "20", "2", "10");
|
||||
h.settle();
|
||||
let first = crate::scenario::handle_at("dialog:Spacing").unwrap();
|
||||
let second = crate::scenario::handle_at("dialog:Spacing 2").unwrap();
|
||||
assert!(second.x - first.x > 180.0, "the directions should occupy separate columns: {first:?}, {second:?}");
|
||||
assert!((second.y - first.y).abs() < 2.0, "the independent spacing fields should share a row");
|
||||
for label in ["Alignment", "Alignment 2"] {
|
||||
let left = crate::scenario::handle_at(&format!("dialog:{label}:Flip direction")).unwrap();
|
||||
let right = crate::scenario::handle_at(&format!("dialog:{label}:Center")).unwrap();
|
||||
assert!((left.y - right.y).abs() < 2.0, "alignment choices should fit on a single line: {left:?}, {right:?}");
|
||||
}
|
||||
h.step(&json!({"click":{"handle":"dialog:Alignment:Center"}})).unwrap();
|
||||
h.step(&json!({"click":{"handle":"dialog:Alignment 2:Flip direction"}})).unwrap();
|
||||
let d = h.app.dialog.as_ref().unwrap();
|
||||
let commands = apply_commands(&h.app, d).unwrap();
|
||||
assert_eq!(commands[0].1["alignment"], "center");
|
||||
assert_eq!(commands[0].1["alignment2"], "right");
|
||||
assert_eq!(commands[0].1["reversed2"], true);
|
||||
assert_eq!(commands[0].1["count"], 4);
|
||||
h.step(&json!({"click":{"handle":"dialog:OK"}})).unwrap();
|
||||
h.settle();
|
||||
assert!(h.app.dialog.is_none());
|
||||
let sid = h.app.session.active_sketch.unwrap();
|
||||
let pattern = h.app.session.world_state().sketch(sid).unwrap().sketch.patterns[0].id.clone();
|
||||
assert!(crate::sketch_modify_ui::edit_pattern(&mut h.app, &pattern));
|
||||
assert!(matches!(
|
||||
h.app.dialog.as_ref().unwrap().kind,
|
||||
Kind::SketchModify(Mod::PatternRect { alignment: PatternAlignment::Center, alignment2: PatternAlignment::Right, reversed2: true, .. })
|
||||
));
|
||||
rect(&mut h, "4", "20", "3", "10");
|
||||
assert!(apply_commands(&h.app, h.app.dialog.as_ref().unwrap()).is_ok());
|
||||
h.settle();
|
||||
h.step(&json!({"click":{"handle":"dialog:Alignment:Flip direction"}})).unwrap();
|
||||
h.step(&json!({"click":{"handle":"dialog:Alignment 2:Center"}})).unwrap();
|
||||
let commands = apply_commands(&h.app, h.app.dialog.as_ref().unwrap()).unwrap();
|
||||
assert_eq!(commands[0].1["alignment"], "center");
|
||||
assert_eq!(commands[0].1["reversed"], true);
|
||||
assert_eq!(commands[0].1["alignment2"], "center");
|
||||
assert_eq!(commands[0].1["reversed2"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_counts_and_spacings_reach_the_command_with_the_second_direction() {
|
||||
let (mut h, id) = open("sketch.pattern.rectangular");
|
||||
@@ -506,6 +552,45 @@ fn a_picked_axis_becomes_an_in_plane_direction() {
|
||||
assert_eq!(cmds[0].1["dir"], json!([0.0, 1.0]), "sketch y on the XY plane");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_picked_lines_are_sent_as_live_references_and_restored_in_the_editor() {
|
||||
let (mut h, id) = open("sketch.pattern.rectangular");
|
||||
h.app.run("sketch.line", json!({"points":[[0,-20],[10,-20]],"ids":["dir1"]})).unwrap();
|
||||
h.app.run("sketch.line", json!({"points":[[0,-40],[6,-32]],"ids":["dir2"]})).unwrap();
|
||||
let d = h.app.dialog.as_mut().unwrap();
|
||||
d.inputs[0].items = vec![Sel::SketchCurve { id }];
|
||||
d.inputs[1].items = vec![Sel::SketchCurve { id: "dir1".into() }];
|
||||
d.inputs[2].items = vec![Sel::SketchCurve { id: "dir2".into() }];
|
||||
rect(&mut h, "3", "20", "2", "10");
|
||||
let d = h.app.dialog.clone().unwrap();
|
||||
let cmds = apply_commands(&h.app, &d).unwrap();
|
||||
assert_eq!(cmds[0].1["direction"], "dir1");
|
||||
assert_eq!(cmds[0].1["direction2"], "dir2");
|
||||
let out = h.app.run(&cmds[0].0, cmds[0].1.clone()).unwrap();
|
||||
assert!(crate::sketch_modify_ui::edit_pattern(&mut h.app, out["pattern"].as_str().unwrap()));
|
||||
let d = h.app.dialog.as_ref().unwrap();
|
||||
assert_eq!(d.inputs[1].items, vec![Sel::SketchCurve { id: "dir1".into() }]);
|
||||
assert_eq!(d.inputs[2].items, vec![Sel::SketchCurve { id: "dir2".into() }]);
|
||||
let reopened = apply_commands(&h.app, d).unwrap();
|
||||
assert_eq!(reopened[0].1["direction2"], "dir2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sketch_pattern_tools_are_promoted_together_and_open_from_the_toolbar() {
|
||||
let mut h = Harness::new();
|
||||
h.app.run("sketch.create", json!({"plane":"XY"})).unwrap();
|
||||
let panels = crate::toolbar_custom::panel_lists("SKETCH");
|
||||
let (_, cmds, promoted) = panels.iter().find(|(panel, ..)| *panel == "PATTERN").unwrap();
|
||||
let ids: Vec<_> = cmds.iter().take(*promoted).map(|c| c.id).collect();
|
||||
assert_eq!(ids, ["sketch.mirror", "sketch.pattern.rectangular", "sketch.pattern.circular"]);
|
||||
for id in ids {
|
||||
h.settle();
|
||||
h.step(&json!({"click":{"handle":format!("toolbar:{id}")}})).unwrap();
|
||||
assert!(matches!(h.app.dialog.as_ref().map(|d| &d.kind), Some(Kind::SketchModify(_))), "{id}");
|
||||
h.step(&json!({"key":"Escape"})).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_axis_perpendicular_to_the_sketch_is_reported_not_silently_replaced() {
|
||||
let (mut h, id) = open("sketch.pattern.rectangular");
|
||||
@@ -590,7 +675,7 @@ fn open_circ() -> (crate::scenario::Harness, String) {
|
||||
|
||||
fn circ(h: &mut crate::scenario::Harness, count: &str, angle: &str) {
|
||||
let d = h.app.dialog.as_mut().expect("the window opened");
|
||||
if let Kind::SketchModify(Mod::PatternCirc { count: c, angle: a }) = &mut d.kind {
|
||||
if let Kind::SketchModify(Mod::PatternCirc { count: c, angle: a, .. }) = &mut d.kind {
|
||||
*c = count.into();
|
||||
*a = angle.into();
|
||||
} else {
|
||||
@@ -627,6 +712,41 @@ fn the_typed_count_and_total_angle_reach_the_command() {
|
||||
assert_eq!(apply_commands(&h.app, &d).unwrap()[0].1["angle"], json!(90.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn circular_flip_and_center_buttons_apply_and_reopen_the_saved_placement() {
|
||||
use solidcraft_engine::sketch::{PatternAlignment, SketchPatternKind};
|
||||
let (mut h, _) = open_circ();
|
||||
circ(&mut h, "6", "90 deg");
|
||||
h.frames(3);
|
||||
h.step(&json!({"click":{"handle":"dialog:Alignment:Center"}})).unwrap();
|
||||
h.step(&json!({"click":{"handle":"dialog:Alignment:Flip direction"}})).unwrap();
|
||||
let d = h.app.dialog.clone().unwrap();
|
||||
let params = &apply_commands(&h.app, &d).unwrap()[0].1;
|
||||
assert_eq!(params["count"], json!(6));
|
||||
assert_eq!(params["angle"], json!(90.));
|
||||
assert_eq!(params["alignment"], json!("center"));
|
||||
assert_eq!(params["reversed"], json!(true));
|
||||
h.step(&json!({"click":{"handle":"dialog:OK"}})).unwrap();
|
||||
let sid = h.app.session.active_sketch.unwrap();
|
||||
let id = {
|
||||
let state = h.app.session.world_state();
|
||||
let pattern = &state.sketch(sid).unwrap().sketch.patterns[0];
|
||||
assert!(matches!(pattern.kind, SketchPatternKind::Circular { alignment: PatternAlignment::Center, reversed: true, .. }));
|
||||
pattern.id.clone()
|
||||
};
|
||||
assert!(crate::sketch_modify_ui::edit_pattern(&mut h.app, &id));
|
||||
assert!(matches!(
|
||||
h.app.dialog.as_ref().unwrap().kind,
|
||||
Kind::SketchModify(Mod::PatternCirc { alignment: PatternAlignment::Center, reversed: true, .. })
|
||||
));
|
||||
h.frames(3);
|
||||
h.step(&json!({"click":{"handle":"dialog:Alignment:Center"}})).unwrap();
|
||||
let d = h.app.dialog.clone().unwrap();
|
||||
let params = &apply_commands(&h.app, &d).unwrap()[0].1;
|
||||
assert_eq!(params["alignment"], json!("right"));
|
||||
assert_eq!(params["reversed"], json!(true));
|
||||
}
|
||||
|
||||
/// The same lesson as upstream #218: `num` reads a JSON number only, and `sketch.pattern.circular`
|
||||
/// falls back to a full turn when `angle` is anything else. An expression has to be evaluated
|
||||
/// here or it disappears in silence.
|
||||
|
||||
Reference in new issue
Block a user