Feature fields: a type for each role (Expr, BodyName, FacePick…) (#1273)
Thanks @jijinbei.
This commit is contained in:
1 parent
3333f8a62b
commit
44968dbfe6
59 files changed
+1505
-1043
No files matched your search
+100
-84
@@ -637,7 +637,7 @@ impl Dialog {
|
||||
let preselection: &[Sel] = if matches!(d.kind, Kind::Text(_)) { &[] } else { &s.selection };
|
||||
for sel in preselection {
|
||||
let as_body = match sel {
|
||||
Sel::Face { body, .. } | Sel::Edge { body, .. } | Sel::Vertex { body, .. } => Some(Sel::Body { name: body.clone() }),
|
||||
Sel::Face { body, .. } | Sel::Edge { body, .. } | Sel::Vertex { body, .. } => Some(Sel::Body { name: body.to_string() }),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(b) = as_body
|
||||
@@ -3343,7 +3343,7 @@ pub fn apply_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>
|
||||
};
|
||||
if let Some(sketch) = original_all
|
||||
&& !d.profile_selection_changed
|
||||
&& let Some(ss) = app.session.world_state().sketch(sketch)
|
||||
&& let Some(ss) = app.session.world_state().sketch(sketch.0)
|
||||
{
|
||||
let mut selected: Vec<usize> = sels(d, 0)
|
||||
.iter()
|
||||
@@ -3483,7 +3483,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
_ => None,
|
||||
});
|
||||
let has_all = |id: u64| curves.iter().all(|c| st.sketch(id).is_some_and(|ss| ss.sketch.curve_index(c).is_some()));
|
||||
let sketch = match sketch.or(edited) {
|
||||
let sketch = match sketch.or(edited.map(u64::from)) {
|
||||
Some(id) if has_all(id) => id,
|
||||
_ => curves_sketch(app, &curves, None).ok_or("the curves must be in one sketch")?,
|
||||
};
|
||||
@@ -4248,14 +4248,15 @@ fn emboss_face_sel(s: &Session, component: u64, face: &solidcraft_engine::doc::E
|
||||
let owner = s.doc.model_body_component(body);
|
||||
let world_name = s.doc.world_body_name(&source, &face.body, owner, &face.path);
|
||||
let author = solidcraft_engine::doc::EmbossFace::component_frame(&s.doc, component, &face.author_path).ok()?;
|
||||
let point = solidcraft_engine::doc::apply_point(&author, face.at);
|
||||
let point = solidcraft_engine::doc::apply_point(&author, face.at.0);
|
||||
s.world_state().body(&world_name)?;
|
||||
Some(Sel::Face { body: world_name, index, point })
|
||||
}
|
||||
|
||||
pub(crate) fn plane_sel(s: &Session, pl: &PlaneRef) -> Option<Sel> {
|
||||
match pl {
|
||||
PlaneRef::Origin { name } | PlaneRef::Construction { name } => Some(Sel::Plane { name: name.clone() }),
|
||||
PlaneRef::Origin { name } => Some(Sel::Plane { name: name.clone() }),
|
||||
PlaneRef::Construction { name } => Some(Sel::Plane { name: name.to_string() }),
|
||||
PlaneRef::Custom { plane } | PlaneRef::Face { plane, .. } => face_sel(s, plane.origin),
|
||||
_ => None,
|
||||
}
|
||||
@@ -4299,7 +4300,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
FeatureKind::Extrude { sketch, profiles, extent, operation, targets, thin } => {
|
||||
let mut d = start("solid.extrude")?;
|
||||
d.kind = Kind::Extrude {
|
||||
distance: extent.distance.clone(),
|
||||
distance: extent.distance.to_string(),
|
||||
direction: match (extent.direction, &extent.distance2) {
|
||||
(_, Some(_)) => TWO_SIDES,
|
||||
(Direction::Positive, None) => 0,
|
||||
@@ -4309,16 +4310,16 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
operation: op_index(operation),
|
||||
auto_op: false,
|
||||
smart: crate::smart_extrude::Smart { manual_dir: true, ..Default::default() },
|
||||
distance2: extent.distance2.clone().unwrap_or_else(|| "10 mm".into()),
|
||||
taper: extent.taper.clone().unwrap_or_else(|| "0 deg".into()),
|
||||
distance2: extent.distance2.as_deref().map(str::to_string).unwrap_or_else(|| "10 mm".into()),
|
||||
taper: extent.taper.as_deref().map(str::to_string).unwrap_or_else(|| "0 deg".into()),
|
||||
taper_present: extent.taper.is_some(),
|
||||
to_offset: extent.to_offset.clone().unwrap_or_default(),
|
||||
start: extent.start_offset.clone().unwrap_or_default(),
|
||||
to_offset: extent.to_offset.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
start: extent.start_offset.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
all: extent.through_all,
|
||||
thin: thin.is_some(),
|
||||
wall: thin.as_ref().map_or_else(|| "2 mm".into(), |t| t.thickness.clone()),
|
||||
wall: thin.as_ref().map_or_else(|| "2 mm".into(), |t| t.thickness.to_string()),
|
||||
wall_side: thin.as_ref().map_or(0, |t| WALL_SIDES.iter().position(|w| ThinSide::parse(w) == Some(t.side)).unwrap_or(0)),
|
||||
wall2: thin.as_ref().and_then(|t| t.thickness2.clone()).unwrap_or_else(|| "1 mm".into()),
|
||||
wall2: thin.as_ref().and_then(|t| t.thickness2.as_deref().map(str::to_string)).unwrap_or_else(|| "1 mm".into()),
|
||||
};
|
||||
d.extra.insert("convention".into(), json!(extent.convention));
|
||||
// Extent references belong to the extrude's authoring frame, including free
|
||||
@@ -4328,7 +4329,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
if let Some(point) = point {
|
||||
set_object(&mut d.inputs, &mut d.active, label, true);
|
||||
if let Some(input) = d.inputs.iter_mut().find(|i| i.label == label) {
|
||||
input.items = vec![Sel::Vertex { body: String::new(), point: solidcraft_engine::doc::apply_point(&frame, point) }];
|
||||
input.items = vec![Sel::Vertex { body: String::new(), point: solidcraft_engine::doc::apply_point(&frame, point.0) }];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4338,9 +4339,9 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
inp.items = thin.curves.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect();
|
||||
}
|
||||
} else {
|
||||
let items = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
let items = st.sketch(sketch.0).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: *sketch, index }).collect();
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: sketch.0, index }).collect();
|
||||
}
|
||||
}
|
||||
if !targets.is_empty() {
|
||||
@@ -4353,12 +4354,12 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
let frame = feature_frame(s, f.component);
|
||||
let stored_axis = match axis {
|
||||
AxisRef::Line { origin, dir } => Some(json!({
|
||||
"origin": pt(solidcraft_engine::doc::apply_point(&frame, *origin)),
|
||||
"dir": pt(solidcraft_engine::doc::apply_vector(&frame, *dir))
|
||||
"origin": pt(solidcraft_engine::doc::apply_point(&frame, origin.0)),
|
||||
"dir": pt(solidcraft_engine::doc::apply_vector(&frame, dir.0))
|
||||
})),
|
||||
_ => None,
|
||||
};
|
||||
d.kind = Kind::Revolve { angle: angle.clone(), operation: op_index(operation), axis: stored_axis };
|
||||
d.kind = Kind::Revolve { angle: angle.to_string(), operation: op_index(operation), axis: stored_axis };
|
||||
d.extra.insert("convention".into(), json!(convention));
|
||||
if let Some(a2) = angle2 {
|
||||
d.extra.insert("angle2".into(), json!(a2));
|
||||
@@ -4369,12 +4370,12 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
if let Some(point) = to {
|
||||
set_to_object(&mut d.inputs, &mut d.active, true);
|
||||
if let Some(input) = d.inputs.iter_mut().find(|i| i.label == TO_OBJECT) {
|
||||
input.items = vec![Sel::Vertex { body: String::new(), point: solidcraft_engine::doc::apply_point(&frame, *point) }];
|
||||
input.items = vec![Sel::Vertex { body: String::new(), point: solidcraft_engine::doc::apply_point(&frame, point.0) }];
|
||||
}
|
||||
}
|
||||
let items = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
let items = st.sketch(sketch.0).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: *sketch, index }).collect();
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: sketch.0, index }).collect();
|
||||
}
|
||||
let ax = match axis {
|
||||
AxisRef::World { axis } => Some(Sel::Axis { name: axis.to_ascii_uppercase() }),
|
||||
@@ -4400,10 +4401,10 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
if let FeatureKind::Chamfer { distance2: d2, angle: a, flip: f, .. } = &f.kind {
|
||||
if let Some(v) = d2 {
|
||||
*ctype = 1;
|
||||
*distance2 = v.clone();
|
||||
*distance2 = v.to_string();
|
||||
} else if let Some(v) = a {
|
||||
*ctype = 2;
|
||||
*angle = v.clone();
|
||||
*angle = v.to_string();
|
||||
}
|
||||
*flip = *f;
|
||||
}
|
||||
@@ -4446,51 +4447,51 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
FilletStyle::Constant | FilletStyle::Chord | FilletStyle::Curvature => {}
|
||||
}
|
||||
// The start was the first edge's last point: ends swapped.
|
||||
let first_end = edges.first().and_then(|p| edge_sel(s, *p)).and_then(|x| match x {
|
||||
let first_end = edges.first().and_then(|p| edge_sel(s, p.0)).and_then(|x| match x {
|
||||
Sel::Edge { body, index, .. } => {
|
||||
s.world_state().body(&body).and_then(|b| b.mesh().edges.get(index).and_then(|e| e.last().copied()))
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
let swapped = first_end.is_some_and(|e| e.dist(*start) < 1e-6);
|
||||
let swapped = first_end.is_some_and(|e| e.dist(start.0) < 1e-6);
|
||||
d.extra.insert("swap_ends".into(), json!(swapped));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = edges.iter().filter_map(|p| edge_sel(s, *p)).collect();
|
||||
inp.items = edges.iter().filter_map(|p| edge_sel(s, p.0)).collect();
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Shell { faces, thickness, direction, tangent_chain, .. } => {
|
||||
let mut d = start("solid.shell")?;
|
||||
d.kind = Kind::Shell { thickness: thickness.clone() };
|
||||
d.kind = Kind::Shell { thickness: thickness.to_string() };
|
||||
d.extra.insert("tangent_chain".into(), json!(tangent_chain));
|
||||
if !direction.is_empty() && direction != "inside" {
|
||||
d.extra.insert("direction".into(), json!(direction));
|
||||
}
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, p.0)).collect();
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Draft { faces, angle, neutral, pull, .. } => {
|
||||
let mut d = start("solid.draft")?;
|
||||
d.kind = Kind::Draft { angle: angle.clone() };
|
||||
d.kind = Kind::Draft { angle: angle.to_string() };
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, p.0)).collect();
|
||||
}
|
||||
if let Some(inp) = d.inputs.get_mut(1) {
|
||||
inp.items = plane_sel(s, neutral).into_iter().collect();
|
||||
}
|
||||
d.extra.insert("pull".into(), pt3(*pull));
|
||||
d.extra.insert("pull".into(), pt3(pull.0));
|
||||
d
|
||||
}
|
||||
FeatureKind::Hole { position, direction, diameter, depth, hole, points, thread, to } => {
|
||||
let mut d = start("solid.hole")?;
|
||||
let mut k = hole_defaults();
|
||||
if let Kind::Hole { diameter: dia, depth: dep, kind, cb_diameter, cb_depth, cs_diameter, cs_angle, opts } = &mut k {
|
||||
*dia = diameter.clone();
|
||||
*dia = diameter.to_string();
|
||||
opts.all = depth.is_none();
|
||||
opts.angled = matches!(hole, HoleKind::Drilled { .. });
|
||||
opts.multiple = points.is_some();
|
||||
@@ -4499,21 +4500,21 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
opts.thread = th.clone();
|
||||
}
|
||||
if let Some(dd) = depth {
|
||||
*dep = dd.clone();
|
||||
*dep = dd.to_string();
|
||||
}
|
||||
match hole {
|
||||
HoleKind::Counterbore { cb_diameter: a, cb_depth: b } => {
|
||||
*kind = 1;
|
||||
*cb_diameter = a.clone();
|
||||
*cb_depth = b.clone();
|
||||
*cb_diameter = a.to_string();
|
||||
*cb_depth = b.to_string();
|
||||
}
|
||||
HoleKind::Countersink { cs_diameter: a, cs_angle: b } => {
|
||||
*kind = 2;
|
||||
*cs_diameter = a.clone();
|
||||
*cs_angle = b.clone();
|
||||
*cs_diameter = a.to_string();
|
||||
*cs_angle = b.to_string();
|
||||
}
|
||||
HoleKind::Drilled { tip_angle } => {
|
||||
opts.tip_angle = tip_angle.clone();
|
||||
opts.tip_angle = tip_angle.to_string();
|
||||
}
|
||||
HoleKind::Simple => {}
|
||||
}
|
||||
@@ -4531,16 +4532,16 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
None => {
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = vec![face_sel(s, *position).unwrap_or(Sel::Face { body: String::new(), index: 0, point: *position })];
|
||||
inp.items = vec![face_sel(s, position.0).unwrap_or(Sel::Face { body: String::new(), index: 0, point: position.0 })];
|
||||
}
|
||||
d.extra.insert("direction".into(), pt3(*direction));
|
||||
d.extra.insert("direction".into(), pt3(direction.0));
|
||||
}
|
||||
}
|
||||
if to.is_some() {
|
||||
if let Kind::Hole { opts, .. } = &mut d.kind {
|
||||
opts.all = false;
|
||||
}
|
||||
with_to_object(&mut d, s, *to);
|
||||
with_to_object(&mut d, s, to.map(Vec3::from));
|
||||
}
|
||||
// A hole at every point of a sketch.
|
||||
if points.as_ref().is_some_and(|sp| sp.ids.is_empty()) {
|
||||
@@ -4568,7 +4569,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
at: None,
|
||||
};
|
||||
d.extra.insert("base".into(), pt_expr(base));
|
||||
d.extra.insert("axis".into(), pt3(*axis));
|
||||
d.extra.insert("axis".into(), pt3(axis.0));
|
||||
d
|
||||
}
|
||||
FeatureKind::Sphere { center, radius, operation } => {
|
||||
@@ -4593,10 +4594,10 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
let mut d = start("solid.boundary_fill")?;
|
||||
// The cells again, ticked where a stored cell point lies in the same cell.
|
||||
let before = s.model.state_before(f.id);
|
||||
let found = fill_cells(&before, tools);
|
||||
let found = fill_cells(&before, &solidcraft_engine::doc::plain(tools));
|
||||
let filled: Vec<String> =
|
||||
cells.iter().filter_map(|c| found.iter().min_by(|a, b| a.0.dist(*c).total_cmp(&b.0.dist(*c))).map(|x| x.1.clone())).collect();
|
||||
let items: Vec<Sel> = tools.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
cells.iter().filter_map(|c| found.iter().min_by(|a, b| a.0.dist(c.0).total_cmp(&b.0.dist(c.0))).map(|x| x.1.clone())).collect();
|
||||
let items: Vec<Sel> = tools.iter().map(|n| Sel::Body { name: n.to_string() }).collect();
|
||||
d.kind = Kind::BoundaryFill {
|
||||
operation: op_index(operation),
|
||||
cells: found.into_iter().map(|(p, l)| (p, l.clone(), filled.contains(&l))).collect(),
|
||||
@@ -4614,10 +4615,10 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
let mut d = start("solid.combine")?;
|
||||
d.kind = Kind::Combine { operation: op_index(operation).max(1), keep_tools: *keep_tools };
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
inp.items = vec![Sel::Body { name: target.clone() }];
|
||||
inp.items = vec![Sel::Body { name: target.to_string() }];
|
||||
}
|
||||
if let Some(inp) = d.inputs.get_mut(1) {
|
||||
inp.items = tools.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
inp.items = tools.iter().map(|n| Sel::Body { name: n.to_string() }).collect();
|
||||
}
|
||||
d
|
||||
}
|
||||
@@ -4628,7 +4629,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
let as_features = ids.iter().any(|id| !st.bodies.iter().any(|b| b.feature == *id));
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
if !bodies.is_empty() {
|
||||
inp.items = bodies.iter().map(|name| Sel::Body { name: name.clone() }).collect();
|
||||
inp.items = bodies.iter().map(|name| Sel::Body { name: name.to_string() }).collect();
|
||||
} else if as_features {
|
||||
inp.accept = selection::FEATURES;
|
||||
inp.label = objects_label(inp.accept);
|
||||
@@ -4647,28 +4648,28 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
let bodies: Vec<Sel> = if copied.is_empty() {
|
||||
st.bodies.iter().filter(|b| ids.contains(&b.feature)).map(|b| Sel::Body { name: b.name.clone() }).collect()
|
||||
} else {
|
||||
copied.iter().map(|name| Sel::Body { name: name.clone() }).collect()
|
||||
copied.iter().map(|name| Sel::Body { name: name.to_string() }).collect()
|
||||
};
|
||||
let mut d = match pattern {
|
||||
PatternKind::Rectangular { dir1, count1, spacing1, dir2, count2, spacing2 } => {
|
||||
let mut d = start("solid.pattern.rectangular")?;
|
||||
d.kind = Kind::PatternRect {
|
||||
count: count1.clone(),
|
||||
spacing: spacing1.clone(),
|
||||
count2: count2.clone().unwrap_or_else(|| "1".into()),
|
||||
spacing2: spacing2.clone().unwrap_or_else(|| "20 mm".into()),
|
||||
count: count1.to_string(),
|
||||
spacing: spacing1.to_string(),
|
||||
count2: count2.as_deref().map(str::to_string).unwrap_or_else(|| "1".into()),
|
||||
spacing2: spacing2.as_deref().map(str::to_string).unwrap_or_else(|| "20 mm".into()),
|
||||
};
|
||||
// Directions along the origin axes show as those axes; others are kept as is.
|
||||
for (i, (key, dir)) in [("dir1", Some(*dir1)), ("dir2", *dir2)].into_iter().enumerate() {
|
||||
let Some(dir) = dir else { continue };
|
||||
match world_axis(dir) {
|
||||
match world_axis(dir.0) {
|
||||
Some(name) => {
|
||||
if let Some(inp) = d.inputs.get_mut(i + 1) {
|
||||
inp.items = vec![Sel::Axis { name }];
|
||||
}
|
||||
}
|
||||
None => {
|
||||
d.extra.insert(key.into(), pt3(dir));
|
||||
d.extra.insert(key.into(), pt3(dir.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4676,15 +4677,15 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
PatternKind::Circular { origin, axis, count, angle } => {
|
||||
let mut d = start("solid.pattern.circular")?;
|
||||
d.kind = Kind::PatternCirc { count: count.clone(), angle: angle.clone() };
|
||||
match world_axis(*axis).filter(|_| origin.len() < 1e-9) {
|
||||
d.kind = Kind::PatternCirc { count: count.to_string(), angle: angle.to_string() };
|
||||
match world_axis(axis.0).filter(|_| origin.len() < 1e-9) {
|
||||
Some(name) => {
|
||||
if let Some(inp) = d.inputs.get_mut(1) {
|
||||
inp.items = vec![Sel::Axis { name }];
|
||||
}
|
||||
}
|
||||
None => {
|
||||
d.extra.insert("axis".into(), json!({"origin": pt3(*origin), "dir": pt3(*axis)}));
|
||||
d.extra.insert("axis".into(), json!({"origin": pt3(origin.0), "dir": pt3(axis.0)}));
|
||||
}
|
||||
}
|
||||
d
|
||||
@@ -4712,8 +4713,8 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
inp.items = sections
|
||||
.iter()
|
||||
.flat_map(|sec| {
|
||||
let idx = st.sketch(sec.sketch).map(|ss| profile_indices(ss, &sec.profiles)).unwrap_or_default();
|
||||
idx.into_iter().map(move |index| Sel::Profile { sketch: sec.sketch, index })
|
||||
let idx = st.sketch(sec.sketch.0).map(|ss| profile_indices(ss, &sec.profiles)).unwrap_or_default();
|
||||
idx.into_iter().map(move |index| Sel::Profile { sketch: sec.sketch.0, index })
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
@@ -4729,9 +4730,9 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
if let Some(inp) = d.inputs.get_mut(2) {
|
||||
inp.items = direction.into_iter().collect();
|
||||
}
|
||||
let items = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
let items = st.sketch(sketch.0).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: *sketch, index }).collect();
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: sketch.0, index }).collect();
|
||||
}
|
||||
if let Some(inp) = d.inputs.get_mut(1) {
|
||||
inp.items = path.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect();
|
||||
@@ -4743,7 +4744,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
FeatureKind::ConstructionPlane { plane: PlaneRef::Offset { base, distance } } => {
|
||||
let mut d = start("construct.plane.offset")?;
|
||||
d.kind = Kind::OffsetPlane { offset: distance.clone() };
|
||||
d.kind = Kind::OffsetPlane { offset: distance.to_string() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = plane_sel(s, base).into_iter().collect();
|
||||
}
|
||||
@@ -4760,7 +4761,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
d.kind = Kind::PathPlane { t: t.clone(), sketch };
|
||||
d.kind = Kind::PathPlane { t: t.to_string(), sketch };
|
||||
if let Some(input) = d.inputs.first_mut() {
|
||||
input.items = vec![item];
|
||||
}
|
||||
@@ -4768,7 +4769,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
FeatureKind::ConstructionPlane { plane: PlaneRef::AtAngle { base, axis_origin, axis_dir, angle } } => {
|
||||
let mut d = start("construct.plane.at_angle")?;
|
||||
d.kind = Kind::AnglePlane { angle: angle.clone() };
|
||||
d.kind = Kind::AnglePlane { angle: angle.to_string() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = plane_sel(s, base).into_iter().collect();
|
||||
}
|
||||
@@ -4787,11 +4788,11 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
FeatureKind::Split { body, plane, tool } => {
|
||||
let mut d = start("solid.split_body")?;
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = vec![Sel::Body { name: body.clone() }];
|
||||
inp.items = vec![Sel::Body { name: body.to_string() }];
|
||||
}
|
||||
if let Some(inp) = d.inputs.get_mut(1) {
|
||||
inp.items = match tool {
|
||||
Some(t) => face_sel(s, t.point).into_iter().collect(),
|
||||
Some(t) => face_sel(s, t.point.0).into_iter().collect(),
|
||||
None => plane_sel(s, plane).into_iter().collect(),
|
||||
};
|
||||
}
|
||||
@@ -4799,11 +4800,11 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
FeatureKind::Scale { bodies, origin, factor, factors } => {
|
||||
let mut d = start("solid.scale")?;
|
||||
d.kind = Kind::Scale { factor: factor.clone() };
|
||||
d.kind = Kind::Scale { factor: factor.to_string() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.to_string() }).collect();
|
||||
}
|
||||
d.extra.insert("origin".into(), pt3(*origin));
|
||||
d.extra.insert("origin".into(), pt3(origin.0));
|
||||
if let Some(f) = factors {
|
||||
d.extra.insert("factors".into(), json!(f));
|
||||
}
|
||||
@@ -4811,9 +4812,9 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
FeatureKind::OffsetFace { faces, distance, body } => {
|
||||
let mut d = start("solid.offset_face")?;
|
||||
d.kind = Kind::OffsetFaces { distance: distance.clone() };
|
||||
d.kind = Kind::OffsetFaces { distance: distance.to_string() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, p.0)).collect();
|
||||
}
|
||||
if let Some(b) = body {
|
||||
d.extra.insert("body".into(), json!(b));
|
||||
@@ -4825,23 +4826,26 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
if *modeled {
|
||||
d.extra.insert("modeled".into(), json!(true));
|
||||
}
|
||||
d.kind = Kind::Thread { designation: designation.clone().unwrap_or_default(), length: length.clone().unwrap_or_default() };
|
||||
d.kind = Kind::Thread {
|
||||
designation: designation.clone().unwrap_or_default(),
|
||||
length: length.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
};
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = face_sel(s, *face).into_iter().collect();
|
||||
inp.items = face_sel(s, face.0).into_iter().collect();
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::BoundingSolid { bodies, margin } => {
|
||||
let mut d = start("solid.bounding_solid")?;
|
||||
d.kind = Kind::Stock { margin: margin.clone() };
|
||||
d.kind = Kind::Stock { margin: margin.to_string() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.to_string() }).collect();
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Pipe { path, diameter, wall, operation, targets, .. } => {
|
||||
let mut d = start("solid.pipe")?;
|
||||
d.kind = Kind::Pipe { diameter: diameter.clone(), wall: wall.clone().unwrap_or_default() };
|
||||
d.kind = Kind::Pipe { diameter: diameter.to_string(), wall: wall.as_deref().map(str::to_string).unwrap_or_default() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = path.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect();
|
||||
}
|
||||
@@ -4853,10 +4857,10 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
FeatureKind::Emboss { sketch, profiles, depth, deboss, targets, face } => {
|
||||
let mut d = start("solid.emboss")?;
|
||||
d.kind = Kind::Emboss { depth: depth.clone(), deboss: *deboss };
|
||||
let items = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
d.kind = Kind::Emboss { depth: depth.to_string(), deboss: *deboss };
|
||||
let items = st.sketch(sketch.0).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: *sketch, index }).collect();
|
||||
inp.items = items.into_iter().map(|index| Sel::Profile { sketch: sketch.0, index }).collect();
|
||||
}
|
||||
if !targets.is_empty() {
|
||||
d.extra.insert("targets".into(), json!(targets));
|
||||
@@ -4870,7 +4874,12 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
FeatureKind::Rib { curves, thickness, depth, flip, web, .. } => {
|
||||
let mut d = start(if *web { "solid.web" } else { "solid.rib" })?;
|
||||
d.kind = Kind::Rib { web: *web, thickness: thickness.clone(), depth: depth.clone().unwrap_or_default(), flip: *flip };
|
||||
d.kind = Kind::Rib {
|
||||
web: *web,
|
||||
thickness: thickness.to_string(),
|
||||
depth: depth.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
flip: *flip,
|
||||
};
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = curves.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect();
|
||||
}
|
||||
@@ -4879,7 +4888,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
FeatureKind::ReplaceFace { faces, target, body } => {
|
||||
let mut d = start("solid.replace_face")?;
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, p.0)).collect();
|
||||
}
|
||||
if let Some(inp) = d.inputs.get_mut(1) {
|
||||
inp.items = plane_sel(s, target).into_iter().collect();
|
||||
@@ -4892,13 +4901,20 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
FeatureKind::Remove { bodies } => {
|
||||
let mut d = start("solid.remove")?;
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.to_string() }).collect();
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Move { bodies, translate, rotate_axis, angle, copy } => {
|
||||
let mut d = start("solid.move")?;
|
||||
crate::dialogs_move::for_feature(&mut d, bodies, translate, *rotate_axis, angle.as_ref(), *copy);
|
||||
crate::dialogs_move::for_feature(
|
||||
&mut d,
|
||||
&solidcraft_engine::doc::plain(bodies),
|
||||
translate,
|
||||
rotate_axis.map(Vec3::from),
|
||||
angle.as_ref(),
|
||||
*copy,
|
||||
);
|
||||
d
|
||||
}
|
||||
other => {
|
||||
|
||||
@@ -288,8 +288,15 @@ pub fn drag(app: &SolveApp, d: &mut Dialog, g: Drag) {
|
||||
|
||||
/// The dialog for editing a move: Free Move with its translation and, about a world axis
|
||||
/// through the origin, its angle (another axis is kept as it is).
|
||||
pub fn for_feature(d: &mut Dialog, bodies: &[String], translate: &[String; 3], axis: Option<Vec3>, angle: Option<&String>, copy: bool) {
|
||||
let [x, y, z] = translate.clone();
|
||||
pub fn for_feature(
|
||||
d: &mut Dialog,
|
||||
bodies: &[String],
|
||||
translate: &[solidcraft_engine::doc::Expr; 3],
|
||||
axis: Option<Vec3>,
|
||||
angle: Option<&solidcraft_engine::doc::Expr>,
|
||||
copy: bool,
|
||||
) {
|
||||
let [x, y, z] = translate.clone().map(String::from);
|
||||
let mut k = kind(FREE);
|
||||
if let Kind::Move { x: kx, y: ky, z: kz, rx, ry, rz, .. } = &mut k {
|
||||
(*kx, *ky, *kz) = (x, y, z);
|
||||
@@ -297,9 +304,9 @@ pub fn for_feature(d: &mut Dialog, bodies: &[String], translate: &[String; 3], a
|
||||
let dir = axis.unwrap_or(Vec3::Z);
|
||||
let world = [Vec3::X, Vec3::Y, Vec3::Z].iter().position(|w| dir.cross(*w).len() < 1e-9 && dir.dot(*w) > 0.0);
|
||||
match world {
|
||||
Some(0) => *rx = a.clone(),
|
||||
Some(1) => *ry = a.clone(),
|
||||
Some(_) => *rz = a.clone(),
|
||||
Some(0) => *rx = a.to_string(),
|
||||
Some(1) => *ry = a.to_string(),
|
||||
Some(_) => *rz = a.to_string(),
|
||||
None => {
|
||||
d.extra.insert("axis".into(), json!([dir.x, dir.y, dir.z]));
|
||||
d.extra.insert("angle".into(), json!(a));
|
||||
|
||||
@@ -410,8 +410,8 @@ pub fn commands(app: &SolveApp, k: &Pl, inputs: &[SelInput], extra: &Map<String,
|
||||
Ok(vec![(cmd.to_string(), p)])
|
||||
}
|
||||
|
||||
fn s(o: &Option<String>) -> String {
|
||||
o.clone().unwrap_or_default()
|
||||
fn s(o: &Option<solidcraft_engine::doc::Expr>) -> String {
|
||||
o.as_deref().map(str::to_string).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The dialog editing a plastic feature, filled from it.
|
||||
@@ -434,7 +434,7 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
rib_offset,
|
||||
body,
|
||||
} => {
|
||||
extra.insert("direction".into(), pt(*direction));
|
||||
extra.insert("direction".into(), pt(direction.0));
|
||||
if let Some(b) = body {
|
||||
extra.insert("body".into(), json!(b));
|
||||
}
|
||||
@@ -452,63 +452,63 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
rib_offset: ro,
|
||||
} = &mut b
|
||||
{
|
||||
*d = diameter.clone();
|
||||
*h = height.clone();
|
||||
*d = diameter.to_string();
|
||||
*h = height.to_string();
|
||||
*hd = s(hole_diameter);
|
||||
*hp = s(hole_depth);
|
||||
*dr = s(draft);
|
||||
*f = s(fillet);
|
||||
*r = ribs.clone().unwrap_or_else(|| "0".into());
|
||||
*r = ribs.as_deref().map(str::to_string).unwrap_or_else(|| "0".into());
|
||||
for (slot, v) in [(rt, rib_thickness), (rl, rib_length), (ro, rib_offset)] {
|
||||
if let Some(v) = v {
|
||||
*slot = v.clone();
|
||||
*slot = v.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
(b, "plastic.boss", *position)
|
||||
(b, "plastic.boss", position.0)
|
||||
}
|
||||
FeatureKind::Lip { face, width, height, groove, gap, outside, body } => {
|
||||
if let Some(b) = body {
|
||||
extra.insert("body".into(), json!(b));
|
||||
}
|
||||
(Pl::Lip { groove: *groove, width: width.clone(), height: height.clone(), gap: s(gap), outside: *outside }, "plastic.lip", *face)
|
||||
(Pl::Lip { groove: *groove, width: width.to_string(), height: height.to_string(), gap: s(gap), outside: *outside }, "plastic.lip", face.0)
|
||||
}
|
||||
FeatureKind::SnapFit { position, direction, hook, length, thickness, width, catch_depth, catch_length, body } => {
|
||||
extra.insert("direction".into(), pt(*direction));
|
||||
extra.insert("direction".into(), pt(direction.0));
|
||||
if let Some(b) = body {
|
||||
extra.insert("body".into(), json!(b));
|
||||
}
|
||||
let h = hook_dirs(*direction).iter().position(|(_, d)| d.dist(*hook) < 1e-6);
|
||||
let h = hook_dirs(direction.0).iter().position(|(_, d)| d.dist(hook.0) < 1e-6);
|
||||
if h.is_none() {
|
||||
extra.insert("hook".into(), pt(*hook));
|
||||
extra.insert("hook".into(), pt(hook.0));
|
||||
}
|
||||
let f = Pl::SnapFit {
|
||||
hook: h.unwrap_or(0),
|
||||
length: length.clone(),
|
||||
thickness: thickness.clone(),
|
||||
width: width.clone(),
|
||||
catch_depth: catch_depth.clone(),
|
||||
catch_length: catch_length.clone(),
|
||||
length: length.to_string(),
|
||||
thickness: thickness.to_string(),
|
||||
width: width.to_string(),
|
||||
catch_depth: catch_depth.to_string(),
|
||||
catch_length: catch_length.to_string(),
|
||||
};
|
||||
(f, "plastic.snap_fit", *position)
|
||||
(f, "plastic.snap_fit", position.0)
|
||||
}
|
||||
FeatureKind::Rest { position, direction, along, width, length, height, draft, thickness, body } => {
|
||||
extra.insert("direction".into(), pt(*direction));
|
||||
extra.insert("direction".into(), pt(direction.0));
|
||||
if let Some(a) = along {
|
||||
extra.insert("along".into(), pt(*a));
|
||||
extra.insert("along".into(), pt(a.0));
|
||||
}
|
||||
if let Some(b) = body {
|
||||
extra.insert("body".into(), json!(b));
|
||||
}
|
||||
let r = Pl::Rest {
|
||||
round: length.is_none(),
|
||||
width: width.clone(),
|
||||
length: length.clone().unwrap_or_else(|| "15 mm".into()),
|
||||
height: height.clone(),
|
||||
width: width.to_string(),
|
||||
length: length.as_deref().map(str::to_string).unwrap_or_else(|| "15 mm".into()),
|
||||
height: height.to_string(),
|
||||
draft: s(draft),
|
||||
thickness: s(thickness),
|
||||
};
|
||||
(r, "plastic.rest", *position)
|
||||
(r, "plastic.rest", position.0)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@@ -673,20 +673,20 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
let mut extra = Map::new();
|
||||
let (k, items): (Sm, Vec<Sel>) = match kind {
|
||||
FeatureKind::SheetBase { sketch, profiles, rule, flip } => {
|
||||
let ps = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
let ps = st.sketch(sketch.0).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
let mut f = new_flange(0);
|
||||
if let Sm::Flange { flip: fl, rule: r, .. } = &mut f {
|
||||
*fl = *flip;
|
||||
*r = rule_index(rule);
|
||||
}
|
||||
(f, ps.into_iter().map(|index| Sel::Profile { sketch: *sketch, index }).collect())
|
||||
(f, ps.into_iter().map(|index| Sel::Profile { sketch: sketch.0, index }).collect())
|
||||
}
|
||||
FeatureKind::SheetContour { curves, distance, rule, flip, reverse, .. } => {
|
||||
let mut f = new_flange(2);
|
||||
if let Sm::Flange { flip: fl, rule: r, distance: d, reverse: rv, .. } = &mut f {
|
||||
*fl = *flip;
|
||||
*r = rule_index(rule);
|
||||
*d = distance.clone();
|
||||
*d = distance.to_string();
|
||||
*rv = *reverse;
|
||||
}
|
||||
(f, curves.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect())
|
||||
@@ -699,17 +699,21 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
// edge's selection carries).
|
||||
let (mut picked, mut own) = (Vec::new(), Vec::new());
|
||||
for e in edges {
|
||||
let Some(sel) = edge_sel(s, e.point) else { continue };
|
||||
let Some(sel) = edge_sel(s, e.point.0) else { continue };
|
||||
if let (Sel::Edge { point, .. }, true) = (&sel, e.angle.is_some() || e.height.is_some()) {
|
||||
own.push((*point, e.angle.clone().unwrap_or_default(), e.height.clone().unwrap_or_default()));
|
||||
own.push((
|
||||
*point,
|
||||
e.angle.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
e.height.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
picked.push(sel);
|
||||
}
|
||||
let f = Sm::Flange {
|
||||
ty: 1,
|
||||
height: height.clone(),
|
||||
angle: angle.clone(),
|
||||
radius: radius.clone().unwrap_or_default(),
|
||||
height: height.to_string(),
|
||||
angle: angle.to_string(),
|
||||
radius: radius.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
position: POSITIONS.iter().position(|p| p == position).unwrap_or(0),
|
||||
flip: *flip,
|
||||
distance: "20 mm".into(),
|
||||
@@ -720,7 +724,7 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
.as_deref()
|
||||
.and_then(|name| RELIEF_SHAPES.iter().position(|s| s.eq_ignore_ascii_case(name.trim())))
|
||||
.map_or(0, |i| i + 1),
|
||||
relief_size: corner_relief.clone().unwrap_or_default(),
|
||||
relief_size: corner_relief.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
};
|
||||
(f, picked)
|
||||
}
|
||||
@@ -729,8 +733,8 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
extra.insert("body".into(), json!(b));
|
||||
}
|
||||
(
|
||||
Sm::Hem { length: length.clone(), gap: gap.clone().unwrap_or_default(), flip: *flip },
|
||||
edges.iter().filter_map(|p| edge_sel(s, *p)).collect(),
|
||||
Sm::Hem { length: length.to_string(), gap: gap.as_deref().map(str::to_string).unwrap_or_default(), flip: *flip },
|
||||
edges.iter().filter_map(|p| edge_sel(s, p.0)).collect(),
|
||||
)
|
||||
}
|
||||
FeatureKind::SheetFold { curve, a, b, angle, radius, position, flip, fixed, body, .. } => {
|
||||
@@ -738,8 +742,8 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
extra.insert("body".into(), json!(bd));
|
||||
}
|
||||
let k = Sm::Fold {
|
||||
angle: angle.clone(),
|
||||
radius: radius.clone().unwrap_or_default(),
|
||||
angle: angle.to_string(),
|
||||
radius: radius.as_deref().map(str::to_string).unwrap_or_default(),
|
||||
position: FOLD_POSITIONS.iter().position(|x| x == position || (*x == "mould" && position == "mold")).unwrap_or(0),
|
||||
flip: *flip,
|
||||
};
|
||||
@@ -752,22 +756,24 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
}
|
||||
// A fold given by two points keeps them.
|
||||
None => {
|
||||
extra.insert("points".into(), json!([pt(*a), pt(*b)]));
|
||||
extra.insert("points".into(), json!([pt(a.0), pt(b.0)]));
|
||||
}
|
||||
}
|
||||
if let Some(i) = ins.get_mut(1) {
|
||||
i.items = fixed.and_then(|f| face_sel(s, f)).into_iter().collect();
|
||||
i.items = fixed.and_then(|f| face_sel(s, f.0)).into_iter().collect();
|
||||
}
|
||||
return Some((Kind::Sheet(k), ins, extra));
|
||||
}
|
||||
FeatureKind::SheetUnfold { body, refold } => (Sm::Unfold { refold: *refold }, body.iter().map(|n| Sel::Body { name: n.clone() }).collect()),
|
||||
FeatureKind::SheetUnfold { body, refold } => {
|
||||
(Sm::Unfold { refold: *refold }, body.iter().map(|n| Sel::Body { name: n.to_string() }).collect())
|
||||
}
|
||||
FeatureKind::SheetConvert { face, rule, bends, .. } => {
|
||||
let (_, mut ins) = start(app, "sheet.convert")?;
|
||||
if let Some(i) = ins.first_mut() {
|
||||
i.items = face_sel(s, *face).into_iter().collect();
|
||||
i.items = face_sel(s, face.0).into_iter().collect();
|
||||
}
|
||||
if let Some(i) = ins.get_mut(1) {
|
||||
i.items = bends.iter().filter_map(|p| edge_sel(s, *p)).collect();
|
||||
i.items = bends.iter().filter_map(|p| edge_sel(s, p.0)).collect();
|
||||
}
|
||||
return Some((Kind::Sheet(Sm::Convert { rule: rule_index(rule) }), ins, extra));
|
||||
}
|
||||
|
||||
@@ -575,7 +575,7 @@ pub fn commands(app: &SolveApp, k: &Surf, inputs: &[SelInput], extra: &Map<Strin
|
||||
/// A tool as a selection again: a plane, else the face at the stored point.
|
||||
fn tool_sel(app: &SolveApp, plane: &PlaneRef, tool: Option<&FaceAt>) -> Option<Sel> {
|
||||
match tool {
|
||||
Some(t) => crate::dialogs::face_sel(&app.session, t.point),
|
||||
Some(t) => crate::dialogs::face_sel(&app.session, t.point.0),
|
||||
None => crate::dialogs::plane_sel(&app.session, plane),
|
||||
}
|
||||
}
|
||||
@@ -584,7 +584,7 @@ fn tool_sel(app: &SolveApp, plane: &PlaneRef, tool: Option<&FaceAt>) -> Option<S
|
||||
/// `frame` maps the feature's component frame to the world, for an axis stored as a line.
|
||||
pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(Kind, Vec<SelInput>, Map<String, Value>)> {
|
||||
let s = &app.session;
|
||||
let edge = |p: &Vec3| crate::dialogs::edge_sel(s, *p);
|
||||
let edge = |p: &solidcraft_engine::doc::Position| crate::dialogs::edge_sel(s, p.0);
|
||||
let (k, fill): (Surf, Vec<Vec<Sel>>) = match kind {
|
||||
FeatureKind::SurfaceRevolve { sketch, profiles, curves, axis, angle, angle2, symmetric } => {
|
||||
let (pick, stored) = match axis {
|
||||
@@ -594,8 +594,8 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
AxisRef::Line { origin, dir } => (
|
||||
None,
|
||||
Some(json!({
|
||||
"origin": pt(solidcraft_engine::doc::apply_point(frame, *origin)),
|
||||
"dir": pt(solidcraft_engine::doc::apply_vector(frame, *dir))
|
||||
"origin": pt(solidcraft_engine::doc::apply_point(frame, origin.0)),
|
||||
"dir": pt(solidcraft_engine::doc::apply_vector(frame, dir.0))
|
||||
})),
|
||||
),
|
||||
};
|
||||
@@ -603,9 +603,9 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
if let Some(a2) = angle2 {
|
||||
extra.insert("angle2".into(), json!(a2));
|
||||
}
|
||||
let k = Surf::Revolve { angle: angle.clone(), symmetric: *symmetric, axis: stored };
|
||||
let k = Surf::Revolve { angle: angle.to_string(), symmetric: *symmetric, axis: stored };
|
||||
let mut ins = inputs(&k);
|
||||
for (inp, items) in ins.iter_mut().zip([sheet_sels(app, *sketch, profiles.as_ref(), curves), pick.into_iter().collect()]) {
|
||||
for (inp, items) in ins.iter_mut().zip([sheet_sels(app, sketch.0, profiles.as_ref(), curves), pick.into_iter().collect()]) {
|
||||
inp.items = items;
|
||||
}
|
||||
return Some((Kind::Surface(k), ins, extra));
|
||||
@@ -615,23 +615,23 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
(
|
||||
Surf::Sweep(opts),
|
||||
vec![
|
||||
sheet_sels(app, *sketch, profiles.as_ref(), curves),
|
||||
sheet_sels(app, sketch.0, profiles.as_ref(), curves),
|
||||
path.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect(),
|
||||
direction.into_iter().collect(),
|
||||
],
|
||||
)
|
||||
}
|
||||
FeatureKind::SurfaceLoft { sections } => {
|
||||
(Surf::Loft, vec![sections.iter().flat_map(|sec| sheet_sels(app, sec.sketch, sec.profiles.as_ref(), &sec.curves)).collect()])
|
||||
(Surf::Loft, vec![sections.iter().flat_map(|sec| sheet_sels(app, sec.sketch.0, sec.profiles.as_ref(), &sec.curves)).collect()])
|
||||
}
|
||||
FeatureKind::SurfaceExtrude { sketch, profiles, curves, extent } => {
|
||||
let sels = sheet_sels(app, *sketch, profiles.as_ref(), curves);
|
||||
let sels = sheet_sels(app, sketch.0, profiles.as_ref(), curves);
|
||||
let direction = match extent.direction {
|
||||
Direction::Positive => 0,
|
||||
Direction::Symmetric => 1,
|
||||
Direction::Negative => 2,
|
||||
};
|
||||
let k = Surf::Extrude { distance: extent.distance.clone(), direction };
|
||||
let k = Surf::Extrude { distance: extent.distance.to_string(), direction };
|
||||
let mut ins = inputs(&k);
|
||||
if let Some(input) = ins.first_mut() {
|
||||
input.items = sels;
|
||||
@@ -644,14 +644,14 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
let mut sels: Vec<Sel> = edges.iter().filter_map(edge).collect();
|
||||
if let Some(sk) = sketch {
|
||||
let st = s.world_state();
|
||||
if let Some(ss) = st.sketch(*sk) {
|
||||
sels.extend((0..ss.profiles.len()).map(|index| Sel::Profile { sketch: *sk, index }));
|
||||
if let Some(ss) = st.sketch(sk.0) {
|
||||
sels.extend((0..ss.profiles.len()).map(|index| Sel::Profile { sketch: sk.0, index }));
|
||||
}
|
||||
}
|
||||
let index = PATCH_CONTINUITY.iter().position(|(_, w)| *w == continuity.all.as_str()).unwrap_or(0);
|
||||
// What the dialog does not show is kept as it was: the crown, edges with a
|
||||
// continuity of their own, and points that are not on a face any more.
|
||||
let through: Vec<Sel> = points.iter().filter_map(|p| crate::dialogs::face_sel(s, *p)).collect();
|
||||
let through: Vec<Sel> = points.iter().filter_map(|p| crate::dialogs::face_sel(s, p.0)).collect();
|
||||
let mut extra = Map::new();
|
||||
if *crown != 0.0 {
|
||||
extra.insert("crown".into(), json!(crown));
|
||||
@@ -660,7 +660,7 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
extra.insert("continuity".into(), serde_json::to_value(continuity).unwrap_or(Value::Null));
|
||||
}
|
||||
if through.len() != points.len() {
|
||||
extra.insert("points".into(), json!(points.iter().map(|p| pt(*p)).collect::<Vec<_>>()));
|
||||
extra.insert("points".into(), json!(points.iter().map(|p| pt(p.0)).collect::<Vec<_>>()));
|
||||
}
|
||||
let k = Surf::Patch { continuity: index };
|
||||
let mut ins = inputs(&k);
|
||||
@@ -669,48 +669,49 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
}
|
||||
return Some((Kind::Surface(k), ins, extra));
|
||||
}
|
||||
FeatureKind::SurfaceOffset { faces, distance, chain, .. } => {
|
||||
(Surf::Offset { distance: distance.clone(), chain: *chain }, vec![faces.iter().filter_map(|p| crate::dialogs::face_sel(s, *p)).collect()])
|
||||
}
|
||||
FeatureKind::SurfaceOffset { faces, distance, chain, .. } => (
|
||||
Surf::Offset { distance: distance.to_string(), chain: *chain },
|
||||
vec![faces.iter().filter_map(|p| crate::dialogs::face_sel(s, p.0)).collect()],
|
||||
),
|
||||
FeatureKind::SurfaceRuled { edges, distance, mode, angle, direction, flip, corners, face, .. } => {
|
||||
let k = Surf::Ruled {
|
||||
distance: distance.clone(),
|
||||
distance: distance.to_string(),
|
||||
mode: match mode {
|
||||
RuledMode::Normal => 0,
|
||||
RuledMode::Tangent => 1,
|
||||
RuledMode::Angle => 2,
|
||||
RuledMode::Direction => 3,
|
||||
},
|
||||
angle: angle.clone().unwrap_or_else(|| "5 deg".into()),
|
||||
angle: angle.as_deref().map(str::to_string).unwrap_or_else(|| "5 deg".into()),
|
||||
flip: *flip,
|
||||
round: *corners == RuledCorners::Round,
|
||||
};
|
||||
let mut ins = inputs(&k);
|
||||
let fill = [edges.iter().filter_map(edge).collect(), face.and_then(|p| crate::dialogs::face_sel(s, p)).into_iter().collect()];
|
||||
let fill = [edges.iter().filter_map(edge).collect(), face.and_then(|p| crate::dialogs::face_sel(s, p.0)).into_iter().collect()];
|
||||
for (inp, items) in ins.iter_mut().zip(fill) {
|
||||
inp.items = items;
|
||||
}
|
||||
let mut extra = Map::new();
|
||||
if let Some(d) = direction {
|
||||
extra.insert("direction".into(), pt(*d));
|
||||
extra.insert("direction".into(), pt(d.0));
|
||||
}
|
||||
return Some((Kind::Surface(k), ins, extra));
|
||||
}
|
||||
FeatureKind::Unstitch { body, faces } | FeatureKind::ReverseNormal { body, faces } => {
|
||||
let sels: Vec<Sel> = if faces.is_empty() {
|
||||
vec![Sel::Body { name: body.clone() }]
|
||||
vec![Sel::Body { name: body.to_string() }]
|
||||
} else {
|
||||
faces.iter().filter_map(|p| crate::dialogs::face_sel(s, *p)).collect()
|
||||
faces.iter().filter_map(|p| crate::dialogs::face_sel(s, p.0)).collect()
|
||||
};
|
||||
(if matches!(kind, FeatureKind::Unstitch { .. }) { Surf::Unstitch } else { Surf::ReverseNormal }, vec![sels])
|
||||
}
|
||||
FeatureKind::Stitch { bodies, tolerance } => (
|
||||
Surf::Stitch { tolerance: if tolerance.trim() == "0" { String::new() } else { tolerance.clone() } },
|
||||
vec![bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect()],
|
||||
Surf::Stitch { tolerance: if tolerance.trim() == "0" { String::new() } else { tolerance.to_string() } },
|
||||
vec![bodies.iter().map(|n| Sel::Body { name: n.to_string() }).collect()],
|
||||
),
|
||||
FeatureKind::Thicken { bodies, thickness, symmetric, operation, .. } => (
|
||||
Surf::Thicken { thickness: thickness.clone(), symmetric: *symmetric, operation: op_index(operation) },
|
||||
vec![bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect()],
|
||||
Surf::Thicken { thickness: thickness.to_string(), symmetric: *symmetric, operation: op_index(operation) },
|
||||
vec![bodies.iter().map(|n| Sel::Body { name: n.to_string() }).collect()],
|
||||
),
|
||||
FeatureKind::SurfaceExtend { edges, distance, mode, up_to, neighbours, .. } => {
|
||||
// The dialog edits the edges and the distance; the rest is kept as it was.
|
||||
@@ -726,14 +727,17 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
extra.insert("up_to".into(), json!("bounds"));
|
||||
}
|
||||
Some(solidcraft_engine::doc::ExtendTo::Face(t)) => {
|
||||
extra.insert("up_to".into(), json!({"body": t.body, "point": pt(t.point)}));
|
||||
extra.insert("up_to".into(), json!({"body": t.body, "point": pt(t.point.0)}));
|
||||
}
|
||||
Some(solidcraft_engine::doc::ExtendTo::Plane(PlaneRef::Origin { name } | PlaneRef::Construction { name })) => {
|
||||
Some(solidcraft_engine::doc::ExtendTo::Plane(PlaneRef::Origin { name })) => {
|
||||
extra.insert("up_to".into(), json!(name));
|
||||
}
|
||||
Some(solidcraft_engine::doc::ExtendTo::Plane(PlaneRef::Construction { name })) => {
|
||||
extra.insert("up_to".into(), json!(name));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let k = Surf::Extend { distance: distance.clone() };
|
||||
let k = Surf::Extend { distance: distance.to_string() };
|
||||
let mut ins = inputs(&k);
|
||||
if let Some(inp) = ins.first_mut() {
|
||||
inp.items = edges.iter().filter_map(edge).collect();
|
||||
@@ -743,9 +747,9 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
FeatureKind::SurfaceTrim { body, plane, tool, keep } => (
|
||||
Surf::Trim,
|
||||
vec![
|
||||
vec![Sel::Body { name: body.clone() }],
|
||||
vec![Sel::Body { name: body.to_string() }],
|
||||
tool_sel(app, plane, tool.as_ref()).into_iter().collect(),
|
||||
crate::dialogs::face_sel(s, *keep).into_iter().collect(),
|
||||
crate::dialogs::face_sel(s, keep.0).into_iter().collect(),
|
||||
],
|
||||
),
|
||||
FeatureKind::SurfaceUntrim { face, holes, outer, in_place } => {
|
||||
@@ -756,12 +760,12 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind, frame: &Mat) -> Option<(K
|
||||
};
|
||||
(
|
||||
Surf::Untrim { holes: index, outer: *outer, in_place: *in_place },
|
||||
vec![crate::dialogs::face_sel(s, face.point).into_iter().collect(), picked],
|
||||
vec![crate::dialogs::face_sel(s, face.point.0).into_iter().collect(), picked],
|
||||
)
|
||||
}
|
||||
FeatureKind::SplitFace { faces, plane, tool, .. } => (
|
||||
Surf::SplitFace,
|
||||
vec![faces.iter().filter_map(|p| crate::dialogs::face_sel(s, *p)).collect(), tool_sel(app, plane, tool.as_ref()).into_iter().collect()],
|
||||
vec![faces.iter().filter_map(|p| crate::dialogs::face_sel(s, p.0)).collect(), tool_sel(app, plane, tool.as_ref()).into_iter().collect()],
|
||||
),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_engine::Sel;
|
||||
use solidcraft_engine::doc::{Mat, SweepOrientation};
|
||||
use solidcraft_engine::doc::{Expr, Mat, SweepOrientation};
|
||||
use solidcraft_engine::geom::Vec3;
|
||||
|
||||
use crate::SolveApp;
|
||||
@@ -40,10 +40,10 @@ impl SweepOpts {
|
||||
/// A feature's stored options as dialog values; `frame` maps the feature's component frame to
|
||||
/// the world (a stored direction is in the component's frame). A direction along an origin
|
||||
/// axis comes back as a pick of that axis.
|
||||
pub fn of(orientation: &SweepOrientation, twist: &Option<String>, scale: &Option<String>, frame: &Mat) -> (SweepOpts, Option<Sel>) {
|
||||
pub fn of(orientation: &SweepOrientation, twist: &Option<Expr>, scale: &Option<Expr>, frame: &Mat) -> (SweepOpts, Option<Sel>) {
|
||||
let mut o = SweepOpts {
|
||||
twist: twist.clone().unwrap_or_else(|| NO_TWIST.into()),
|
||||
scale: scale.clone().unwrap_or_else(|| NO_SCALE.into()),
|
||||
twist: twist.as_ref().map_or_else(|| NO_TWIST.into(), |t| t.to_string()),
|
||||
scale: scale.as_ref().map_or_else(|| NO_SCALE.into(), |k| k.to_string()),
|
||||
..SweepOpts::default()
|
||||
};
|
||||
let mut pick = None;
|
||||
@@ -52,7 +52,7 @@ impl SweepOpts {
|
||||
SweepOrientation::Parallel => o.orientation = 1,
|
||||
SweepOrientation::Direction(d) => {
|
||||
o.orientation = DIRECTION;
|
||||
let world = solidcraft_engine::doc::apply_vector(frame, *d);
|
||||
let world = solidcraft_engine::doc::apply_vector(frame, d.0);
|
||||
match world.normalized().and_then(world_axis) {
|
||||
Some(name) => pick = Some(Sel::Axis { name: name.into() }),
|
||||
None => o.direction = Some(world),
|
||||
|
||||
@@ -25,7 +25,7 @@ fn arrow(h: &Harness) -> (Vec3, Vec3) {
|
||||
|
||||
fn last_hole_direction(h: &Harness) -> Vec3 {
|
||||
match &h.app.session.doc.features.last().unwrap().kind {
|
||||
FeatureKind::Hole { direction, .. } => *direction,
|
||||
FeatureKind::Hole { direction, .. } => direction.0,
|
||||
other => panic!("the last feature is not a hole: {}", other.type_name()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ fn hovered_face_point(h: &mut Harness, at: [f64; 2]) -> Vec3 {
|
||||
fn hole(h: &Harness) -> (u64, Vec3, bool) {
|
||||
let f = h.app.session.doc.features.last().unwrap();
|
||||
match &f.kind {
|
||||
FeatureKind::Hole { position, points, .. } => (f.id, *position, points.is_some()),
|
||||
FeatureKind::Hole { position, points, .. } => (f.id, position.0, points.is_some()),
|
||||
other => panic!("the last feature is not a hole: {}", other.type_name()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,10 +156,10 @@ fn a_centimetre_design_shows_centimetres() {
|
||||
fn stored(h: &Harness, id: u64) -> Vec<String> {
|
||||
use solidcraft_engine::doc::FeatureKind;
|
||||
match h.app.session.doc.feature(id).map(|f| &f.kind) {
|
||||
Some(FeatureKind::Box { length, width, height, .. }) => vec![length.clone(), width.clone(), height.clone()],
|
||||
Some(FeatureKind::Cylinder { radius, height, .. }) => vec![radius.clone(), height.clone()],
|
||||
Some(FeatureKind::Sphere { radius, .. }) => vec![radius.clone()],
|
||||
Some(FeatureKind::Torus { major, minor, .. }) => vec![major.clone(), minor.clone()],
|
||||
Some(FeatureKind::Box { length, width, height, .. }) => vec![length.to_string(), width.to_string(), height.to_string()],
|
||||
Some(FeatureKind::Cylinder { radius, height, .. }) => vec![radius.to_string(), height.to_string()],
|
||||
Some(FeatureKind::Sphere { radius, .. }) => vec![radius.to_string()],
|
||||
Some(FeatureKind::Torus { major, minor, .. }) => vec![major.to_string(), minor.to_string()],
|
||||
other => panic!("not a primitive: {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ fn volume(h: &Harness) -> f64 {
|
||||
}
|
||||
fn angle(h: &Harness) -> String {
|
||||
let FeatureKind::Revolve { angle, .. } = &h.app.session.doc.find_feature("Turn").unwrap().kind else { panic!("revolve") };
|
||||
angle.clone()
|
||||
angle.to_string()
|
||||
}
|
||||
fn expected_volume(degrees: f64) -> f64 {
|
||||
0.5 * (225.0 - 100.0) * 5.0 * degrees.to_radians()
|
||||
|
||||
@@ -39,9 +39,9 @@ fn a_new_sweep_takes_its_options_from_the_dialog() {
|
||||
assert!(h.app.dialog.is_none(), "{:?}", h.app.session.log);
|
||||
let f = h.app.session.doc.features.last().unwrap();
|
||||
let FeatureKind::Sweep { orientation, twist, scale, .. } = &f.kind else { panic!("{:?}", f.kind) };
|
||||
assert_eq!(*orientation, SweepOrientation::Direction(Vec3::X));
|
||||
assert_eq!(twist.as_deref(), Some("90 deg"));
|
||||
assert_eq!(scale.as_deref(), Some("0.5"));
|
||||
assert_eq!(*orientation, SweepOrientation::Direction(solidcraft_engine::doc::Dir(Vec3::X)));
|
||||
assert_eq!(twist.as_ref().map(|t| t.as_str()), Some("90 deg"));
|
||||
assert_eq!(scale.as_ref().map(|k| k.as_str()), Some("0.5"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -333,7 +333,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
if let Some(sk) = sketch_of
|
||||
&& ui.button(crate::i18n::tr_label("Edit Profile Sketch")).clicked()
|
||||
{
|
||||
app.edit_sketch(sk);
|
||||
app.edit_sketch(sk.0);
|
||||
ui.close();
|
||||
}
|
||||
if ui.button(crate::i18n::tr_label("Rename")).clicked() {
|
||||
|
||||
@@ -774,7 +774,7 @@ fn vertical_features(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
if let Some(sketch) = sketch
|
||||
&& ui.button("Edit Profile Sketch").clicked()
|
||||
{
|
||||
app.edit_sketch(sketch);
|
||||
app.edit_sketch(sketch.0);
|
||||
ui.close();
|
||||
}
|
||||
if ui.button("Rename Feature").clicked() {
|
||||
|
||||
Reference in new issue
Block a user