Isocurve Analysis (FusionIsoCurveAnalysisCommand): a grid of isoparametric curves each way across picked faces (the face cut by evenly spaced planes of each family), drawn while the tool is active
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
561df89b1d
commit
319386760f
5 files changed
+132
-2
No files matched your search
@@ -16,6 +16,7 @@ mod params;
|
||||
pub mod plastic;
|
||||
pub mod project;
|
||||
mod project3d;
|
||||
pub use project3d::iso_grid;
|
||||
pub mod sheet;
|
||||
|
||||
pub use assembly::{IDENTITY, Mat, Occurrence, apply_point, apply_vector, is_identity, mat_inverse, mat_mul, rigid};
|
||||
|
||||
@@ -351,6 +351,55 @@ pub(crate) fn iso_curve(m: &Mesh, f: u32, p: Vec3, dir: &str) -> Vec<Vec<Vec3>>
|
||||
if len(&a) >= len(&b) { a } else { b }
|
||||
}
|
||||
|
||||
/// Isocurve analysis: `count` curves each way across face `f` (picked at `p`), as world
|
||||
/// polylines. Each family is the face cut by parallel planes spaced evenly over the face.
|
||||
pub fn iso_grid(m: &Mesh, f: u32, p: Vec3, count: usize) -> Vec<Vec<Vec3>> {
|
||||
let count = count.clamp(1, 100);
|
||||
let verts: Vec<Vec3> = m.triangles.iter().zip(&m.tri_face).filter(|(_, x)| **x == f).filter_map(|(t, _)| m.tri(t)).flatten().collect();
|
||||
if verts.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let mut out = Vec::new();
|
||||
for dir in ["u", "v"] {
|
||||
// The family's cutting direction, from the curve through p.
|
||||
let Some(first) = iso_curve(m, f, p, dir).into_iter().next() else { continue };
|
||||
let pts: Vec<Vec3> = first.iter().copied().collect();
|
||||
// Normal of the plane that curve lies in.
|
||||
let Some(c) = pts.first().copied() else { continue };
|
||||
let mut n = Vec3::ZERO;
|
||||
for w in pts.windows(2) {
|
||||
n = n + (w[0] - c).cross(w[1] - c);
|
||||
}
|
||||
let cut_n = match n.normalized() {
|
||||
Some(n) => n,
|
||||
// A straight curve: across it, within the face.
|
||||
None => {
|
||||
let d = pts.last().copied().unwrap_or(c) - c;
|
||||
let np = (verts[1] - verts[0]).cross(verts[2] - verts[0]);
|
||||
match d.cross(np).normalized() {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
let (lo, hi) = verts.iter().map(|v| v.dot(cut_n)).fold((f64::INFINITY, f64::NEG_INFINITY), |(a, b), x| (a.min(x), b.max(x)));
|
||||
if !(hi - lo > 1e-9) {
|
||||
continue;
|
||||
}
|
||||
for k in 1..=count {
|
||||
let d = lo + (hi - lo) * k as f64 / (count + 1) as f64;
|
||||
let Some(pl) = Plane::from_normal(cut_n * d, cut_n) else { continue };
|
||||
for g in crate::project::section(&pl, m, Some(f)) {
|
||||
let w: Vec<Vec3> = link_polyline(&g).into_iter().map(|q| pl.to_world(q)).collect();
|
||||
if w.len() >= 2 {
|
||||
out.push(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Join polylines whose ends meet into longer ones.
|
||||
fn join_wires(mut ws: Vec<Vec<Vec3>>, tol: f64) -> Vec<Vec<Vec3>> {
|
||||
let mut out: Vec<Vec<Vec3>> = Vec::new();
|
||||
|
||||
Reference in new issue
Block a user