Thanks @DuBearte.
This commit is contained in:
1 parent
e93f8c1b84
commit
4d58d3bbca
5 files changed
+48
-13
No files matched your search
@@ -141,6 +141,20 @@ fn extrude_base(s: &Session, p: &Value) -> Option<(Vec3, Vec3)> {
|
||||
Some((ss.plane.to_world(q), ss.plane.normal()))
|
||||
}
|
||||
|
||||
/// How far an extrude with these parameters goes from its profile along the profile's normal
|
||||
/// (negative: the other way). To an object, the target's height, on whichever side it is: the
|
||||
/// extrude goes there and its `direction` does not count (#1633). Otherwise its distance, turned
|
||||
/// round for the negative direction.
|
||||
pub fn extrude_reach(s: &Session, p: &Value) -> Option<f64> {
|
||||
if let Some(t) = p.get("to").and_then(vec3) {
|
||||
let (base, n) = extrude_base(s, p)?;
|
||||
return Some((t - base).dot(n)).filter(|h| h.is_finite());
|
||||
}
|
||||
let d = s.doc.eval(&expr(p, "distance")?, Kind::Length).ok()?;
|
||||
let dir = str_(p, "direction").map(str::to_ascii_lowercase);
|
||||
Some(if matches!(dir.as_deref(), Some("negative" | "reverse" | "flip")) { -d } else { d })
|
||||
}
|
||||
|
||||
/// How far past the profile [`auto_operation`] looks for material it starts in.
|
||||
const START_PROBE_MM: f64 = 0.1;
|
||||
|
||||
@@ -152,10 +166,8 @@ const START_PROBE_MM: f64 = 0.1;
|
||||
/// direction it grows, or halfway along it. With the halfway point alone, a cut dragged far
|
||||
/// past a body's far side read as a Join (#1257).
|
||||
pub fn auto_operation(s: &Session, p: &Value) -> Option<&'static str> {
|
||||
let d = s.doc.eval(&expr(p, "distance")?, Kind::Length).ok()?;
|
||||
let dir = str_(p, "direction").map(str::to_ascii_lowercase);
|
||||
let symmetric = dir.as_deref() == Some("symmetric");
|
||||
let d = if matches!(dir.as_deref(), Some("negative" | "reverse" | "flip")) { -d } else { d };
|
||||
let d = extrude_reach(s, p)?;
|
||||
let symmetric = p.get("to").is_none() && str_(p, "direction").is_some_and(|d| d.eq_ignore_ascii_case("symmetric"));
|
||||
let (base, n) = extrude_base(s, p)?;
|
||||
let st = s.model.state();
|
||||
if st.bodies.is_empty() || !d.is_finite() {
|
||||
|
||||
@@ -33,7 +33,7 @@ mod edit;
|
||||
mod face;
|
||||
mod features;
|
||||
mod features_more;
|
||||
pub use features::auto_operation;
|
||||
pub use features::{auto_operation, extrude_reach};
|
||||
mod component_link;
|
||||
mod config;
|
||||
mod file;
|
||||
|
||||
@@ -31,7 +31,7 @@ use serde_json::Value;
|
||||
use solidcraft_doc::{Document, Model, ModelState};
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
pub use cmd::{CommandInfo, CommandSpec, auto_operation, command_specs, find_command};
|
||||
pub use cmd::{CommandInfo, CommandSpec, auto_operation, command_specs, extrude_reach, find_command};
|
||||
pub use solidcraft_doc as doc;
|
||||
pub use solidcraft_doc::Visibility;
|
||||
pub use solidcraft_geom as geom;
|
||||
|
||||
@@ -1196,6 +1196,27 @@ fn extrude_auto_operation() {
|
||||
assert!(rel(volume(&mut s), 40.0 * 30.0 * 15.0 - 100.0 * 8.0) < 1e-6);
|
||||
}
|
||||
|
||||
/// #1633: to an object, the extrude goes to it on whichever side it is, whatever its
|
||||
/// `direction`: through the body to its far face is a Cut, out to something above is a Join.
|
||||
#[test]
|
||||
fn extrude_auto_operation_follows_the_to_object() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 40, "width": 30, "height": 20}));
|
||||
run(&mut s, "solid.box", json!({"length": 40, "width": 30, "height": 5, "corner": [0, 0, 50], "body_name": "Above"}));
|
||||
for direction in ["positive", "negative"] {
|
||||
// From the top face to the bottom face: through the body.
|
||||
let down = json!({"face": [20, 15, 20], "to": [10, 10, 0], "distance": 19, "direction": direction});
|
||||
assert!(extrude_reach(&s, &down).is_some_and(|h| (h + 20.0).abs() < 1e-6), "{down}");
|
||||
assert_eq!(auto_operation(&s, &down), Some("cut"), "{down}");
|
||||
// From the top face up to the box above: out of the body.
|
||||
let up = json!({"face": [20, 15, 20], "to": [10, 10, 50], "distance": 19, "direction": direction});
|
||||
assert!(extrude_reach(&s, &up).is_some_and(|h| (h - 30.0).abs() < 1e-6), "{up}");
|
||||
assert_eq!(auto_operation(&s, &up), Some("join"), "{up}");
|
||||
}
|
||||
// Without a To object the direction still counts.
|
||||
assert_eq!(extrude_reach(&s, &json!({"face": [20, 15, 20], "distance": 5, "direction": "negative"})), Some(-5.0));
|
||||
}
|
||||
|
||||
/// Additive extrusions default to joining even when only a profile side touches material.
|
||||
#[test]
|
||||
fn auto_extrude_joins_side_contact_elbow_and_keeps_new_body_explicit() {
|
||||
|
||||
@@ -2054,16 +2054,18 @@ fn auto_operation(app: &SolveApp, d: &mut Dialog) {
|
||||
}
|
||||
let Kind::Extrude { direction, distance, .. } = &d.kind else { return };
|
||||
let Ok(dist) = app.session.doc.eval(distance, solidcraft_engine::doc::expr::Kind::Length) else { return };
|
||||
let side = crate::smart_extrude::side(dist, *direction);
|
||||
let mut side = crate::smart_extrude::side(dist, *direction);
|
||||
if !matches!(d.kind, Kind::Extrude { auto_op: true, .. }) {
|
||||
return;
|
||||
}
|
||||
let Some(fresh) = dialog_commands(app, d)
|
||||
.ok()
|
||||
.and_then(|c| c.into_iter().next())
|
||||
.and_then(|(_, p)| solidcraft_engine::auto_operation(&app.session, &p))
|
||||
.and_then(|op| OPS.iter().position(|o| *o == op))
|
||||
else {
|
||||
let Some(params) = dialog_commands(app, d).ok().and_then(|c| c.into_iter().next()).map(|(_, p)| p) else { return };
|
||||
// To an object, the extrude goes to it on whichever side it is: that is the side (#1633).
|
||||
if params.get("to").is_some()
|
||||
&& let Some(reach) = solidcraft_engine::extrude_reach(&app.session, ¶ms)
|
||||
{
|
||||
side = crate::smart_extrude::side(reach, 0);
|
||||
}
|
||||
let Some(fresh) = solidcraft_engine::auto_operation(&app.session, ¶ms).and_then(|op| OPS.iter().position(|o| *o == op)) else {
|
||||
return;
|
||||
};
|
||||
if let Kind::Extrude { operation, smart, .. } = &mut d.kind {
|
||||
|
||||
Reference in new issue
Block a user