mirror of https://codeberg.org/topola/topola.git
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.
This commit is contained in:
parent
987a4c6e9e
commit
191e646d68
|
|
@ -26,7 +26,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
graph::GetPetgraphIndex,
|
graph::GetPetgraphIndex,
|
||||||
layout::{Layout, NodeIndex},
|
layout::{Layout, NodeIndex},
|
||||||
math::{intersect_linestring_and_ray, LineIntersection, NormalLine},
|
math::{intersect_linestring_and_ray, LineInGeneralForm, LineIntersection},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<R: AccessRules> Layout<R> {
|
impl<R: AccessRules> Layout<R> {
|
||||||
|
|
@ -45,7 +45,7 @@ impl<R: AccessRules> Layout<R> {
|
||||||
to: right_pos,
|
to: right_pos,
|
||||||
width: f64::EPSILON * 16.0,
|
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();
|
orig_hline.make_normal_unit();
|
||||||
let orig_hline = orig_hline;
|
let orig_hline = orig_hline;
|
||||||
let location_denom = orig_hline.segment_interval(<r_line);
|
let location_denom = orig_hline.segment_interval(<r_line);
|
||||||
|
|
@ -78,7 +78,7 @@ impl<R: AccessRules> Layout<R> {
|
||||||
let band_uid = self.drawing.loose_band_uid(loose).ok()?;
|
let band_uid = self.drawing.loose_band_uid(loose).ok()?;
|
||||||
let loose_hline = orig_hline.orthogonal_through(&match shape {
|
let loose_hline = orig_hline.orthogonal_through(&match shape {
|
||||||
PrimitiveShape::Seg(seg) => {
|
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) {
|
match orig_hline.intersects(&seg_hline) {
|
||||||
LineIntersection::Empty => return None,
|
LineIntersection::Empty => return None,
|
||||||
LineIntersection::Overlapping => shape.center(),
|
LineIntersection::Overlapping => shape.center(),
|
||||||
|
|
@ -92,7 +92,7 @@ impl<R: AccessRules> Layout<R> {
|
||||||
shape.center()
|
shape.center()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let location = (loose_hline.offset - location_start) / location_denom;
|
let location = (loose_hline.c - location_start) / location_denom;
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"intersection ({:?}) with {:?} is at {:?}",
|
"intersection ({:?}) with {:?} is at {:?}",
|
||||||
band_uid,
|
band_uid,
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ pub enum LineIntersection {
|
||||||
|
|
||||||
/// A line in the normal form: `x0*y + y0*y + offset = 0`.
|
/// A line in the normal form: `x0*y + y0*y + offset = 0`.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct NormalLine {
|
pub struct LineInGeneralForm {
|
||||||
pub x: f64,
|
pub a: f64,
|
||||||
pub y: f64,
|
pub b: f64,
|
||||||
pub offset: f64,
|
pub c: f64, // On the same equation side as a and b, not on the other.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Line> for NormalLine {
|
impl From<Line> for LineInGeneralForm {
|
||||||
fn from(l: Line) -> Self {
|
fn from(l: Line) -> Self {
|
||||||
// the normal vector is perpendicular to the line
|
// the normal vector is perpendicular to the line
|
||||||
let normal = point! {
|
let normal = point! {
|
||||||
|
|
@ -28,28 +28,28 @@ impl From<Line> for NormalLine {
|
||||||
y: -l.dx(),
|
y: -l.dx(),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
x: normal.0.x,
|
a: normal.0.x,
|
||||||
y: normal.0.y,
|
b: normal.0.y,
|
||||||
offset: -perp_dot_product(l.end.into(), l.start.into()),
|
c: -perp_dot_product(l.end.into(), l.start.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NormalLine {
|
impl LineInGeneralForm {
|
||||||
pub fn evaluate_at(&self, pt: Point) -> f64 {
|
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 {
|
pub fn angle(&self) -> f64 {
|
||||||
self.y.atan2(self.x)
|
self.b.atan2(self.a)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_normal_unit(&mut self) {
|
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) {
|
if normal_len > (f64::EPSILON * 16.0) {
|
||||||
self.x /= normal_len;
|
self.a /= normal_len;
|
||||||
self.y /= normal_len;
|
self.b /= normal_len;
|
||||||
self.offset /= normal_len;
|
self.c /= normal_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,11 +58,11 @@ impl NormalLine {
|
||||||
const ALMOST_ZERO: f64 = f64::EPSILON * 16.0;
|
const ALMOST_ZERO: f64 = f64::EPSILON * 16.0;
|
||||||
let (mut a, mut b) = (*self, *b);
|
let (mut a, mut b) = (*self, *b);
|
||||||
let _ = (a.make_normal_unit(), b.make_normal_unit());
|
let _ = (a.make_normal_unit(), b.make_normal_unit());
|
||||||
let apt = geo::point! { x: a.x, y: a.y };
|
let apt = geo::point! { x: a.a, y: a.b };
|
||||||
let bpt = geo::point! { x: b.x, y: b.y };
|
let bpt = geo::point! { x: b.a, y: b.b };
|
||||||
let det = perp_dot_product(apt, bpt);
|
let det = perp_dot_product(apt, bpt);
|
||||||
let rpx = b.y * a.offset - a.y * b.offset;
|
let rpx = b.b * a.c - a.b * b.c;
|
||||||
let rpy = -b.x * a.offset + a.x * b.offset;
|
let rpy = -b.a * a.c + a.a * b.c;
|
||||||
|
|
||||||
if det.abs() > ALMOST_ZERO {
|
if det.abs() > ALMOST_ZERO {
|
||||||
LineIntersection::Point(geo::point! { x: rpx, y: rpy } / det)
|
LineIntersection::Point(geo::point! { x: rpx, y: rpy } / det)
|
||||||
|
|
@ -79,17 +79,17 @@ impl NormalLine {
|
||||||
pub fn orthogonal_through(&self, pt: &Point) -> Self {
|
pub fn orthogonal_through(&self, pt: &Point) -> Self {
|
||||||
Self {
|
Self {
|
||||||
// recover the original parallel vector
|
// recover the original parallel vector
|
||||||
x: -self.y,
|
a: -self.b,
|
||||||
y: self.x,
|
b: self.a,
|
||||||
offset: self.x * pt.0.y - self.y * pt.0.x,
|
c: self.a * pt.0.y - self.b * pt.0.x,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn segment_interval(&self, line: &Line) -> core::ops::RangeInclusive<f64> {
|
pub fn segment_interval(&self, line: &Line) -> core::ops::RangeInclusive<f64> {
|
||||||
// recover the original parallel vector
|
// recover the original parallel vector
|
||||||
let parv = geo::point! {
|
let parv = geo::point! {
|
||||||
x: -self.y,
|
x: -self.b,
|
||||||
y: self.x,
|
y: self.a,
|
||||||
};
|
};
|
||||||
dot_product(parv, line.start.into())..=dot_product(parv, line.end.into())
|
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.
|
/// Returns `Some(p)` when `p` lies in the intersection of the given lines.
|
||||||
pub fn intersect_lines(line1: &Line, line2: &Line) -> Option<Point> {
|
pub fn intersect_lines(line1: &Line, line2: &Line) -> Option<Point> {
|
||||||
let nline1 = NormalLine::from(*line1);
|
let nline1 = LineInGeneralForm::from(*line1);
|
||||||
let nline2 = NormalLine::from(*line2);
|
let nline2 = LineInGeneralForm::from(*line2);
|
||||||
|
|
||||||
match nline1.intersects(&nline2) {
|
match nline1.intersects(&nline2) {
|
||||||
LineIntersection::Empty | LineIntersection::Overlapping => None,
|
LineIntersection::Empty | LineIntersection::Overlapping => None,
|
||||||
|
|
@ -139,8 +139,8 @@ pub fn intersect_lines(line1: &Line, line2: &Line) -> Option<Point> {
|
||||||
/// Returns `Some(p)` when `p` lies in the intersection of a line and a ray
|
/// 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)
|
/// (line which is only bounded at one side, i.e. point + directon)
|
||||||
pub fn intersect_line_and_ray(line1: &Line, ray2: &Line) -> Option<Point> {
|
pub fn intersect_line_and_ray(line1: &Line, ray2: &Line) -> Option<Point> {
|
||||||
let nline1 = NormalLine::from(*line1);
|
let nline1 = LineInGeneralForm::from(*line1);
|
||||||
let nray2 = NormalLine::from(*ray2);
|
let nray2 = LineInGeneralForm::from(*ray2);
|
||||||
|
|
||||||
match nline1.intersects(&nray2) {
|
match nline1.intersects(&nray2) {
|
||||||
LineIntersection::Empty | LineIntersection::Overlapping => None,
|
LineIntersection::Empty | LineIntersection::Overlapping => None,
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ use geo::{geometry::Point, Line};
|
||||||
use specctra_core::math::Circle;
|
use specctra_core::math::Circle;
|
||||||
use thiserror::Error;
|
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)]
|
#[derive(Error, Debug, Clone, Copy, PartialEq)]
|
||||||
#[error("no tangents for {0:?} and {1:?}")] // TODO add real error message
|
#[error("no tangents for {0:?} and {1:?}")] // TODO add real error message
|
||||||
pub struct NoTangents(pub Circle, pub Circle);
|
pub struct NoTangents(pub Circle, pub Circle);
|
||||||
|
|
||||||
fn _tangent(center: Point, r1: f64, r2: f64) -> Result<NormalLine, ()> {
|
fn _tangent(center: Point, r1: f64, r2: f64) -> Result<LineInGeneralForm, ()> {
|
||||||
let epsilon = 1e-9;
|
let epsilon = 1e-9;
|
||||||
let dr = r2 - r1;
|
let dr = r2 - r1;
|
||||||
let norm = center.x() * center.x() + center.y() * center.y();
|
let norm = center.x() * center.x() + center.y() * center.y();
|
||||||
|
|
@ -24,15 +24,15 @@ fn _tangent(center: Point, r1: f64, r2: f64) -> Result<NormalLine, ()> {
|
||||||
|
|
||||||
let sqrt_discriminant = f64::sqrt(f64::abs(discriminant));
|
let sqrt_discriminant = f64::sqrt(f64::abs(discriminant));
|
||||||
|
|
||||||
Ok(NormalLine {
|
Ok(LineInGeneralForm {
|
||||||
x: (center.x() * dr + center.y() * sqrt_discriminant) / norm,
|
a: (center.x() * dr + center.y() * sqrt_discriminant) / norm,
|
||||||
y: (center.y() * dr - center.x() * sqrt_discriminant) / norm,
|
b: (center.y() * dr - center.x() * sqrt_discriminant) / norm,
|
||||||
offset: r1,
|
c: r1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _tangents(circle1: Circle, circle2: Circle) -> Result<[NormalLine; 4], ()> {
|
fn _tangents(circle1: Circle, circle2: Circle) -> Result<[LineInGeneralForm; 4], ()> {
|
||||||
let mut tgs: [NormalLine; 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)?,
|
_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() {
|
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)
|
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.b * (line.b * pt.x() - line.a * pt.y()) - line.a * line.c)
|
||||||
/ (line.x * line.x + line.y * line.y),
|
/ (line.a * line.a + line.b * line.b),
|
||||||
(line.x * (-line.y * pt.x() + line.x * pt.y()) - line.y * line.offset)
|
(line.a * (-line.b * pt.x() + line.a * pt.y()) - line.b * line.c)
|
||||||
/ (line.x * line.x + line.y * line.y),
|
/ (line.a * line.a + line.b * line.b),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue