add gear profile

This commit is contained in:
numzero 2026-10-17 22:14:28 +03:00
parent 42b37369f5
commit bb0db54ef1
2 changed files with 99 additions and 0 deletions

View File

@ -3,6 +3,7 @@ use std::error::Error;
use glam::{Mat4, UVec2, Vec3, Vec4, vec3, vec4}; use glam::{Mat4, UVec2, Vec3, Vec4, vec3, vec4};
mod camera; mod camera;
mod profile;
mod render; mod render;
const BACKGROUND: Vec4 = vec4(0.00, 0.39, 0.52, 1.00); const BACKGROUND: Vec4 = vec4(0.00, 0.39, 0.52, 1.00);

98
src/profile.rs Normal file
View File

@ -0,0 +1,98 @@
use std::f32::consts::PI;
use glam::FloatExt;
#[derive(Debug, Clone, Copy)]
pub struct Profile {
pub addendum: f32,
pub dedendum: f32,
pub pressure_angle: f32,
}
impl Default for Profile {
fn default() -> Self {
Profile {
addendum: 1.0,
dedendum: 1.25,
pressure_angle: const { 20.0f32.to_radians() },
}
}
}
impl Profile {
pub(crate) const PITCH: f32 = PI;
pub fn y_from_x(&self, x: f32) -> f32 {
self.y_from_tooth(x / Self::PITCH)
}
pub fn y_from_tooth(&self, t: f32) -> f32 {
debug_assert!(self.addendum > 0.0);
debug_assert!(self.dedendum > 0.0);
debug_assert!(self.pressure_angle > 0.0);
let t = t.fract_gl();
let slope = PI * self.slope();
let a = self.addendum / slope;
let d = self.dedendum / slope;
debug_assert!(2.0 * a + 2.0 * d < 1.0, "pressure angle is too high");
if t < a {
slope * t
} else if t < 0.5 - a {
self.addendum
} else if t < 0.5 + d {
-(t - 0.5) * slope
} else if t < 1.0 - d {
-self.dedendum
} else {
(t - 1.0) * slope
}
}
pub fn slope(&self) -> f32 {
1.0 / self.pressure_angle.tan()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_profile() {
let profile = Profile::default();
let eps = 0.01;
for (t, y) in [
(0.0, 0.0),
(0.25, profile.addendum),
(0.5, 0.0),
(0.75, -profile.dedendum),
(1.0, 0.0),
(1.25, profile.addendum),
] {
let x = Profile::PITCH * t;
let comp_y = profile.y_from_x(x);
assert!(
(comp_y - y).abs() < eps,
"At t={t} (x={x}), expected y={y}, got y={comp_y}"
);
}
for (t, slope) in [
(0.0, 1.0),
(eps, 1.0),
(0.25, 0.0),
(0.5, -1.0),
(0.75, 0.0),
(1.0 - eps, 1.0),
(1.0, 1.0),
(1.0 + eps, 1.0),
] {
let x = Profile::PITCH * t;
let comp_slope = (profile.y_from_x(x + eps) - profile.y_from_x(x - eps)) / (2.0 * eps);
let exp_slope = profile.slope() * slope;
assert!(
(comp_slope - exp_slope).abs() < eps,
"At t={t} (x={x}), expected slope={exp_slope}, got slope={comp_slope}"
);
}
}
}