cut a bevel gear

This commit is contained in:
numzero 2026-10-08 21:28:17 +03:00
parent e46b68003d
commit 5603fea44b

View File

@ -4,7 +4,7 @@ use flo_draw::{
canvas::{Color::Rgba, GraphicsContext},
create_drawing_window_with_events, with_2d_graphics,
};
use glam::{FloatExt, Vec2, vec2};
use glam::{FloatExt, Vec2, mat3, vec2, vec3};
const fn rad(deg: f32) -> f32 {
deg * (PI / 180.0)
@ -53,14 +53,32 @@ fn main() {
let (canvas, mut events) = create_drawing_window_with_events(title);
let mut t = 0.0;
let n_rack_teeth = 10;
let n_rack_teeth = 6;
let n_crown_teeth = 3;
let r_crown = 18.0;
let n_teeth = 36;
let n_teeth = 24;
let n_teeth_sample = n_teeth;
let r_pitch = 0.5 * n_teeth as f32;
let n_other = 16;
let r_other = 0.5 * n_other as f32;
let shaft_angle = 0.5 * PI;
let r_crown = {
let (s, c) = shaft_angle.sin_cos();
let pt = vec2(r_other * s, r_pitch + r_other * c);
let x = pt.x + pt.y * (c / s);
let y = r_pitch;
vec2(x, y).length()
};
let crown_angle = (r_pitch / r_crown).asin();
dbg!(r_pitch, r_crown, crown_angle * 180.0 / PI);
let crown_rot = {
let (s, c) = crown_angle.sin_cos();
mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, c, s), vec3(0.0, -s, c))
};
let gear_profile = Profile {
addemdum: ADDENDUM,
dedendum: DEDENDUM,
@ -74,7 +92,12 @@ fn main() {
let n_points_per_tooth = 100;
let n_points = n_points_per_tooth * n_teeth_sample;
let mut shape = vec![r_pitch + ADDENDUM; n_points];
let mut inv_shape = vec![r_pitch + ADDENDUM; n_points];
let mut bevel_shape = vec![r_pitch + ADDENDUM; n_points];
let show_crown_gear = false;
let show_crown_cutter = true;
let show_rack_gear = false;
let show_rack_cutter = true;
'main: loop {
while let Poll::Ready(event) =
events.poll_next_unpin(&mut Context::from_waker(Waker::noop()))
@ -97,12 +120,55 @@ fn main() {
let gear_offset = t * TPS - (n_teeth_sample as f32 + 5.0);
let rack_offset = (t * TPS).fract_gl();
let rack_y_at_x = |x, profile| {
let angle = f32::atan2(x, r_crown);
let vx = angle * r_crown;
rack_shape(vx - rack_offset * PI, profile)
let make_crown = |profile| {
let mut crown_project = Vec::new();
for tooth in -n_crown_teeth..=n_crown_teeth {
for k in 0..n_points_per_tooth {
let tooth_frac = k as f32 / n_points_per_tooth as f32;
let normal_height_at_r =
rack_shape((tooth_frac - rack_offset) * PI, profile);
let angle = PI * (tooth as f32 + tooth_frac) / r_crown;
let (s, c) = angle.sin_cos();
let pos_rel_to_crown =
vec3(r_crown * s, normal_height_at_r, r_crown * c);
let pos_rel_to_crown_center = crown_rot * pos_rel_to_crown;
let pos_rel_to_center = r_crown
* crown_angle.cos() * vec2(
pos_rel_to_crown_center.x,
pos_rel_to_crown_center.y,
) / pos_rel_to_crown_center.z;
let pos = vec2(pos_rel_to_center.x, pos_rel_to_center.y + r_pitch);
crown_project.push(pos);
}
}
crown_project
};
let crown_gear = make_crown(&gear_profile);
let crown_cutter = make_crown(&cutter_profile);
let rack_y_at_x = |x, profile| rack_shape(x - rack_offset * PI, profile);
let inv_cutter_y_at_x = |x| rack_y_at_x(x, &cutter_profile);
let bevel_cutter_y_at_x = |x| {
if x < crown_cutter.first().unwrap().x || x > crown_cutter.last().unwrap().x {
return -cutter_profile.dedendum;
}
let mut i1 = 0;
let mut i2 = crown_cutter.len() - 1;
while i2 - i1 > 1 {
let i3 = (i1 + i2) / 2;
if x < crown_cutter[i3].x {
i2 = i3;
} else {
i1 = i3;
}
}
let a = crown_cutter[i1];
let b = crown_cutter[i2];
assert!(a.x <= x);
assert!(x <= b.x);
let delta = (x - a.x) / (b.x - a.x);
a.y + delta * (b.y - a.y)
};
let cutter_y_at_x = |x| rack_y_at_x(x, &cutter_profile);
let kr_to_xy = |k, r: f32| {
let u = k as f32 / n_points_per_tooth as f32;
@ -111,10 +177,10 @@ fn main() {
vec2(s * r, r_pitch - c * r)
};
for (k, r) in shape.iter_mut().enumerate() {
for (k, r) in inv_shape.iter_mut().enumerate() {
loop {
let xy = kr_to_xy(k, *r);
let cut_y = cutter_y_at_x(xy.x);
let cut_y = inv_cutter_y_at_x(xy.x);
if cut_y < xy.y {
break;
}
@ -122,7 +188,29 @@ fn main() {
}
}
line(gc, shape.iter().enumerate().map(|(k, r)| kr_to_xy(k, *r)));
for (k, r) in bevel_shape.iter_mut().enumerate() {
loop {
let xy = kr_to_xy(k, *r);
let cut_y = bevel_cutter_y_at_x(xy.x);
if cut_y < xy.y {
break;
}
*r -= 0.01;
}
}
line(
gc,
inv_shape.iter().enumerate().map(|(k, r)| kr_to_xy(k, *r)),
);
gc.close_path();
gc.stroke_color(Rgba(0.3, 0.3, 0.3, 0.4));
gc.stroke();
line(
gc,
bevel_shape.iter().enumerate().map(|(k, r)| kr_to_xy(k, *r)),
);
gc.close_path();
gc.stroke_color(Rgba(0.1, 0.6, 1.0, 0.4));
gc.stroke();
@ -134,17 +222,30 @@ fn main() {
let x = (tooth as f32 + k as f32 / n_points_per_tooth as f32 + rack_offset)
* PI;
pts_rack.push(vec2(x, rack_y_at_x(x, &gear_profile)));
pts_cutter.push(vec2(x, cutter_y_at_x(x)));
pts_cutter.push(vec2(x, inv_cutter_y_at_x(x)));
}
}
if show_crown_cutter {
line(gc, crown_cutter);
gc.stroke_color(Rgba(1.0, 1.0, 0.1, 0.4));
gc.stroke();
}
if show_crown_gear {
line(gc, crown_gear);
gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.4));
gc.stroke();
}
if show_rack_cutter {
line(gc, pts_cutter);
gc.stroke_color(Rgba(1.0, 0.1, 0.1, 0.4));
gc.stroke();
}
if show_rack_gear {
line(gc, pts_rack);
gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.4));
gc.stroke();
}
});
std::thread::sleep(Duration::from_secs_f32(1.0 / FPS));