From 41468d51c39f678a5d2d0af4b87c4c07035b7ffe Mon Sep 17 00:00:00 2001 From: Ellen Emilia Anna Zscheile Date: Mon, 10 Feb 2025 10:31:40 +0100 Subject: [PATCH] refactor: merge CanonicalLine and HomogeneousLine into NormalLine (they represent the exact same object, modulo sign of offset) --- src/layout/layout.rs | 6 +++--- src/math/mod.rs | 27 ++++++++++++++------------- src/math/tangents.rs | 35 ++++++++++++++--------------------- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/layout/layout.rs b/src/layout/layout.rs index 6a73edd..15f3c1d 100644 --- a/src/layout/layout.rs +++ b/src/layout/layout.rs @@ -39,7 +39,7 @@ use crate::{ poly::{MakePolygon, Poly, PolyWeight}, via::{Via, ViaWeight}, }, - math::{HomogeneousLine, LineIntersection}, + math::{LineIntersection, NormalLine}, }; /// Represents a weight for various compounds @@ -388,7 +388,7 @@ impl Layout { to: right_pos.into(), width: f64::EPSILON * 16.0, }; - let mut orig_hline = HomogeneousLine::from(ltr_line); + let mut orig_hline = NormalLine::from(ltr_line); orig_hline.make_normal_unit(); let orig_hline = orig_hline; let location_denom = orig_hline.segment_interval(<r_line); @@ -422,7 +422,7 @@ impl Layout { let band_uid = self.drawing.loose_band_uid(loose); let loose_hline = orig_hline.orthogonal_through(&match shape { PrimitiveShape::Seg(seg) => { - let seg_hline = HomogeneousLine::from(seg.middle_line()); + let seg_hline = NormalLine::from(seg.middle_line()); match orig_hline.intersects(&seg_hline) { LineIntersection::Empty => return None, LineIntersection::Overlapping => shape.center(), diff --git a/src/math/mod.rs b/src/math/mod.rs index 240ae70..118b0a1 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -16,15 +16,15 @@ pub enum LineIntersection { Point(Point), } -#[derive(Clone, Copy, Debug)] -pub struct HomogeneousLine { - // x0*x + y0*y = offset - pub x: T, - pub y: T, - pub offset: T, +/// A line in 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, } -impl From for HomogeneousLine { +impl From for NormalLine { fn from(l: Line) -> Self { // the normal vector is perpendicular to the line let normal = geo::point! { @@ -34,14 +34,14 @@ impl From for HomogeneousLine { Self { x: normal.0.x, y: normal.0.y, - offset: perp_dot_product(l.end.into(), l.start.into()), + offset: -perp_dot_product(l.end.into(), l.start.into()), } } } -impl HomogeneousLine { +impl NormalLine { pub fn evaluate_at(&self, pt: Point) -> f64 { - self.x * pt.x() + self.y * pt.y() - self.offset + self.x * pt.x() + self.y * pt.y() + self.offset } pub fn angle(&self) -> f64 { @@ -53,6 +53,7 @@ impl HomogeneousLine { if normal_len > (f64::EPSILON * 16.0) { self.x /= normal_len; self.y /= normal_len; + self.offset /= normal_len; } } @@ -63,8 +64,8 @@ impl HomogeneousLine { let apt = geo::point! { x: a.x, y: a.y }; let bpt = geo::point! { x: b.x, y: b.y }; 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.y * a.offset + a.y * b.offset; + let rpy = b.x * a.offset - a.x * b.offset; if det.abs() > ALMOST_ZERO { LineIntersection::Point(geo::point! { x: rpx, y: rpy } / det) @@ -83,7 +84,7 @@ impl HomogeneousLine { // recover the original parallel vector x: -self.y, y: self.x, - offset: self.x * pt.0.y - self.y * pt.0.x, + offset: -self.x * pt.0.y + self.y * pt.0.x, } } diff --git a/src/math/tangents.rs b/src/math/tangents.rs index 16e8d37..ee4e0f5 100644 --- a/src/math/tangents.rs +++ b/src/math/tangents.rs @@ -6,20 +6,13 @@ use geo::{geometry::Point, Line}; use specctra_core::math::Circle; use thiserror::Error; -use super::seq_perp_dot_product; +use super::{seq_perp_dot_product, NormalLine}; #[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); -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct CanonicalLine { - pub a: f64, - pub b: f64, - pub c: f64, -} - -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(); @@ -31,15 +24,15 @@ fn _tangent(center: Point, r1: f64, r2: f64) -> Result { let sqrt_discriminant = f64::sqrt(f64::abs(discriminant)); - Ok(CanonicalLine { - a: (center.x() * dr + center.y() * sqrt_discriminant) / norm, - b: (center.y() * dr - center.x() * sqrt_discriminant) / norm, - c: r1, + Ok(NormalLine { + x: (center.x() * dr + center.y() * sqrt_discriminant) / norm, + y: (center.y() * dr - center.x() * sqrt_discriminant) / norm, + offset: r1, }) } -fn _tangents(circle1: Circle, circle2: Circle) -> Result<[CanonicalLine; 4], ()> { - let mut tgs: [CanonicalLine; 4] = [ +fn _tangents(circle1: Circle, circle2: Circle) -> Result<[NormalLine; 4], ()> { + let mut tgs: [NormalLine; 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)?, @@ -47,18 +40,18 @@ fn _tangents(circle1: Circle, circle2: Circle) -> Result<[CanonicalLine; 4], ()> ]; for tg in tgs.iter_mut() { - tg.c -= tg.a * circle1.pos.x() + tg.b * circle1.pos.y(); + tg.offset -= tg.x * circle1.pos.x() + tg.y * circle1.pos.y(); } Ok(tgs) } -fn cast_point_to_canonical_line(pt: Point, line: CanonicalLine) -> Point { +fn cast_point_to_canonical_line(pt: Point, line: NormalLine) -> Point { ( - (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), + (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), ) .into() }