M1: oracle recipe coverage (arcs, refs-based constraints/dimensions, extents), taper and through-all extrudes, curve splitting in profiles, region merging, angle dimensions by sector, smoother tangency solve, merged topology for revolves
Oracle: 13/29 parts match Fusion. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
0f3e7701a7
commit
b85ccd62cf
17 files changed
+1067
-164
No files matched your search
@@ -101,6 +101,12 @@ pub struct Extent {
|
||||
/// Start offset from the sketch plane (expression).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub start_offset: Option<String>,
|
||||
/// Through all bodies in the direction(s) instead of `distance`.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub through_all: bool,
|
||||
/// Taper angle (expression; positive grows the profile).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub taper: Option<String>,
|
||||
}
|
||||
|
||||
/// Revolve axis.
|
||||
@@ -113,6 +119,8 @@ pub enum AxisRef {
|
||||
SketchAxis { axis: String },
|
||||
/// World X / Y / Z axis through the origin.
|
||||
World { axis: String },
|
||||
/// Any line in the sketch plane, in world coordinates.
|
||||
Line { origin: Vec3, dir: Vec3 },
|
||||
}
|
||||
|
||||
/// Feature definitions.
|
||||
@@ -260,6 +268,9 @@ impl FeatureKind {
|
||||
if let Some(d) = &extent.start_offset {
|
||||
v.push(d);
|
||||
}
|
||||
if let Some(d) = &extent.taper {
|
||||
v.push(d);
|
||||
}
|
||||
}
|
||||
FeatureKind::Revolve { angle, .. } => v.push(angle),
|
||||
FeatureKind::Fillet { radius, .. } => v.push(radius),
|
||||
|
||||
+47
-3
@@ -328,6 +328,31 @@ fn apply_op(state: &mut ModelState, f: &Feature, tools: Vec<Body>, op: Operation
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Distance that reaches through every target body from the sketch plane (either side).
|
||||
fn through_all_distance(st: &ModelState, plane: &Plane, targets: &[String]) -> Result<f64> {
|
||||
let mut far: f64 = 0.0;
|
||||
let mut any = false;
|
||||
for b in st.bodies.iter().filter(|b| targets.is_empty() || targets.contains(&b.name)) {
|
||||
let bb = b.mesh().bounds();
|
||||
if bb.is_empty() {
|
||||
continue;
|
||||
}
|
||||
any = true;
|
||||
for i in 0..8 {
|
||||
let p = Vec3::new(
|
||||
if i & 1 == 0 { bb.min.x } else { bb.max.x },
|
||||
if i & 2 == 0 { bb.min.y } else { bb.max.y },
|
||||
if i & 4 == 0 { bb.min.z } else { bb.max.z },
|
||||
);
|
||||
far = far.max(plane.height(p).abs());
|
||||
}
|
||||
}
|
||||
if !any {
|
||||
return Err(DocError::Invalid("through all needs a body to cut".into()));
|
||||
}
|
||||
Ok(far * 1.02 + 1.0)
|
||||
}
|
||||
|
||||
/// Sample points inside a region (sketch coordinates).
|
||||
fn region_samples(r: &Region2) -> Vec<Vec2> {
|
||||
let mut out = vec![r.interior_point()];
|
||||
@@ -428,6 +453,14 @@ fn revolve_axis(ss: &SolvedSketch, axis: &AxisRef) -> Result<(Vec2, Vec2)> {
|
||||
}
|
||||
Ok((pl.to_local(Vec3::ZERO), Vec2::new(d.dot(pl.x), d.dot(pl.y))))
|
||||
}
|
||||
AxisRef::Line { origin, dir } => {
|
||||
let pl = &ss.plane;
|
||||
let d = dir.normalized().ok_or_else(|| DocError::Invalid("revolve axis has no direction".into()))?;
|
||||
if d.dot(pl.normal()).abs() > 1e-9 || pl.height(*origin).abs() > 1e-6 {
|
||||
return Err(DocError::Invalid("the revolve axis does not lie in the sketch plane".into()));
|
||||
}
|
||||
Ok((pl.to_local(*origin), Vec2::new(d.dot(pl.x), d.dot(pl.y))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,8 +481,19 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
}
|
||||
FeatureKind::Extrude { sketch, profiles, extent, operation, targets } => {
|
||||
let ss = st.sketch(*sketch).ok_or_else(|| DocError::Unknown(format!("sketch {sketch} (it must come earlier in the timeline)")))?.clone();
|
||||
let regions = select_profiles(&ss, profiles)?;
|
||||
let d = val(vals, &extent.distance, Kind::Length)?;
|
||||
let regions = solvecraft_sketch::merge_regions(&select_profiles(&ss, profiles)?);
|
||||
let d = if extent.through_all { through_all_distance(st, &ss.plane, targets)? } else { val(vals, &extent.distance, Kind::Length)? };
|
||||
if let Some(tp) = &extent.taper {
|
||||
let taper = val(vals, tp, Kind::Angle)?;
|
||||
if extent.distance2.is_some() || extent.start_offset.is_some() || extent.direction == crate::Direction::Symmetric {
|
||||
return Err(DocError::Invalid("not supported yet: taper with two-sided, symmetric or offset extents".into()));
|
||||
}
|
||||
let sign = if extent.direction == crate::Direction::Negative { -1.0 } else { 1.0 };
|
||||
let (len, sign) = if d < 0.0 { (-d, -sign) } else { (d, sign) };
|
||||
let tools =
|
||||
regions.iter().map(|r| kernel::extrude_tapered(&ss.plane, r, len, sign, taper)).collect::<std::result::Result<Vec<_>, _>>()?;
|
||||
return apply_op(st, f, tools, *operation, targets);
|
||||
}
|
||||
let off = match &extent.start_offset {
|
||||
Some(e) => val(vals, e, Kind::Length)?,
|
||||
None => 0.0,
|
||||
@@ -471,7 +515,7 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
}
|
||||
FeatureKind::Revolve { sketch, profiles, axis, angle, operation, targets } => {
|
||||
let ss = st.sketch(*sketch).ok_or_else(|| DocError::Unknown(format!("sketch {sketch} (it must come earlier in the timeline)")))?.clone();
|
||||
let regions = select_profiles(&ss, profiles)?;
|
||||
let regions = solvecraft_sketch::merge_regions(&select_profiles(&ss, profiles)?);
|
||||
let (o, d) = revolve_axis(&ss, axis)?;
|
||||
let ang = val(vals, angle, Kind::Angle)?;
|
||||
let tools = kernel::revolve(&ss.plane, ®ions, o, d, ang)?;
|
||||
|
||||
+16
-2
@@ -31,7 +31,14 @@ fn plate_doc() -> (Document, u64) {
|
||||
FeatureKind::Extrude {
|
||||
sketch: s,
|
||||
profiles: ProfileSel::All,
|
||||
extent: Extent { distance: "20".into(), direction: Direction::Positive, distance2: None, start_offset: None },
|
||||
extent: Extent {
|
||||
distance: "20".into(),
|
||||
direction: Direction::Positive,
|
||||
distance2: None,
|
||||
start_offset: None,
|
||||
through_all: false,
|
||||
taper: None,
|
||||
},
|
||||
operation: Operation::NewBody,
|
||||
targets: vec![],
|
||||
},
|
||||
@@ -82,7 +89,14 @@ fn box_fillet_cut_timeline() {
|
||||
FeatureKind::Extrude {
|
||||
sketch: s2,
|
||||
profiles: ProfileSel::Points { points: vec![Vec2::new(20.0, 15.0)] },
|
||||
extent: Extent { distance: "20".into(), direction: Direction::Positive, distance2: None, start_offset: None },
|
||||
extent: Extent {
|
||||
distance: "20".into(),
|
||||
direction: Direction::Positive,
|
||||
distance2: None,
|
||||
start_offset: None,
|
||||
through_all: false,
|
||||
taper: None,
|
||||
},
|
||||
operation: Operation::Cut,
|
||||
targets: vec![],
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user