Skip to main content

09_bspline/
09_bspline.rs

1use cadrum::{DQuat, DVec3, Solid};
2use std::f64::consts::TAU;
3
4// 2 field-period stellarator-like torus.
5// `Solid::bspline` is fed a 2D control-point grid to build a periodic B-spline solid.
6// Every variation below is invariant under phi → phi+π (or shifts by a multiple
7// of 2π), so the resulting shape has 180° rotational symmetry around the Z axis:
8//   a(phi)       = 1.8 + 0.6 * sin(2φ)      radial semi-axis
9//   b(phi)       = 1.0 + 0.4 * cos(2φ)      Z semi-axis
10//   psi(phi)     = 2 * phi                  cross-section twist (2 turns per loop)
11//   z_shift(phi) = 1.0 * sin(2φ)            vertical undulation
12const M: usize = 48; // toroidal (U) — must be even for 180° symmetry
13const N: usize = 24; // poloidal (V) — arbitrary
14const RING_R: f64 = 6.0;
15
16fn point(i: usize, j: usize) -> DVec3 {
17	let phi = TAU * (i as f64) / (M as f64);
18	let theta = TAU * (j as f64) / (N as f64);
19	let two_phi = 2.0 * phi;
20	let a = 1.8 + 0.6 * two_phi.sin();
21	let b = 1.0 + 0.4 * two_phi.cos();
22	let psi = two_phi; // twist: 2 full turns per toroidal loop
23	let z_shift = 1.0 * two_phi.sin();
24	// 1. Local cross-section (pre-twist ellipse in the (X, Z) plane)
25	let local_raw = DVec3::X * (a * theta.cos()) + DVec3::Z * (b * theta.sin());
26	// 2. Rotate by psi around the local Y axis (major-circle tangent) — the twist
27	let local_twisted = DQuat::from_axis_angle(DVec3::Y, psi) * local_raw;
28	// 3. Undulate vertically in the local frame
29	let local_shifted = local_twisted + DVec3::Z * z_shift;
30	// 4. Push outward along the major radius by RING_R
31	let translated = local_shifted + DVec3::X * RING_R;
32	// 5. Rotate the whole point around the global Z axis by phi
33	DQuat::from_axis_angle(DVec3::Z, phi) * translated
34}
35
36fn main() {
37	let example_name = std::path::Path::new(file!()).file_stem().unwrap().to_str().unwrap();
38
39	let plasma = Solid::bspline(M, N, true, point).expect("2-period bspline torus should succeed");
40	let objects = [plasma.color("cyan")];
41	let mut f = std::fs::File::create(format!("{example_name}.step")).unwrap();
42	cadrum::write_step(&objects, &mut f).unwrap();
43	let mut f_svg = std::fs::File::create(format!("{example_name}.svg")).unwrap();
44	cadrum::mesh(&objects, 0.1).and_then(|m| m.write_svg(DVec3::new(0.05, 0.05, 1.0), DVec3::Y, false, true, &mut f_svg)).unwrap();
45	println!("wrote {example_name}.step / {example_name}.svg");
46}