draw a few gear shapes
This commit is contained in:
parent
96e7725857
commit
4324336cf0
342
src/main.rs
342
src/main.rs
|
|
@ -1,167 +1,253 @@
|
||||||
use std::{
|
use std::f32::consts::PI;
|
||||||
f32::consts::PI,
|
|
||||||
pin::pin,
|
|
||||||
task::{Context, Poll, Waker},
|
|
||||||
};
|
|
||||||
|
|
||||||
use flo_draw::{
|
use flo_draw::{
|
||||||
canvas::{Color::Rgba, GraphicsContext, GraphicsPrimitives},
|
canvas::{Color::Rgba, GraphicsContext, GraphicsPrimitives, Transform2D},
|
||||||
create_drawing_window, create_drawing_window_with_events, with_2d_graphics,
|
create_drawing_window, create_drawing_window_with_events, with_2d_graphics,
|
||||||
};
|
};
|
||||||
use futures::{StreamExt, executor, pin_mut};
|
use glam::{Affine2, FloatExt, Vec2, vec2};
|
||||||
use glam::{FloatExt, Mat2, Vec2, vec2};
|
|
||||||
|
|
||||||
const fn rad(deg: f32) -> f32 {
|
const fn rad(deg: f32) -> f32 {
|
||||||
deg * (PI / 180.0)
|
deg * (PI / 180.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn involute(t: f32) -> Vec2 {
|
const ADDENDUM: f32 = 1.00;
|
||||||
let v = Vec2::from_angle(t);
|
const DEDENDUM: f32 = 1.25;
|
||||||
v - t * v.perp()
|
const PRESSURE_ANGLE: f32 = rad(20.0);
|
||||||
|
const PITCH: f32 = PI;
|
||||||
|
|
||||||
|
fn draw_rack(gc: &mut impl GraphicsContext) {
|
||||||
|
gc.move_to(-ADDENDUM * PRESSURE_ANGLE.tan(), ADDENDUM);
|
||||||
|
gc.line_to(DEDENDUM * PRESSURE_ANGLE.tan(), -DEDENDUM);
|
||||||
|
gc.line_to(0.5 * PITCH - DEDENDUM * PRESSURE_ANGLE.tan(), -DEDENDUM);
|
||||||
|
gc.line_to(0.5 * PITCH + ADDENDUM * PRESSURE_ANGLE.tan(), ADDENDUM);
|
||||||
|
gc.line_to(PITCH - ADDENDUM * PRESSURE_ANGLE.tan(), ADDENDUM);
|
||||||
|
gc.line_to(PITCH + DEDENDUM * PRESSURE_ANGLE.tan(), -DEDENDUM);
|
||||||
|
gc.stroke();
|
||||||
|
|
||||||
|
gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.2));
|
||||||
|
|
||||||
|
gc.move_to(-PITCH, 0.0);
|
||||||
|
gc.line_to(PITCH, 0.0);
|
||||||
|
gc.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn involute_radius_to_t(r: f32) -> f32 {
|
fn cut_involute(gc: &mut impl GraphicsContext, n_teeth: u32) {
|
||||||
(r * r - 1.).sqrt()
|
let tooth_angle = 360.0 / n_teeth as f32;
|
||||||
|
let r = 0.5 * n_teeth as f32;
|
||||||
|
let n = 500;
|
||||||
|
for k in -n..=n {
|
||||||
|
let t = 2.0 * k as f32 / n as f32;
|
||||||
|
gc.push_state();
|
||||||
|
gc.transform(Transform2D::translate(0.0, -r));
|
||||||
|
gc.transform(Transform2D::rotate_degrees(tooth_angle * t));
|
||||||
|
gc.transform(Transform2D::translate(0.0, r));
|
||||||
|
gc.transform(Transform2D::translate(PITCH * t, 0.0));
|
||||||
|
gc.transform(Transform2D::translate(-2.0 * PITCH, 0.0));
|
||||||
|
for _ in -2..2 {
|
||||||
|
gc.move_to(-ADDENDUM * PRESSURE_ANGLE.tan(), ADDENDUM);
|
||||||
|
gc.line_to(DEDENDUM * PRESSURE_ANGLE.tan(), -DEDENDUM);
|
||||||
|
gc.line_to(0.5 * PITCH - DEDENDUM * PRESSURE_ANGLE.tan(), -DEDENDUM);
|
||||||
|
gc.line_to(0.5 * PITCH + ADDENDUM * PRESSURE_ANGLE.tan(), ADDENDUM);
|
||||||
|
gc.line_to(PITCH - ADDENDUM * PRESSURE_ANGLE.tan(), ADDENDUM);
|
||||||
|
gc.line_to(PITCH + DEDENDUM * PRESSURE_ANGLE.tan(), -DEDENDUM);
|
||||||
|
gc.stroke();
|
||||||
|
gc.transform(Transform2D::translate(PITCH, 0.0));
|
||||||
|
}
|
||||||
|
gc.pop_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gear_shape_ex(n: usize, inner: f32, outer: f32) -> Vec<Vec2> {
|
gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.2));
|
||||||
const Q: usize = 6;
|
|
||||||
|
|
||||||
const A: f32 = rad(28.);
|
gc.circle(0.0, -r, r);
|
||||||
let base = A.cos();
|
gc.stroke();
|
||||||
|
|
||||||
let base_to_pitch_angle = involute(involute_radius_to_t(1. / base)).to_angle();
|
|
||||||
|
|
||||||
let tooth_angle = 2.0 * PI / n as f32;
|
|
||||||
println!("{n}: {base}, {inner}, {outer}");
|
|
||||||
let t1 = involute_radius_to_t((inner / base).max(1.));
|
|
||||||
let t2 = involute_radius_to_t(outer / base);
|
|
||||||
println!("{t1}-{t2}");
|
|
||||||
println!("{base_to_pitch_angle}, {}", tooth_angle / 4.);
|
|
||||||
|
|
||||||
let mut side = [Vec2::ZERO; Q + 2];
|
|
||||||
side[0] = vec2(inner, 0.0);
|
|
||||||
for k in 0..=Q {
|
|
||||||
let t01 = k as f32 / Q as f32;
|
|
||||||
let t = f32::lerp(t1, t2, t01);
|
|
||||||
side[k + 1] = base * involute(t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = Mat2::from_angle(-(tooth_angle / 4.0 + base_to_pitch_angle));
|
fn show_involute(gc: &mut impl GraphicsContext, n_teeth: u32) {
|
||||||
for p in &mut side {
|
let tooth_angle = 360.0 / n_teeth as f32;
|
||||||
*p = m * *p;
|
let r = 0.5 * n_teeth as f32;
|
||||||
}
|
let n = 500;
|
||||||
if base_to_pitch_angle > tooth_angle / 4.0 {
|
let size = 5.0;
|
||||||
side[0] = Mat2::from_angle(-(tooth_angle / 2.0)) * vec2(inner, 0.0);
|
for k in -n..=n {
|
||||||
|
let t = 2.0 * k as f32 / n as f32;
|
||||||
|
gc.push_state();
|
||||||
|
gc.transform(Transform2D::translate(0.0, -r));
|
||||||
|
gc.transform(Transform2D::rotate_degrees(tooth_angle * t));
|
||||||
|
gc.transform(Transform2D::translate(0.0, r));
|
||||||
|
gc.transform(Transform2D::translate(PITCH * t, 0.0));
|
||||||
|
|
||||||
|
gc.move_to(-size * PRESSURE_ANGLE.tan(), size);
|
||||||
|
gc.line_to(size * PRESSURE_ANGLE.tan(), -size);
|
||||||
|
gc.stroke();
|
||||||
|
gc.pop_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ret = Vec::with_capacity(2 * n * (Q + 2));
|
gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.2));
|
||||||
for i in 0..n {
|
|
||||||
let m = Mat2::from_angle(i as f32 * tooth_angle);
|
gc.circle(0.0, -r, r);
|
||||||
for p in side {
|
gc.circle(0.0, -r, r - DEDENDUM);
|
||||||
ret.push(m * p);
|
gc.circle(0.0, -r, r + ADDENDUM);
|
||||||
}
|
gc.stroke();
|
||||||
for p in side.iter().rev() {
|
|
||||||
ret.push(m * vec2(p.x, -p.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gear_shape_std(n: usize, low: f32, high: f32) -> Vec<Vec2> {
|
fn draw_crown(gc: &mut impl GraphicsContext, n_teeth: u32) {
|
||||||
let unit = 1.0 / n as f32;
|
let tooth_angle = rad(360.0 / n_teeth as f32);
|
||||||
return gear_shape_ex(n, 1.0 - low * unit, 1.0 + high * unit);
|
let r = 0.5 * n_teeth as f32;
|
||||||
|
|
||||||
|
let mut points = Vec::new();
|
||||||
|
let n = 500;
|
||||||
|
for i_point in -n..=n {
|
||||||
|
let t_point = 5.0 * i_point as f32 / n as f32;
|
||||||
|
let x = PITCH * t_point;
|
||||||
|
let angle = f32::atan2(x, r);
|
||||||
|
let tooth = angle / tooth_angle;
|
||||||
|
let vx = PITCH * tooth.fract_gl();
|
||||||
|
|
||||||
|
let tan = PRESSURE_ANGLE.tan();
|
||||||
|
let vy = if vx < DEDENDUM * tan {
|
||||||
|
-vx / tan
|
||||||
|
} else if vx <= 0.5 * PITCH - DEDENDUM * tan {
|
||||||
|
-DEDENDUM
|
||||||
|
} else if vx <= 0.5 * PITCH + ADDENDUM * tan {
|
||||||
|
(vx - 0.5 * PITCH) / tan
|
||||||
|
} else if vx <= PITCH - ADDENDUM * tan {
|
||||||
|
ADDENDUM
|
||||||
|
} else {
|
||||||
|
-(vx - PITCH) / tan
|
||||||
|
};
|
||||||
|
points.push(vec2(x, vy));
|
||||||
|
}
|
||||||
|
gc.move_to(points[0].x, points[0].y);
|
||||||
|
for pt in &points[1..] {
|
||||||
|
gc.line_to(pt.x, pt.y);
|
||||||
|
}
|
||||||
|
gc.stroke();
|
||||||
|
|
||||||
|
gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.2));
|
||||||
|
|
||||||
|
gc.move_to(-20.0, 0.0);
|
||||||
|
gc.line_to(20.0, 0.0);
|
||||||
|
gc.stroke();
|
||||||
|
|
||||||
|
let n = 4;
|
||||||
|
for k in -n..=n {
|
||||||
|
let angle = tooth_angle * k as f32;
|
||||||
|
let x = r * angle.tan();
|
||||||
|
gc.move_to(x, -1.0);
|
||||||
|
gc.line_to(x, 1.0);
|
||||||
|
gc.stroke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gear_shape(n: usize) -> Vec<Vec2> {
|
fn cut_crown(gc: &mut impl GraphicsContext, n_teeth_crown: u32, n_teeth_gear: u32) {
|
||||||
return gear_shape_std(n, 2.2, 2.0);
|
let tooth_angle_crown = rad(360.0 / n_teeth_crown as f32);
|
||||||
|
let r_crown = 0.5 * n_teeth_crown as f32;
|
||||||
|
|
||||||
|
let tooth_angle_gear = rad(360.0 / n_teeth_gear as f32);
|
||||||
|
let r_gear = 0.5 * n_teeth_gear as f32;
|
||||||
|
|
||||||
|
let n_subcuts = 200;
|
||||||
|
for i_subcut in -n_subcuts..=n_subcuts {
|
||||||
|
let t_cut = 2.0 * i_subcut as f32 / n_subcuts as f32;
|
||||||
|
|
||||||
|
let rot_gear = t_cut * tooth_angle_gear;
|
||||||
|
let rot_crown = t_cut * tooth_angle_crown;
|
||||||
|
|
||||||
|
let tfm_gear = Affine2::from_translation(vec2(0.0, -r_gear))
|
||||||
|
* Affine2::from_angle_translation(-rot_gear, Vec2::ZERO)
|
||||||
|
* Affine2::from_translation(vec2(0.0, r_gear));
|
||||||
|
|
||||||
|
let mut points = Vec::new();
|
||||||
|
let n_points = 300;
|
||||||
|
for i_point in -n_points..=n_points {
|
||||||
|
let t_point = 2.0 * i_point as f32 / n_points as f32;
|
||||||
|
let x = PITCH * t_point;
|
||||||
|
let angle = f32::atan2(x, r_crown) + rot_crown;
|
||||||
|
let tooth = angle / tooth_angle_crown;
|
||||||
|
let vx = PITCH * tooth.fract_gl();
|
||||||
|
|
||||||
|
let tan = PRESSURE_ANGLE.tan();
|
||||||
|
let vy = if vx < DEDENDUM * tan {
|
||||||
|
-vx / tan
|
||||||
|
} else if vx <= 0.5 * PITCH - DEDENDUM * tan {
|
||||||
|
-DEDENDUM
|
||||||
|
} else if vx <= 0.5 * PITCH + ADDENDUM * tan {
|
||||||
|
(vx - 0.5 * PITCH) / tan
|
||||||
|
} else if vx <= PITCH - ADDENDUM * tan {
|
||||||
|
ADDENDUM
|
||||||
|
} else {
|
||||||
|
-(vx - PITCH) / tan
|
||||||
|
};
|
||||||
|
points.push(tfm_gear.transform_point2(vec2(x, vy)));
|
||||||
|
}
|
||||||
|
gc.move_to(points[0].x, points[0].y);
|
||||||
|
for pt in &points[1..] {
|
||||||
|
gc.line_to(pt.x, pt.y);
|
||||||
|
}
|
||||||
|
gc.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gear_shape_outer(n: usize) -> Vec<Vec2> {
|
gc.stroke_color(Rgba(0.1, 1.0, 0.1, 0.2));
|
||||||
return gear_shape_std(n, 2.0, 2.2);
|
|
||||||
|
gc.move_to(-20.0, 0.0);
|
||||||
|
gc.line_to(20.0, 0.0);
|
||||||
|
gc.stroke();
|
||||||
|
|
||||||
|
let n = 4;
|
||||||
|
for k in -n..=n {
|
||||||
|
let angle = tooth_angle_crown * k as f32;
|
||||||
|
let x = r_crown * angle.tan();
|
||||||
|
gc.move_to(x, -1.0);
|
||||||
|
gc.line_to(x, 1.0);
|
||||||
|
gc.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.circle(0.0, -r_gear, r_gear);
|
||||||
|
gc.circle(0.0, -r_gear, r_gear - DEDENDUM);
|
||||||
|
gc.circle(0.0, -r_gear, r_gear + ADDENDUM);
|
||||||
|
gc.stroke();
|
||||||
|
|
||||||
|
let n = 4;
|
||||||
|
for k in -n..=n {
|
||||||
|
let angle = tooth_angle_gear * k as f32;
|
||||||
|
let (x, y) = angle.sin_cos();
|
||||||
|
gc.move_to(0.0, -r_gear);
|
||||||
|
gc.line_to(x * r_gear, (y - 1.0) * r_gear);
|
||||||
|
gc.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 main() {
|
fn main() {
|
||||||
const R_SCALE: f32 = 0.5;
|
|
||||||
const N_TEETH_SUN: usize = 36;
|
|
||||||
const N_TEETH_SAT: usize = 24;
|
|
||||||
const N_TEETH_SKY: usize = N_TEETH_SUN + 2 * N_TEETH_SAT;
|
|
||||||
const N_SAT: usize = 5;
|
|
||||||
const R_SUN: f32 = R_SCALE * N_TEETH_SUN as f32;
|
|
||||||
const R_SAT: f32 = R_SCALE * N_TEETH_SAT as f32;
|
|
||||||
const OFF_SAT: f32 = R_SUN + R_SAT;
|
|
||||||
const R_SKY: f32 = R_SCALE * N_TEETH_SKY as f32;
|
|
||||||
|
|
||||||
const FPS: f32 = 60.0;
|
|
||||||
|
|
||||||
with_2d_graphics(|| {
|
with_2d_graphics(|| {
|
||||||
let sun = gear_shape(N_TEETH_SUN);
|
let title = "Gear shape test";
|
||||||
let sat = gear_shape(N_TEETH_SAT);
|
if cfg!(events) {
|
||||||
let crown = gear_shape_outer(N_TEETH_SKY);
|
use futures::StreamExt;
|
||||||
|
use std::task::{Context, Poll, Waker};
|
||||||
|
|
||||||
let (canvas, mut events) = create_drawing_window_with_events("Canvas window");
|
let (canvas, mut events) = create_drawing_window_with_events(title);
|
||||||
let mut t = 0.0f32;
|
|
||||||
let mut angle_sun = 1.0 / N_TEETH_SUN as f32 * PI;
|
|
||||||
let mut angle_sky = 0.0;
|
|
||||||
let mut angle_sat = 0.0;
|
|
||||||
let mut angle_orbit = 0.0;
|
|
||||||
'main: loop {
|
'main: loop {
|
||||||
while let Poll::Ready(event) =
|
while let Poll::Ready(event) =
|
||||||
events.poll_next_unpin(&mut Context::from_waker(Waker::noop()))
|
events.poll_next_unpin(&mut Context::from_waker(Waker::noop()))
|
||||||
{
|
{
|
||||||
let Some(_event) = event else { break 'main };
|
let Some(_event) = event else { break 'main };
|
||||||
}
|
}
|
||||||
canvas.draw(|gc| {
|
canvas.draw(draw);
|
||||||
gc.clear_canvas(Rgba(0.1, 0.1, 0.1, 1.0));
|
|
||||||
|
|
||||||
gc.canvas_height(100.0);
|
|
||||||
gc.line_width(0.25);
|
|
||||||
|
|
||||||
let m = Mat2::from_angle(angle_sun);
|
|
||||||
gc.new_path();
|
|
||||||
for pt in &sun {
|
|
||||||
let pt = R_SUN * m * pt;
|
|
||||||
gc.line_to(pt.x, pt.y);
|
|
||||||
}
|
}
|
||||||
gc.close_path();
|
} else {
|
||||||
gc.stroke_color(Rgba(0.8, 0.7, 0.1, 1.0));
|
let canvas = create_drawing_window(title);
|
||||||
gc.stroke();
|
canvas.draw(draw);
|
||||||
|
|
||||||
let mr = Mat2::from_angle(angle_sat);
|
|
||||||
for k in 0..N_SAT {
|
|
||||||
let mo = Mat2::from_angle(angle_orbit + k as f32 / N_SAT as f32 * 2. * PI);
|
|
||||||
gc.new_path();
|
|
||||||
for pt in &sat {
|
|
||||||
let pt = mo * vec2(OFF_SAT, 0.) + R_SAT * mr * pt;
|
|
||||||
gc.line_to(pt.x, pt.y);
|
|
||||||
}
|
|
||||||
gc.close_path();
|
|
||||||
gc.stroke_color(Rgba(0.1, 0.8, 0.1, 1.0));
|
|
||||||
gc.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
let m = Mat2::from_angle(angle_sky);
|
|
||||||
gc.new_path();
|
|
||||||
for pt in &crown {
|
|
||||||
let pt = R_SKY * m * pt;
|
|
||||||
gc.line_to(pt.x, pt.y);
|
|
||||||
}
|
|
||||||
gc.close_path();
|
|
||||||
gc.stroke_color(Rgba(0.1, 0.4, 0.8, 1.0));
|
|
||||||
gc.stroke();
|
|
||||||
});
|
|
||||||
|
|
||||||
let v_sun: f32 = 1.0 + 2.0 * (0.007 * t).sin();
|
|
||||||
let v_sky: f32 = 1.0 - 2.0 * (0.003 * t).cos();
|
|
||||||
let v_sat: f32 = (v_sky - v_sun) / 2.0;
|
|
||||||
let v_orbit: f32 = (v_sun + v_sky) / 2.0;
|
|
||||||
|
|
||||||
let dt = 1.0 / FPS;
|
|
||||||
t += dt;
|
|
||||||
angle_sun += dt * v_sun / N_TEETH_SUN as f32;
|
|
||||||
angle_sky += dt * v_sky / N_TEETH_SKY as f32;
|
|
||||||
angle_sat += dt * v_sat / N_TEETH_SAT as f32;
|
|
||||||
angle_orbit += dt * v_orbit / (N_TEETH_SUN + N_TEETH_SAT) as f32;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user