diff --git a/src/main.rs b/src/main.rs index 8885614..fd0df79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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))); } } - line(gc, pts_cutter); - gc.stroke_color(Rgba(1.0, 0.1, 0.1, 0.4)); - gc.stroke(); - - line(gc, pts_rack); - gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.4)); - gc.stroke(); + 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));