diff --git a/src/main.rs b/src/main.rs index 4dabeb6..1625373 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::f32::consts::PI; +use std::{f32::consts::PI, time::Duration}; use flo_draw::{ canvas::{Color::Rgba, GraphicsContext, GraphicsPrimitives, Transform2D}, @@ -216,38 +216,147 @@ fn cut_crown(gc: &mut impl GraphicsContext, n_teeth_crown: u32, n_teeth_gear: u3 } } -fn draw(gc: &mut impl GraphicsContext) { - gc.clear_canvas(Rgba(0.1, 0.1, 0.1, 1.0)); - - gc.canvas_height(20.0); - gc.line_width(0.05); - - gc.stroke_color(Rgba(1.0, 0.1, 0.1, 0.1)); - // cut_involute(gc, 24); - - gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.1)); - cut_crown(gc, 36, 24); +fn cutter_y_at_x(x: f32) -> f32 { + let x = (x / PITCH).fract_gl() * PITCH; + let a = DEDENDUM * vec2(PRESSURE_ANGLE.tan(), 1.0); + let d = ADDENDUM * vec2(PRESSURE_ANGLE.tan(), -1.0); + let slope = 1.0 / PRESSURE_ANGLE.tan(); + if x < d.x { + return -x * slope; + } + if x <= 0.5 * PITCH - d.x { + return d.y; + } + if x <= 0.5 * PITCH + a.x { + return (x - 0.5 * PITCH) * slope; + } + if x <= PITCH - a.x { + return a.y; + } + return (PITCH - x) * slope; } +const FPS: f32 = 60.0; +const TPS: f32 = 1.0; + fn main() { with_2d_graphics(|| { let title = "Gear shape test"; - if cfg!(events) { - use futures::StreamExt; - use std::task::{Context, Poll, Waker}; + use futures::StreamExt; + use std::task::{Context, Poll, Waker}; - let (canvas, mut events) = create_drawing_window_with_events(title); - 'main: loop { - while let Poll::Ready(event) = - events.poll_next_unpin(&mut Context::from_waker(Waker::noop())) - { - let Some(_event) = event else { break 'main }; - } - canvas.draw(draw); + let (canvas, mut events) = create_drawing_window_with_events(title); + let mut t = 0.0; + + let n_rack_teeth = 10; + + let n_teeth = 36; + let n_teeth_sample = n_teeth; + let r_pitch = 0.5 * n_teeth as f32; + 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]; + 'main: loop { + while let Poll::Ready(event) = + events.poll_next_unpin(&mut Context::from_waker(Waker::noop())) + { + let Some(_event) = event else { break 'main }; } - } else { - let canvas = create_drawing_window(title); - canvas.draw(draw); + canvas.draw(|gc| { + gc.clear_canvas(Rgba(0.1, 0.1, 0.1, 1.0)); + + gc.canvas_height(20.0); + gc.line_width(0.05); + + gc.stroke_color(Rgba(1.0, 1.0, 1.0, 0.1)); + gc.move_to(-20.0, 0.0); + gc.line_to(20.0, 0.0); + gc.move_to(0.0, -20.0); + gc.line_to(0.0, 20.0); + gc.stroke(); + + let gear_offset = t * TPS - (n_teeth_sample as f32 + 5.0); + let rack_offset = (t * TPS).fract_gl(); + + let kr_to_xy = |k, r: f32| { + let u = k as f32 / n_points_per_tooth as f32; + let angle = 2.0 * PI * (gear_offset + u) / n_teeth as f32; + let (s, c) = angle.sin_cos(); + vec2(s * r, r_pitch - c * r) + }; + + for (k, r) in shape.iter_mut().enumerate() { + loop { + let xy = kr_to_xy(k, *r); + let cut_y = cutter_y_at_x(xy.x - rack_offset * PITCH); + if cut_y < xy.y { + break; + } + *r -= 0.01; + } + } + + #[cfg(false)] + { + line( + gc, + shape.iter().enumerate().map(|(k, r)| { + let xy = kr_to_xy(k, *r); + let cut_y = cutter_y_at_x(xy.x - rack_offset * PITCH); + vec2(xy.x, cut_y) + }), + ); + gc.stroke_color(Rgba(1.0, 1.0, 0.1, 0.4)); + gc.stroke(); + } + + line(gc, 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(); + + let mut pts = Vec::new(); + for tooth in -n_rack_teeth..=n_rack_teeth { + let x = (tooth as f32 + rack_offset) * PITCH; + let a = DEDENDUM * vec2(PRESSURE_ANGLE.tan(), 1.0); + let d = ADDENDUM * vec2(PRESSURE_ANGLE.tan(), -1.0); + pts.push(vec2(x - 0.5 * PITCH - d.x, d.y)); + pts.push(vec2(x - 0.5 * PITCH + a.x, a.y)); + pts.push(vec2(x - a.x, a.y)); + pts.push(vec2(x + d.x, d.y)); + } + line(gc, pts); + gc.stroke_color(Rgba(1.0, 0.1, 0.1, 0.4)); + gc.stroke(); + + let mut pts = Vec::new(); + for tooth in -n_rack_teeth..=n_rack_teeth { + let x = (tooth as f32 + rack_offset) * PITCH; + let a = ADDENDUM * vec2(PRESSURE_ANGLE.tan(), 1.0); + let d = DEDENDUM * vec2(PRESSURE_ANGLE.tan(), -1.0); + pts.push(vec2(x - 0.5 * PITCH - d.x, d.y)); + pts.push(vec2(x - 0.5 * PITCH + a.x, a.y)); + pts.push(vec2(x - a.x, a.y)); + pts.push(vec2(x + d.x, d.y)); + } + line(gc, pts); + gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.4)); + gc.stroke(); + }); + + std::thread::sleep(Duration::from_secs_f32(1.0 / FPS)); + t += 1.0 / FPS; } }); } + +fn line(gc: &mut impl GraphicsContext, pts: impl IntoIterator) { + let mut pts = pts.into_iter(); + let Some(pt) = pts.next() else { + return; + }; + gc.move_to(pt.x, pt.y); + for pt in pts { + gc.line_to(pt.x, pt.y); + } +}