use embassy_ssd1306_graphics::Graphics;
use embedded_hal_async::i2c::I2c;
use crate::draw_utils::{segment, ring, filled_disk};
const TAU_FP: i32 = 6434;
#[derive(Clone, Copy, Debug)]
pub struct Gear {
pub cx: i32,
pub cy: i32,
pub pitch_r: i32,
pub teeth: i32,
pub tooth_h: i32,
pub tooth_w_pct: i32,
pub hub_r: i32,
}
impl Gear {
#[inline]
pub fn new(
cx: i32, cy: i32,
pitch_r: i32,
teeth: i32,
tooth_h: i32,
tooth_w_pct: i32,
hub_r: i32,
) -> Self {
Self { cx, cy, pitch_r, teeth, tooth_h, tooth_w_pct: tooth_w_pct.max(1).min(99), hub_r }
}
pub fn draw<I: I2c>(
&self,
gfx: &mut Graphics<'_, I>,
angle: f32,
on: bool,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) {
if self.teeth < 3 { return; }
let outer_r = self.pitch_r + self.tooth_h;
ring(gfx, self.cx, self.cy, self.pitch_r, on);
let step = core::f32::consts::TAU / (self.teeth as f32);
let half_tooth_angle = step * (self.tooth_w_pct as f32) / 200.0;
for i in 0..self.teeth {
let center_angle = angle + (i as f32) * step;
let a_left = center_angle - half_tooth_angle;
let a_right = center_angle + half_tooth_angle;
let (blx, bly) = polar_px(self.cx, self.cy, self.pitch_r, a_left, cos_fn, sin_fn);
let (brx, bry) = polar_px(self.cx, self.cy, self.pitch_r, a_right, cos_fn, sin_fn);
let (tlx, tly) = polar_px(self.cx, self.cy, outer_r, a_left, cos_fn, sin_fn);
let (trx, try_) = polar_px(self.cx, self.cy, outer_r, a_right, cos_fn, sin_fn);
segment(gfx, blx, bly, tlx, tly, on);
segment(gfx, brx, bry, trx, try_, on);
segment(gfx, tlx, tly, trx, try_, on);
let a_next_left = center_angle + step - half_tooth_angle;
draw_arc(gfx, self.cx, self.cy, self.pitch_r,
a_right, a_next_left, on, cos_fn, sin_fn);
}
if self.hub_r > 0 {
ring(gfx, self.cx, self.cy, self.hub_r, on);
gfx.pixel(self.cx, self.cy, on);
} else {
filled_disk(gfx, self.cx, self.cy, 2, on);
}
}
#[inline]
pub fn erase<I: I2c>(
&self,
gfx: &mut Graphics<'_, I>,
angle: f32,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) {
self.draw(gfx, angle, false, cos_fn, sin_fn);
}
#[inline]
pub fn outer_r(&self) -> i32 {
self.pitch_r + self.tooth_h
}
}
#[derive(Clone, Copy, Debug)]
pub struct GearPair {
pub g1: Gear,
pub g2: Gear,
pub axis_angle: f32,
}
impl GearPair {
pub fn new(
g1: Gear,
mut g2: Gear,
axis_angle: f32,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) -> Self {
let dist = g1.pitch_r + g2.pitch_r;
g2.cx = g1.cx + ((dist as f32) * cos_fn(axis_angle) + 0.5) as i32;
g2.cy = g1.cy + ((dist as f32) * sin_fn(axis_angle) + 0.5) as i32;
Self { g1, g2, axis_angle }
}
pub fn draw<I: I2c>(
&self,
gfx: &mut Graphics<'_, I>,
angle1: f32,
on: bool,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) {
let ratio = (self.g1.teeth as f32) / (self.g2.teeth as f32);
let phase2 = self.axis_angle + core::f32::consts::PI / (self.g2.teeth as f32);
let angle2 = phase2 - angle1 * ratio;
self.g1.draw(gfx, angle1, on, cos_fn, sin_fn);
self.g2.draw(gfx, angle2, on, cos_fn, sin_fn);
}
#[inline]
pub fn erase<I: I2c>(
&self,
gfx: &mut Graphics<'_, I>,
angle1: f32,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) {
self.draw(gfx, angle1, false, cos_fn, sin_fn);
}
}
#[derive(Clone, Copy, Debug)]
pub struct GearTrain {
pub g1: Gear,
pub g2: Gear,
pub g3: Gear,
pub axis_angle: f32,
}
impl GearTrain {
pub fn new(
g1: Gear,
mut g2: Gear,
mut g3: Gear,
axis_angle: f32,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) -> Self {
let d12 = g1.pitch_r + g2.pitch_r;
g2.cx = g1.cx + ((d12 as f32) * cos_fn(axis_angle) + 0.5) as i32;
g2.cy = g1.cy + ((d12 as f32) * sin_fn(axis_angle) + 0.5) as i32;
let d23 = g2.pitch_r + g3.pitch_r;
g3.cx = g2.cx + ((d23 as f32) * cos_fn(axis_angle) + 0.5) as i32;
g3.cy = g2.cy + ((d23 as f32) * sin_fn(axis_angle) + 0.5) as i32;
Self { g1, g2, g3, axis_angle }
}
pub fn draw<I: I2c>(
&self,
gfx: &mut Graphics<'_, I>,
angle1: f32,
on: bool,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) {
let r12 = (self.g1.teeth as f32) / (self.g2.teeth as f32);
let phase2 = self.axis_angle + core::f32::consts::PI / (self.g2.teeth as f32);
let angle2 = phase2 - angle1 * r12;
let r23 = (self.g2.teeth as f32) / (self.g3.teeth as f32);
let phase3 = self.axis_angle + core::f32::consts::PI / (self.g3.teeth as f32);
let angle3 = phase3 - angle2 * r23;
self.g1.draw(gfx, angle1, on, cos_fn, sin_fn);
self.g2.draw(gfx, angle2, on, cos_fn, sin_fn);
self.g3.draw(gfx, angle3, on, cos_fn, sin_fn);
}
#[inline]
pub fn erase<I: I2c>(
&self,
gfx: &mut Graphics<'_, I>,
angle1: f32,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) {
self.draw(gfx, angle1, false, cos_fn, sin_fn);
}
}
#[inline]
fn polar_px(
cx: i32, cy: i32, r: i32, angle: f32,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) -> (i32, i32) {
let x = cx + ((r as f32) * cos_fn(angle) + 0.5) as i32;
let y = cy + ((r as f32) * sin_fn(angle) + 0.5) as i32;
(x, y)
}
fn draw_arc<I: I2c>(
gfx: &mut Graphics<'_, I>,
cx: i32, cy: i32, r: i32,
a_start: f32, a_end: f32,
on: bool,
cos_fn: fn(f32) -> f32,
sin_fn: fn(f32) -> f32,
) {
let span = a_end - a_start;
if span <= 0.0 { return; }
let steps = ((r as f32) * span + 1.5) as i32;
if steps <= 0 { return; }
for i in 0..=steps {
let a = a_start + span * (i as f32) / (steps as f32);
let x = cx + ((r as f32) * cos_fn(a) + 0.5) as i32;
let y = cy + ((r as f32) * sin_fn(a) + 0.5) as i32;
gfx.pixel(x, y, on);
}
}
#[allow(dead_code)]
const _: i32 = TAU_FP;