From 191e646d689ebaa0f285e0b09a667978c941c01b Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Tue, 12 Aug 2025 20:01:16 +0200 Subject: [PATCH] refactor(math/line): Rename `NormalLine` to `LineInGeneralForm` The previous name sounded as if it was a normal (to some surface), which is not the case. --- src/layout/collect_bands.rs | 8 +++--- src/math/line.rs | 56 ++++++++++++++++++------------------- src/math/tangents.rs | 28 +++++++++---------- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/layout/collect_bands.rs b/src/layout/collect_bands.rs index d6cc418..66eebdb 100644 --- a/src/layout/collect_bands.rs +++ b/src/layout/collect_bands.rs @@ -26,7 +26,7 @@ use crate::{ }, graph::GetPetgraphIndex, layout::{Layout, NodeIndex}, - math::{intersect_linestring_and_ray, LineIntersection, NormalLine}, + math::{intersect_linestring_and_ray, LineInGeneralForm, LineIntersection}, }; impl Layout { @@ -45,7 +45,7 @@ impl Layout { to: right_pos, width: f64::EPSILON * 16.0, }; - let mut orig_hline = NormalLine::from(ltr_line); + let mut orig_hline = LineInGeneralForm::from(ltr_line); orig_hline.make_normal_unit(); let orig_hline = orig_hline; let location_denom = orig_hline.segment_interval(<r_line); @@ -78,7 +78,7 @@ impl Layout { let band_uid = self.drawing.loose_band_uid(loose).ok()?; let loose_hline = orig_hline.orthogonal_through(&match shape { PrimitiveShape::Seg(seg) => { - let seg_hline = NormalLine::from(seg.middle_line()); + let seg_hline = LineInGeneralForm::from(seg.middle_line()); match orig_hline.intersects(&seg_hline) { LineIntersection::Empty => return None, LineIntersection::Overlapping => shape.center(), @@ -92,7 +92,7 @@ impl Layout { shape.center() } }); - let location = (loose_hline.offset - location_start) / location_denom; + let location = (loose_hline.c - location_start) / location_denom; log::trace!( "intersection ({:?}) with {:?} is at {:?}", band_uid, diff --git a/src/math/line.rs b/src/math/line.rs index 984096f..400bf10 100644 --- a/src/math/line.rs +++ b/src/math/line.rs @@ -14,13 +14,13 @@ pub enum LineIntersection { /// A line in the normal form: `x0*y + y0*y + offset = 0`. #[derive(Clone, Copy, Debug, PartialEq)] -pub struct NormalLine { - pub x: f64, - pub y: f64, - pub offset: f64, +pub struct LineInGeneralForm { + pub a: f64, + pub b: f64, + pub c: f64, // On the same equation side as a and b, not on the other. } -impl From for NormalLine { +impl From for LineInGeneralForm { fn from(l: Line) -> Self { // the normal vector is perpendicular to the line let normal = point! { @@ -28,28 +28,28 @@ impl From for NormalLine { y: -l.dx(), }; Self { - x: normal.0.x, - y: normal.0.y, - offset: -perp_dot_product(l.end.into(), l.start.into()), + a: normal.0.x, + b: normal.0.y, + c: -perp_dot_product(l.end.into(), l.start.into()), } } } -impl NormalLine { +impl LineInGeneralForm { pub fn evaluate_at(&self, pt: Point) -> f64 { - self.x * pt.x() + self.y * pt.y() + self.offset + self.a * pt.x() + self.b * pt.y() + self.c } pub fn angle(&self) -> f64 { - self.y.atan2(self.x) + self.b.atan2(self.a) } pub fn make_normal_unit(&mut self) { - let normal_len = self.y.hypot(self.x); + let normal_len = self.b.hypot(self.a); if normal_len > (f64::EPSILON * 16.0) { - self.x /= normal_len; - self.y /= normal_len; - self.offset /= normal_len; + self.a /= normal_len; + self.b /= normal_len; + self.c /= normal_len; } } @@ -58,11 +58,11 @@ impl NormalLine { const ALMOST_ZERO: f64 = f64::EPSILON * 16.0; let (mut a, mut b) = (*self, *b); let _ = (a.make_normal_unit(), b.make_normal_unit()); - let apt = geo::point! { x: a.x, y: a.y }; - let bpt = geo::point! { x: b.x, y: b.y }; + let apt = geo::point! { x: a.a, y: a.b }; + let bpt = geo::point! { x: b.a, y: b.b }; let det = perp_dot_product(apt, bpt); - let rpx = b.y * a.offset - a.y * b.offset; - let rpy = -b.x * a.offset + a.x * b.offset; + let rpx = b.b * a.c - a.b * b.c; + let rpy = -b.a * a.c + a.a * b.c; if det.abs() > ALMOST_ZERO { LineIntersection::Point(geo::point! { x: rpx, y: rpy } / det) @@ -79,17 +79,17 @@ impl NormalLine { pub fn orthogonal_through(&self, pt: &Point) -> Self { Self { // recover the original parallel vector - x: -self.y, - y: self.x, - offset: self.x * pt.0.y - self.y * pt.0.x, + a: -self.b, + b: self.a, + c: self.a * pt.0.y - self.b * pt.0.x, } } pub fn segment_interval(&self, line: &Line) -> core::ops::RangeInclusive { // recover the original parallel vector let parv = geo::point! { - x: -self.y, - y: self.x, + x: -self.b, + y: self.a, }; dot_product(parv, line.start.into())..=dot_product(parv, line.end.into()) } @@ -106,8 +106,8 @@ impl NormalLine { /// Returns `Some(p)` when `p` lies in the intersection of the given lines. pub fn intersect_lines(line1: &Line, line2: &Line) -> Option { - let nline1 = NormalLine::from(*line1); - let nline2 = NormalLine::from(*line2); + let nline1 = LineInGeneralForm::from(*line1); + let nline2 = LineInGeneralForm::from(*line2); match nline1.intersects(&nline2) { LineIntersection::Empty | LineIntersection::Overlapping => None, @@ -139,8 +139,8 @@ pub fn intersect_lines(line1: &Line, line2: &Line) -> Option { /// Returns `Some(p)` when `p` lies in the intersection of a line and a ray /// (line which is only bounded at one side, i.e. point + directon) pub fn intersect_line_and_ray(line1: &Line, ray2: &Line) -> Option { - let nline1 = NormalLine::from(*line1); - let nray2 = NormalLine::from(*ray2); + let nline1 = LineInGeneralForm::from(*line1); + let nray2 = LineInGeneralForm::from(*ray2); match nline1.intersects(&nray2) { LineIntersection::Empty | LineIntersection::Overlapping => None, diff --git a/src/math/tangents.rs b/src/math/tangents.rs index 3b6f237..c6d2509 100644 --- a/src/math/tangents.rs +++ b/src/math/tangents.rs @@ -6,13 +6,13 @@ use geo::{geometry::Point, Line}; use specctra_core::math::Circle; use thiserror::Error; -use super::{seq_perp_dot_product, NormalLine, RotationSense}; +use super::{seq_perp_dot_product, LineInGeneralForm, RotationSense}; #[derive(Error, Debug, Clone, Copy, PartialEq)] #[error("no tangents for {0:?} and {1:?}")] // TODO add real error message pub struct NoTangents(pub Circle, pub Circle); -fn _tangent(center: Point, r1: f64, r2: f64) -> Result { +fn _tangent(center: Point, r1: f64, r2: f64) -> Result { let epsilon = 1e-9; let dr = r2 - r1; let norm = center.x() * center.x() + center.y() * center.y(); @@ -24,15 +24,15 @@ fn _tangent(center: Point, r1: f64, r2: f64) -> Result { let sqrt_discriminant = f64::sqrt(f64::abs(discriminant)); - Ok(NormalLine { - x: (center.x() * dr + center.y() * sqrt_discriminant) / norm, - y: (center.y() * dr - center.x() * sqrt_discriminant) / norm, - offset: r1, + Ok(LineInGeneralForm { + a: (center.x() * dr + center.y() * sqrt_discriminant) / norm, + b: (center.y() * dr - center.x() * sqrt_discriminant) / norm, + c: r1, }) } -fn _tangents(circle1: Circle, circle2: Circle) -> Result<[NormalLine; 4], ()> { - let mut tgs: [NormalLine; 4] = [ +fn _tangents(circle1: Circle, circle2: Circle) -> Result<[LineInGeneralForm; 4], ()> { + let mut tgs: [LineInGeneralForm; 4] = [ _tangent((circle2 - circle1).pos, -circle1.r, -circle2.r)?, _tangent((circle2 - circle1).pos, -circle1.r, circle2.r)?, _tangent((circle2 - circle1).pos, circle1.r, -circle2.r)?, @@ -40,18 +40,18 @@ fn _tangents(circle1: Circle, circle2: Circle) -> Result<[NormalLine; 4], ()> { ]; for tg in tgs.iter_mut() { - tg.offset -= tg.x * circle1.pos.x() + tg.y * circle1.pos.y(); + tg.c -= tg.a * circle1.pos.x() + tg.b * circle1.pos.y(); } Ok(tgs) } -fn cast_point_to_canonical_line(pt: Point, line: NormalLine) -> Point { +fn cast_point_to_canonical_line(pt: Point, line: LineInGeneralForm) -> Point { ( - (line.y * (line.y * pt.x() - line.x * pt.y()) - line.x * line.offset) - / (line.x * line.x + line.y * line.y), - (line.x * (-line.y * pt.x() + line.x * pt.y()) - line.y * line.offset) - / (line.x * line.x + line.y * line.y), + (line.b * (line.b * pt.x() - line.a * pt.y()) - line.a * line.c) + / (line.a * line.a + line.b * line.b), + (line.a * (-line.b * pt.x() + line.a * pt.y()) - line.b * line.c) + / (line.a * line.a + line.b * line.b), ) .into() }