#![allow(dead_code)]
#[allow(dead_code)]
pub struct ScrewResult {
pub verts: Vec<[f32; 3]>,
pub tris: Vec<[u32; 3]>,
}
#[allow(dead_code)]
pub fn screw_vert_count(profile_len: usize, steps: u32) -> usize {
profile_len * (steps as usize + 1)
}
#[allow(dead_code)]
pub fn screw_mesh(
profile: &[[f32; 3]],
steps: u32,
angle_rad: f32,
screw_offset: f32,
axis: u8,
) -> ScrewResult {
let pl = profile.len();
let total_steps = steps + 1;
let mut verts = Vec::with_capacity(pl * total_steps as usize);
let mut tris = Vec::new();
for s in 0..total_steps {
let t = s as f32 / steps.max(1) as f32;
let angle = angle_rad * t;
let (sin_a, cos_a) = angle.sin_cos();
let z_off = screw_offset * t;
for p in profile {
let v = match axis {
1 => {
let x = p[0] * cos_a - p[2] * sin_a;
let z = p[0] * sin_a + p[2] * cos_a;
[x, p[1] + z_off, z]
}
2 => {
let x = p[0] * cos_a - p[1] * sin_a;
let y = p[0] * sin_a + p[1] * cos_a;
[x, y, p[2] + z_off]
}
_ => {
let y = p[1] * cos_a - p[2] * sin_a;
let z = p[1] * sin_a + p[2] * cos_a;
[p[0], y, z + z_off]
}
};
verts.push(v);
}
if s > 0 {
let base0 = ((s - 1) as usize * pl) as u32;
let base1 = (s as usize * pl) as u32;
for i in 0..pl.saturating_sub(1) {
let i0 = base0 + i as u32;
let i1 = base0 + i as u32 + 1;
let i2 = base1 + i as u32;
let i3 = base1 + i as u32 + 1;
tris.push([i0, i2, i1]);
tris.push([i1, i2, i3]);
}
}
}
ScrewResult { verts, tris }
}
use std::f32::consts::PI;
pub struct ScrewParams {
pub angle_deg: f32,
pub screw_offset: f32,
pub axis: [f32; 3],
pub steps: usize,
pub iterations: usize,
}
pub fn new_screw_params(angle_deg: f32, offset: f32, steps: usize) -> ScrewParams {
ScrewParams {
angle_deg,
screw_offset: offset,
axis: [0.0, 1.0, 0.0],
steps: steps.max(1),
iterations: 1,
}
}
pub fn screw_transform_vertex(p: [f32; 3], params: &ScrewParams, step: usize) -> [f32; 3] {
let total_steps = params.steps * params.iterations;
let t = step as f32 / total_steps.max(1) as f32;
let angle_rad = params.angle_deg * PI / 180.0 * t;
let (sin_a, cos_a) = angle_rad.sin_cos();
let ax = params.axis;
let dot = p[0] * ax[0] + p[1] * ax[1] + p[2] * ax[2];
let cross = [
ax[1] * p[2] - ax[2] * p[1],
ax[2] * p[0] - ax[0] * p[2],
ax[0] * p[1] - ax[1] * p[0],
];
let rx = p[0] * cos_a + cross[0] * sin_a + ax[0] * dot * (1.0 - cos_a);
let ry = p[1] * cos_a + cross[1] * sin_a + ax[1] * dot * (1.0 - cos_a);
let rz = p[2] * cos_a + cross[2] * sin_a + ax[2] * dot * (1.0 - cos_a);
let translate = params.screw_offset * t;
[
rx + ax[0] * translate,
ry + ax[1] * translate,
rz + ax[2] * translate,
]
}
pub fn screw_total_height(params: &ScrewParams) -> f32 {
params.screw_offset * params.iterations as f32
}
pub fn screw_total_angle_deg(params: &ScrewParams) -> f32 {
params.angle_deg * params.iterations as f32
}
pub fn screw_output_vertex_count(input: usize, params: &ScrewParams) -> usize {
input * (params.steps * params.iterations + 1)
}
#[cfg(test)]
mod tests {
use super::*;
fn line_profile() -> Vec<[f32; 3]> {
vec![[1.0, 0.0, 0.0], [1.0, 1.0, 0.0]]
}
#[test]
fn vert_count_formula() {
assert_eq!(screw_vert_count(2, 4), 10);
}
#[test]
fn screw_verts_count_matches() {
let r = screw_mesh(&line_profile(), 4, PI, 1.0, 0);
assert_eq!(r.verts.len(), screw_vert_count(2, 4));
}
#[test]
fn zero_angle_first_vert_unchanged() {
let profile = vec![[1.0, 0.0, 0.0]];
let r = screw_mesh(&profile, 4, 0.0, 0.0, 0);
assert!((r.verts[0][0] - 1.0).abs() < 1e-5);
}
#[test]
fn screw_generates_tris() {
let r = screw_mesh(&line_profile(), 4, PI, 0.0, 0);
assert!(!r.tris.is_empty());
}
#[test]
fn axis_y_rotation() {
let profile = vec![[1.0, 0.0, 0.0]];
let r = screw_mesh(&profile, 1, PI / 2.0, 0.0, 1);
assert!(r.verts[1][2].abs() > 0.5);
}
#[test]
fn axis_z_rotation() {
let profile = vec![[1.0, 0.0, 0.0]];
let r = screw_mesh(&profile, 1, PI / 2.0, 0.0, 2);
assert!(r.verts[1][1].abs() > 0.5);
}
#[test]
fn screw_offset_applied() {
let profile = vec![[1.0, 0.0, 0.0]];
let r = screw_mesh(&profile, 2, 0.0, 3.0, 0);
assert!((r.verts[2][2] - 3.0).abs() < 1e-5);
}
#[test]
fn full_rotation_uses_tau() {
let tau = std::f32::consts::TAU;
assert!((tau - 2.0 * PI).abs() < 1e-6);
}
#[test]
fn single_step_two_tris_per_segment() {
let r = screw_mesh(&line_profile(), 1, PI, 0.0, 0);
assert_eq!(r.tris.len(), 2);
}
}