use cadrum::{BSplineEnd, DVec3, ProfileOrient, Solid, Wire};
use std::io::Read;
use crate::Result;
use crate::artifact::Artifact;
use crate::coils;
pub fn run(
input: impl Read,
width: f64,
thickness: f64,
toroidal_extent: f64,
scale: f64,
) -> Result<Vec<Artifact>> {
let filaments = coils::parse(input)?;
println!(
" Parsed {} filaments (nfp={})",
filaments.coils.len(),
filaments.nfp
);
if toroidal_extent < 360.0 {
println!(
" [note] --toroidal-extent {} specified but coil filtering not implemented in this version",
toroidal_extent
);
}
println!(
"Building {} coil solids (width = {} m, thickness = {} m, scale = {})...",
filaments.coils.len(),
width,
thickness,
scale
);
let mut solids: Vec<Solid> = Vec::with_capacity(filaments.coils.len());
let mut points: Vec<DVec3> = Vec::new();
for (idx, raw_pts) in filaments.coils.iter().enumerate() {
match build_one(
raw_pts.iter().map(|p| *p * scale),
width * scale,
thickness * scale,
) {
Ok((s, pts)) => {
solids.push(s);
points.extend(pts);
}
Err(e) => {
eprintln!(" [warn] coil #{} sweep failed: {}", idx, e);
}
}
}
println!(
" {} / {} coils succeeded",
solids.len(),
filaments.coils.len()
);
if solids.is_empty() {
return Err("no coil solids produced".into());
}
Ok([(Artifact {
name: "magnet_set".to_string(),
solids,
points
})].into_iter().collect())
}
fn build_one(
raw_pts: impl IntoIterator<Item = DVec3>,
width: f64,
thickness: f64,
) -> Result<(Solid, Vec<DVec3>)> {
use cadrum::Edge;
let raw_pts: Vec<DVec3> = raw_pts.into_iter().collect();
if raw_pts.len() < 4 {
return Err(format!("too few points ({})", raw_pts.len()).into());
}
let spine = Edge::bspline(&raw_pts, BSplineEnd::NotAKnot)
.map_err(|e| format!("bspline failed: {:?}", e))?;
const AUX_SCALE: f64 = 0.9;
let com: DVec3 = raw_pts.iter().copied().sum::<DVec3>() / (raw_pts.len() as f64);
let aux_pts: Vec<DVec3> = raw_pts
.iter()
.map(|p| {
let (point, tangent) = spine.project(*p);
let aux_raw = com + (*p - com) * AUX_SCALE;
let aux = aux_raw - tangent * tangent.dot(aux_raw - point);
point + (aux - point).normalize() * thickness
})
.collect();
let aux_spine = Edge::bspline(&aux_pts, BSplineEnd::NotAKnot)
.map_err(|e| format!("aux bspline failed: {:?}", e))?;
let w = width;
let t = thickness;
let profile = if false {
Edge::polygon(&[
DVec3::new(w / 2.0, t / 2.0, 0.0),
DVec3::new(w / 2.0, -t / 2.0, 0.0),
DVec3::new(-w / 2.0, -t / 2.0, 0.0),
DVec3::new(-w / 2.0, t / 2.0, 0.0),
])
.map_err(|e| format!("polygon failed: {:?}", e))?
} else {
vec![Edge::circle(DVec3::new(width, thickness, 0.).length()/2.0, DVec3::Z)?]
};
let tangent = spine.start_tangent();
let origin = spine.start_point();
let outward = origin - com;
let profile = profile.align_z(tangent, outward).translate(origin);
let mut dump_pts: Vec<DVec3> = Vec::with_capacity(profile.len() + raw_pts.len());
for e in profile.iter() {
dump_pts.push(e.start_point());
}
dump_pts.extend_from_slice(&raw_pts);
dump_pts.extend_from_slice(&aux_pts);
let aux_spines = [aux_spine];
let orient = if profile.len() == 1 {
ProfileOrient::Torsion
} else {
ProfileOrient::Auxiliary(&aux_spines)
};
let coil = Solid::sweep(profile.iter(), std::iter::once(&spine), orient)
.map_err(|e| format!("sweep failed: {:?}", e))?;
Ok((coil, dump_pts))
}