Thanks @thodoristsagko.
This commit is contained in:
1 parent
f350c35414
commit
337564190b
2 files changed
+68
-8
No files matched your search
@@ -114,6 +114,8 @@ pub enum Kind {
|
|||||||
OffsetPlane {
|
OffsetPlane {
|
||||||
offset: String,
|
offset: String,
|
||||||
},
|
},
|
||||||
|
/// Halfway between two planes or planar faces.
|
||||||
|
Midplane,
|
||||||
/// A construction plane through an axis at an angle to a plane.
|
/// A construction plane through an axis at an angle to a plane.
|
||||||
AnglePlane {
|
AnglePlane {
|
||||||
angle: String,
|
angle: String,
|
||||||
@@ -521,6 +523,10 @@ impl Dialog {
|
|||||||
"construct.plane.offset" => {
|
"construct.plane.offset" => {
|
||||||
Dialog::new(Kind::OffsetPlane { offset: "10 mm".into() }, vec![SelInput::new("Plane", PLANES | PLANAR_FACES, false)])
|
Dialog::new(Kind::OffsetPlane { offset: "10 mm".into() }, vec![SelInput::new("Plane", PLANES | PLANAR_FACES, false)])
|
||||||
}
|
}
|
||||||
|
"construct.plane.midplane" => Dialog::new(
|
||||||
|
Kind::Midplane,
|
||||||
|
vec![SelInput::new("First plane", PLANES | PLANAR_FACES, false), SelInput::new("Second plane", PLANES | PLANAR_FACES, false)],
|
||||||
|
),
|
||||||
"construct.plane.along_path" => {
|
"construct.plane.along_path" => {
|
||||||
Dialog::new(Kind::PathPlane { t: "0.5".into(), sketch: None }, vec![SelInput::new("Path", CURVES | EDGES, false)])
|
Dialog::new(Kind::PathPlane { t: "0.5".into(), sketch: None }, vec![SelInput::new("Path", CURVES | EDGES, false)])
|
||||||
}
|
}
|
||||||
@@ -790,6 +796,7 @@ impl Dialog {
|
|||||||
| Kind::Loft { .. }
|
| Kind::Loft { .. }
|
||||||
| Kind::Sweep { .. }
|
| Kind::Sweep { .. }
|
||||||
| Kind::PathPlane { .. }
|
| Kind::PathPlane { .. }
|
||||||
|
| Kind::Midplane
|
||||||
| Kind::Move { .. }
|
| Kind::Move { .. }
|
||||||
| Kind::Hole { .. }
|
| Kind::Hole { .. }
|
||||||
| Kind::Primitive { .. }
|
| Kind::Primitive { .. }
|
||||||
@@ -1379,6 +1386,7 @@ fn title(k: &Kind) -> &'static str {
|
|||||||
Kind::Measure { .. } => "MEASURE",
|
Kind::Measure { .. } => "MEASURE",
|
||||||
Kind::Material { .. } => "PHYSICAL MATERIAL",
|
Kind::Material { .. } => "PHYSICAL MATERIAL",
|
||||||
Kind::OffsetPlane { .. } => "OFFSET PLANE",
|
Kind::OffsetPlane { .. } => "OFFSET PLANE",
|
||||||
|
Kind::Midplane => "MIDPLANE",
|
||||||
Kind::PathPlane { .. } => "PLANE ALONG PATH",
|
Kind::PathPlane { .. } => "PLANE ALONG PATH",
|
||||||
Kind::Rib { web: false, .. } => "RIB",
|
Kind::Rib { web: false, .. } => "RIB",
|
||||||
Kind::Rib { web: true, .. } => "WEB",
|
Kind::Rib { web: true, .. } => "WEB",
|
||||||
@@ -2686,7 +2694,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
|||||||
enter |= field(ui, t);
|
enter |= field(ui, t);
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
}
|
}
|
||||||
Kind::Split | Kind::ReplaceFace | Kind::Remove => {}
|
Kind::Split | Kind::ReplaceFace | Kind::Remove | Kind::Midplane => {}
|
||||||
Kind::Rib { thickness, depth, flip, .. } => {
|
Kind::Rib { thickness, depth, flip, .. } => {
|
||||||
row_label(ui, "Thickness");
|
row_label(ui, "Thickness");
|
||||||
enter |= field(ui, thickness);
|
enter |= field(ui, thickness);
|
||||||
@@ -3897,15 +3905,16 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
|||||||
}
|
}
|
||||||
Kind::OffsetPlane { offset } => {
|
Kind::OffsetPlane { offset } => {
|
||||||
need(0, "a plane")?;
|
need(0, "a plane")?;
|
||||||
let base = match sels(d, 0).first() {
|
let base = plane_arg(app, sels(d, 0).first())?;
|
||||||
Some(Sel::Plane { name }) => json!(name),
|
|
||||||
Some(face @ Sel::Face { .. }) => {
|
|
||||||
crate::sketch_attachment_ui::face_plane(app, face).ok_or("the picked face is no longer in the model")?
|
|
||||||
}
|
|
||||||
_ => return Err("pick an origin or construction plane, or a planar face".into()),
|
|
||||||
};
|
|
||||||
("construct.plane.offset", json!({"base": base, "offset": offset}))
|
("construct.plane.offset", json!({"base": base, "offset": offset}))
|
||||||
}
|
}
|
||||||
|
Kind::Midplane => {
|
||||||
|
need(0, "the first plane")?;
|
||||||
|
need(1, "the second plane")?;
|
||||||
|
let a = plane_arg(app, sels(d, 0).first())?;
|
||||||
|
let b = plane_arg(app, sels(d, 1).first())?;
|
||||||
|
("construct.plane.midplane", json!({"a": a, "b": b}))
|
||||||
|
}
|
||||||
Kind::PathPlane { t, sketch } => {
|
Kind::PathPlane { t, sketch } => {
|
||||||
need(0, "a path")?;
|
need(0, "a path")?;
|
||||||
let path = match sels(d, 0).first() {
|
let path = match sels(d, 0).first() {
|
||||||
@@ -4495,6 +4504,24 @@ fn emboss_face_sel(s: &Session, component: u64, face: &solidcraft_engine::doc::E
|
|||||||
Some(Sel::Face { body: world_name, index, point })
|
Some(Sel::Face { body: world_name, index, point })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A picked plane or planar face as `construct.plane.*` takes it.
|
||||||
|
fn plane_arg(app: &SolveApp, sel: Option<&Sel>) -> Result<Value, String> {
|
||||||
|
match sel {
|
||||||
|
Some(Sel::Plane { name }) => Ok(json!(name)),
|
||||||
|
Some(face @ Sel::Face { .. }) => crate::sketch_attachment_ui::face_plane(app, face).ok_or("the picked face is no longer in the model".into()),
|
||||||
|
_ => Err("pick an origin or construction plane, or a planar face".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A stored construction reference back into a dialog pick.
|
||||||
|
fn geo_sel(s: &Session, g: &solidcraft_engine::doc::construct::GeoRef) -> Option<Sel> {
|
||||||
|
match g {
|
||||||
|
solidcraft_engine::doc::construct::GeoRef::Plane { plane } => plane_sel(s, plane),
|
||||||
|
solidcraft_engine::doc::construct::GeoRef::Face { at, .. } => face_sel(s, *at),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn plane_sel(s: &Session, pl: &PlaneRef) -> Option<Sel> {
|
pub(crate) fn plane_sel(s: &Session, pl: &PlaneRef) -> Option<Sel> {
|
||||||
match pl {
|
match pl {
|
||||||
PlaneRef::Origin { name } => Some(Sel::Plane { name: name.clone() }),
|
PlaneRef::Origin { name } => Some(Sel::Plane { name: name.clone() }),
|
||||||
@@ -5022,6 +5049,16 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
|||||||
}
|
}
|
||||||
d
|
d
|
||||||
}
|
}
|
||||||
|
FeatureKind::ConstructionPlane { plane: PlaneRef::Midplane { a, b } } => {
|
||||||
|
let mut d = start("construct.plane.midplane")?;
|
||||||
|
if let Some(inp) = d.inputs.first_mut() {
|
||||||
|
inp.items = geo_sel(s, a).into_iter().collect();
|
||||||
|
}
|
||||||
|
if let Some(inp) = d.inputs.get_mut(1) {
|
||||||
|
inp.items = geo_sel(s, b).into_iter().collect();
|
||||||
|
}
|
||||||
|
d
|
||||||
|
}
|
||||||
FeatureKind::ConstructionPlane { plane: PlaneRef::AtAngle { base, axis_origin, axis_dir, angle } } => {
|
FeatureKind::ConstructionPlane { plane: PlaneRef::AtAngle { base, axis_origin, axis_dir, angle } } => {
|
||||||
let mut d = start("construct.plane.at_angle")?;
|
let mut d = start("construct.plane.at_angle")?;
|
||||||
d.kind = Kind::AnglePlane { angle: angle.to_string() };
|
d.kind = Kind::AnglePlane { angle: angle.to_string() };
|
||||||
|
|||||||
@@ -1649,3 +1649,26 @@ fn patch_dialog_rows_are_translated() {
|
|||||||
assert!(h.widget(through).is_some(), "{lang}: the points input must show {through:?}");
|
assert!(h.widget(through).is_some(), "{lang}: the points input must show {through:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Midplane opens a two-plane dialog. Starting it with nothing selected used to run the
|
||||||
|
/// command and report that `a` is required.
|
||||||
|
#[test]
|
||||||
|
fn midplane_asks_for_two_planes_then_builds_the_one_between_them() {
|
||||||
|
let mut h = crate::scenario::Harness::new();
|
||||||
|
h.app.run("construct.plane.offset", json!({"base":"XY","offset":"20 mm","name":"Shelf"})).unwrap();
|
||||||
|
h.app.start("construct.plane.midplane");
|
||||||
|
h.frames(2);
|
||||||
|
let d = h.app.dialog.as_ref().unwrap();
|
||||||
|
assert!(matches!(d.kind, Kind::Midplane));
|
||||||
|
assert_eq!(d.inputs.len(), 2);
|
||||||
|
let status = h.app.status.as_ref().map(|s| s.0.clone()).unwrap_or_default();
|
||||||
|
assert!(!status.contains("is required"), "{status}");
|
||||||
|
h.app.dialog.as_mut().unwrap().inputs[0].items = vec![Sel::Plane { name: "XY".into() }];
|
||||||
|
h.app.dialog.as_mut().unwrap().inputs[1].items = vec![Sel::Plane { name: "Shelf".into() }];
|
||||||
|
h.step(&json!({"key":"Enter"})).unwrap();
|
||||||
|
assert!(h.app.dialog.is_none(), "{:?}", h.app.dialog.as_ref().and_then(|d| d.error.clone()));
|
||||||
|
let (_, _, pl) =
|
||||||
|
solidcraft_engine::view::construction_planes(&h.app.session).into_iter().find(|(_, name, _)| name != "Shelf").expect("the midplane");
|
||||||
|
assert!(pl.normal().cross(Vec3::Z).len() < 1e-6, "{pl:?}");
|
||||||
|
assert!((pl.origin.z - 10.0).abs() < 1e-6, "{pl:?}");
|
||||||
|
}
|
||||||
Reference in new issue
Block a user