diff --git a/src/bevel_gear.rs b/src/bevel_gear.rs index c8173f8..d109e12 100644 --- a/src/bevel_gear.rs +++ b/src/bevel_gear.rs @@ -70,19 +70,30 @@ impl BevelGearShape { let angle = 2.0 * PI / self.points.len() as f32; for (k, pt) in self.points.iter_mut().enumerate() { let dir = Vec2::from_angle(k as f32 * angle); - for line in cutting_line.iter().zip(&cutting_line[1..]) { - if line.0.dot(dir) <= 0. || line.1.dot(dir) <= 0. { - continue; + for (&a, &b) in cutting_line.iter().zip(&cutting_line[1..]) { + if let Some(intersect) = intersect_ray_segment(dir, (a, b)) + && intersect > 0. + { + *pt = pt.min(intersect); } - if line.0.dot(dir.perp()) < 0. || line.1.dot(dir.perp()) > 0. { - continue; - } - *pt = pt.min(f32::min(line.0.length(), line.1.length())); } } } } +/// Computes the intersection point of a line (from the origin along `dir`) and a line segment (from `a` to `b`). +/// Returns the distance from the origin, in multiples of `dir`. +fn intersect_ray_segment(dir: Vec2, (a, b): (Vec2, Vec2)) -> Option { + const EPS: f32 = 1e-4; + let den = dir.perp_dot(b - a); + if den.abs() < EPS { + // parallel or almost parallel + return None; + } + let u = a.perp_dot(dir) / den; // intersection = a + u * (b - a) + (0.0..1.0).contains(&u).then(|| a.perp_dot(b - a) / den) +} + #[derive(Debug, Clone, Copy)] pub enum BevelGearCap { Flat,