diff --git a/crates/doc/src/eval_thread.rs b/crates/doc/src/eval_thread.rs index 969c4250d8..621c713b93 100644 --- a/crates/doc/src/eval_thread.rs +++ b/crates/doc/src/eval_thread.rs @@ -339,7 +339,10 @@ fn thread_grooves(t: &ThreadInfo, radius: f64, face: (f64, f64), ends: [End; 2], return Err(DocError::Invalid("thread too short".into())); } if len / lead > 190.0 { - return Err(DocError::Invalid(format!("a modelled thread is limited to 190 turns ({:.0} mm at this lead); use a cosmetic one", 190.0 * lead))); + return Err(DocError::Invalid(format!( + "a modelled thread is limited to 190 turns ({:.0} mm at this lead); use a cosmetic one", + 190.0 * lead + ))); } let mut g = form.groove(d, p, t.internal); if steep == SteepFlank::Start { @@ -453,7 +456,15 @@ fn thread_grooves(t: &ThreadInfo, radius: f64, face: (f64, f64), ends: [End; 2], // A left-handed groove: turning the same way about the reversed axis while rising // along it (the screw motion about −k rising −lead per turn). let (axis, pitch) = if h > 0.0 { (k, lead) } else { (-k, -lead) }; - grooves.push(GrooveShape { plane: Plane { origin: base, x, y: k }, section: section.clone(), base, axis, pitch, turns, law: law.clone() }); + grooves.push(GrooveShape { + plane: Plane { origin: base, x, y: k }, + section: section.clone(), + base, + axis, + pitch, + turns, + law: law.clone(), + }); } } Ok(grooves) diff --git a/crates/doc/src/thread_forms.rs b/crates/doc/src/thread_forms.rs index 2ca79cbc87..d2099d2dd8 100644 --- a/crates/doc/src/thread_forms.rs +++ b/crates/doc/src/thread_forms.rs @@ -596,10 +596,11 @@ mod tests { for f in [ThreadForm::Metric, ThreadForm::Trapezoidal, ThreadForm::Acme] { let (d, p) = (20.0, 2.5); let g = f.groove(d, p, false); - let r = d / 2.0 - match f { - ThreadForm::Metric => 3.0 * p * 3f64.sqrt() / 16.0, - _ => p / 4.0, - }; + let r = d / 2.0 + - match f { + ThreadForm::Metric => 3.0 * p * 3f64.sqrt() / 16.0, + _ => p / 4.0, + }; assert!((g.width(p, r) - p / 2.0).abs() < 1e-9, "{f:?}"); } } diff --git a/crates/engine/src/thread_tests.rs b/crates/engine/src/thread_tests.rs index bae96c7fd4..49919d921c 100644 --- a/crates/engine/src/thread_tests.rs +++ b/crates/engine/src/thread_tests.rs @@ -805,9 +805,25 @@ fn every_form_cuts_its_standard_profile() { let t7 = 7f64.to_radians().tan() + 1.0; let cases = [ ("trapezoidal", "Tr20x4", 20.0, 4.0, 2.0 * (1.0 - 15f64.to_radians().tan()), (15.0, 15.0), 10.0 - 2.0 - tr_ac), - ("acme", "3/4-6 ACME", 0.75 * inch, inch / 6.0, inch / 12.0 * (1.0 - 14.5f64.to_radians().tan()), (14.5, 14.5), 0.375 * inch - inch / 12.0 - acme_a / 2.0), + ( + "acme", + "3/4-6 ACME", + 0.75 * inch, + inch / 6.0, + inch / 12.0 * (1.0 - 14.5f64.to_radians().tan()), + (14.5, 14.5), + 0.375 * inch - inch / 12.0 - acme_a / 2.0, + ), ("buttress", "S20x4", 20.0, 4.0, (4.0 - 3.0 * t3) / 2.0, (3.0, 30.0), 10.0 - 0.86777 * 4.0), - ("buttress_asme", "1.5-8 BUTT", 1.5 * inch, inch / 8.0, (inch / 8.0 - 0.6 * inch / 8.0 * t7) / 2.0, (7.0, 45.0), 0.75 * inch - 0.6 * inch / 8.0), + ( + "buttress_asme", + "1.5-8 BUTT", + 1.5 * inch, + inch / 8.0, + (inch / 8.0 - 0.6 * inch / 8.0 * t7) / 2.0, + (7.0, 45.0), + 0.75 * inch - 0.6 * inch / 8.0, + ), ]; for (form, designation, d, p, flat, (a, b), r3) in cases { let mut volumes = Vec::new(); @@ -816,7 +832,11 @@ fn every_form_cuts_its_standard_profile() { let len = 6.0 * p; run(&mut s, "solid.cylinder", json!({"radius": d / 2.0, "height": len, "body_name": "Shaft"})); let before = exact_volume(&s, "Shaft"); - let th = run(&mut s, "solid.thread", json!({"face": [d / 2.0, 0, len / 2.0], "form": form, "designation": designation, "hand": hand, "modeled": true})); + let th = run( + &mut s, + "solid.thread", + json!({"face": [d / 2.0, 0, len / 2.0], "form": form, "designation": designation, "hand": hand, "modeled": true}), + ); assert!(th["thread"].is_null(), "{th}"); let st = s.model.state(); assert!(st.body("Shaft").unwrap().body.validity().is_empty(), "{designation} {hand}"); @@ -965,7 +985,11 @@ fn a_chamfered_tip_tapers_the_last_teeth() { assert!((got - want).abs() < want * 1e-4, "{got} vs {want}"); // 30° to the axis and 0.5 mm deep, at the one free end of a shaft on a collar. let mut s = shaft_on_collar(); - run(&mut s, "solid.thread", json!({"face": [5, 0, 25], "designation": "M10", "tip": "chamfer", "chamfer_angle": "30 deg", "chamfer_depth": 0.5, "modeled": true})); + run( + &mut s, + "solid.thread", + json!({"face": [5, 0, 25], "designation": "M10", "tip": "chamfer", "chamfer_angle": "30 deg", "chamfer_depth": 0.5, "modeled": true}), + ); let w = ext_half_width(10.0, 1.5); let rf = 4.5; let slope = 30f64.to_radians().tan(); @@ -976,7 +1000,10 @@ fn a_chamfered_tip_tapers_the_last_teeth() { let mut s = Session::default(); run(&mut s, "solid.box", json!({"corner": [-10, -10, 0], "length": 20, "width": 20, "height": 10, "body_name": "Nut"})); run(&mut s, "solid.hole", json!({"position": [0, 0, 10], "diameter": 8.5})); - let e = s.execute("solid.thread", &json!({"face": [4.25, 0, 5], "designation": "M10", "tip": "chamfer", "modeled": true})).err().map(|e| e.to_string()); + let e = s + .execute("solid.thread", &json!({"face": [4.25, 0, 5], "designation": "M10", "tip": "chamfer", "modeled": true})) + .err() + .map(|e| e.to_string()); assert!(e.unwrap_or_default().contains("countersink")); } @@ -1022,7 +1049,11 @@ fn a_runout_fades_the_groove_out_before_each_shoulder() { let mut s = shaft_on_collar(); run(&mut s, "solid.cylinder", json!({"base": [0, 0, 35], "radius": 8, "height": 10, "operation": "join", "targets": ["Part"]})); let before = exact_volume(&s, "Part"); - run(&mut s, "solid.thread", json!({"face": [5, 0, 20], "designation": "M10", "ease_in": "standard", "ease_out": "short", "from": [0, 0, 10], "modeled": true})); + run( + &mut s, + "solid.thread", + json!({"face": [5, 0, 20], "designation": "M10", "ease_in": "standard", "ease_out": "short", "from": [0, 0, 10], "modeled": true}), + ); let st = s.model.state(); assert!(st.body("Part").unwrap().body.validity().is_empty()); let (p, w) = (1.5, ext_half_width(10.0, 1.5)); diff --git a/crates/kernel/src/helix.rs b/crates/kernel/src/helix.rs index 7224f10a8b..ce8381a593 100644 --- a/crates/kernel/src/helix.rs +++ b/crates/kernel/src/helix.rs @@ -23,12 +23,22 @@ pub fn sweep_helix(plane: &Plane, region: &Region2, axis_origin: Vec3, axis: Vec /// `offset(t)` mm away from the axis (negative: towards it) in its half-plane, `offset` piecewise /// linear through `law`'s (turn, offset) pairs, turns ascending within [0, `turns`]. A thread's /// groove fading in or out over a runout: SMLib's radial screw sweep. -pub fn sweep_helix_radial(plane: &Plane, region: &Region2, axis_origin: Vec3, axis: Vec3, pitch: f64, turns: f64, law: &[(f64, f64)]) -> Result { +pub fn sweep_helix_radial( + plane: &Plane, + region: &Region2, + axis_origin: Vec3, + axis: Vec3, + pitch: f64, + turns: f64, + law: &[(f64, f64)], +) -> Result { if law.is_empty() { return sweep_helix(plane, region, axis_origin, axis, pitch, turns); } let (k, profile) = helix_profile(plane, region, axis_origin, axis, pitch, turns)?; - if !law.windows(2).all(|w| matches!(w, [a, b] if b.0 > a.0)) || law.iter().any(|(t, o)| !(t.is_finite() && o.is_finite() && *t >= 0.0 && *t <= turns)) { + if !law.windows(2).all(|w| matches!(w, [a, b] if b.0 > a.0)) + || law.iter().any(|(t, o)| !(t.is_finite() && o.is_finite() && *t >= 0.0 && *t <= turns)) + { return Err(KernelError::Invalid("helix: the radial law's turns must ascend within the sweep's turns".into())); } crate::smlib_sweeps::screw_radial(plane, &crate::smlib_sweeps::pieces([&profile]), axis_origin, k, pitch, turns, law) @@ -36,7 +46,14 @@ pub fn sweep_helix_radial(plane: &Plane, region: &Region2, axis_origin: Vec3, ax /// A helical sweep's axis and profile (the region's outer loop), checked: finite turns and /// pitch, an area, and a profile that clears itself after one turn. -fn helix_profile(plane: &Plane, region: &Region2, axis_origin: Vec3, axis: Vec3, pitch: f64, turns: f64) -> Result<(Vec3, crate::numbered::NumberedLoop)> { +fn helix_profile( + plane: &Plane, + region: &Region2, + axis_origin: Vec3, + axis: Vec3, + pitch: f64, + turns: f64, +) -> Result<(Vec3, crate::numbered::NumberedLoop)> { let k = axis.normalized().ok_or_else(|| KernelError::Invalid("helix axis".into()))?; if !(pitch.is_finite() && turns.is_finite() && turns > 0.0 && turns <= 200.0 && pitch.abs() < 1e7 && axis_origin.is_finite()) { return Err(KernelError::Invalid("helix: turns must be in (0, 200] and the pitch finite".into())); diff --git a/crates/kernel/src/smlib_sweeps.rs b/crates/kernel/src/smlib_sweeps.rs index 079b688590..bc26f505ba 100644 --- a/crates/kernel/src/smlib_sweeps.rs +++ b/crates/kernel/src/smlib_sweeps.rs @@ -71,8 +71,8 @@ pub(crate) struct Draft { } pub(crate) use imp::{ - body_from_record, curve, extrude, face_section, loft, loft_rails, loft_to_point, path_curve, project_onto_face, revolve, screw, screw_radial, sphere, sweep, - sweep_controlled, + body_from_record, curve, extrude, face_section, loft, loft_rails, loft_to_point, path_curve, project_onto_face, revolve, screw, screw_radial, + sphere, sweep, sweep_controlled, }; /// Refuses what no SMLib sweep does, before any work. @@ -1267,7 +1267,15 @@ mod imp { /// [`screw`] with the profile moved `offset(t)` mm off the axis at turn t (negative: towards /// it), piecewise linear through `law`'s (turn, offset) pairs: a groove fading over a runout. - pub(crate) fn screw_radial(plane: &Plane, pieces: &[Piece], origin: Vec3, axis: Vec3, pitch: f64, turns: f64, law: &[(f64, f64)]) -> Result { + pub(crate) fn screw_radial( + plane: &Plane, + pieces: &[Piece], + origin: Vec3, + axis: Vec3, + pitch: f64, + turns: f64, + law: &[(f64, f64)], + ) -> Result { let c = curves(plane, pieces)?; let tolerance = (pitch.abs() * 1e-4).clamp(1e-6, 1e-2); let brep = diff --git a/crates/kernel/src/topo.rs b/crates/kernel/src/topo.rs index ace62e9fb7..be2929ed0e 100644 --- a/crates/kernel/src/topo.rs +++ b/crates/kernel/src/topo.rs @@ -365,8 +365,7 @@ pub fn material_in_ring(b: &Body, c: &CylinderFace, r_in: f64, r_out: f64, along if !(r_in >= 0.0 && r_out > r_in && a1 > a0 && r_out.is_finite() && a0.is_finite() && a1.is_finite()) { return Err(crate::KernelError::Invalid(format!("ring r {r_in}..{r_out} over {a0}..{a1}"))); } - let plane = - Plane::from_normal(c.axis_point + c.axis * a0, *c.axis).ok_or_else(|| crate::KernelError::Invalid("ring axis".into()))?; + let plane = Plane::from_normal(c.axis_point + c.axis * a0, *c.axis).ok_or_else(|| crate::KernelError::Invalid("ring axis".into()))?; // Circles started at the primitives' odd seam angle, off the frame's axes (as `cylinder`). let seam = crate::build::SEAM_ANGLE; let holes = if r_in > 0.0 { vec![Loop2::circle_from(Vec2::ZERO, r_in, seam).reversed()] } else { Vec::new() };