refactor(math): Replace `cw` booleans with `RotationSense` enums

The term "cw" (clockwise) was somewhat cryptic, using a more verbose
term improves readability.
This commit is contained in:
Mikolaj Wielgus 2025-04-29 13:19:59 +02:00
parent 640247a675
commit b000f2b7d8
9 changed files with 144 additions and 95 deletions

View File

@ -21,7 +21,7 @@ use topola::{
geometry::{shape::AccessShape, GenericNode},
graph::MakeRef,
layout::{poly::MakePolygon, via::ViaWeight},
math::Circle,
math::{Circle, RotationSense},
};
use crate::{config::Config, menu_bar::MenuBar, painter::Painter, workspace::Workspace};
@ -213,23 +213,25 @@ impl Viewport {
.shape()
.center();
if let Some(from_cw) =
navmesh.node_weight(edge.source()).unwrap().maybe_cw
if let Some(from_sense) =
navmesh.node_weight(edge.source()).unwrap().maybe_sense
{
if from_cw {
from -= [0.0, 150.0].into();
} else {
from += [0.0, 150.0].into();
}
from += match from_sense {
RotationSense::Counterclockwise => {
[0.0, 150.0].into()
}
RotationSense::Clockwise => [-0.0, -150.0].into(),
};
}
if let Some(to_cw) =
navmesh.node_weight(edge.target()).unwrap().maybe_cw
if let Some(to_sense) =
navmesh.node_weight(edge.target()).unwrap().maybe_sense
{
if to_cw {
to -= [0.0, 150.0].into();
} else {
to += [0.0, 150.0].into();
to += match to_sense {
RotationSense::Counterclockwise => {
[0.0, 150.0].into()
}
RotationSense::Clockwise => [-0.0, -150.0].into(),
}
}

View File

@ -10,14 +10,6 @@ use core::fmt;
use rstar::{RTree, AABB};
use thiserror::Error;
use crate::geometry::{
edit::{ApplyGeometryEdit, GeometryEdit},
primitive::{AccessPrimitiveShape, PrimitiveShape},
recording_with_rtree::RecordingGeometryWithRtree,
with_rtree::BboxedIndex,
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
GetLayer, GetOffset, GetSetPos, GetWidth,
};
use crate::graph::{GenericIndex, GetPetgraphIndex};
use crate::math::NoTangents;
use crate::{
@ -42,6 +34,17 @@ use crate::{
},
graph::MakeRef,
};
use crate::{
geometry::{
edit::{ApplyGeometryEdit, GeometryEdit},
primitive::{AccessPrimitiveShape, PrimitiveShape},
recording_with_rtree::RecordingGeometryWithRtree,
with_rtree::BboxedIndex,
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
GetLayer, GetOffset, GetSetPos, GetWidth,
},
math::RotationSense,
};
#[derive(Clone, Copy, Error)]
pub enum DrawingException {
@ -498,7 +501,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
dot_weight: LooseDotWeight,
seg_weight: SeqLooseSegWeight,
bend_weight: LooseBendWeight,
cw: bool,
sense: RotationSense,
) -> Result<Cane, DrawingException> {
let maybe_next_gear = around.ref_(self).next_gear();
let cane = self.add_cane_with_infringables(
@ -508,7 +511,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
dot_weight,
seg_weight,
bend_weight,
cw,
sense,
Some(&[]),
)?;
@ -551,14 +554,34 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
let (from, to, offset) = if let Some(inner) = rail_primitive.inner() {
let inner = inner.into();
let from = self.head_around_bend_segment(&from_head, inner, true, width)?;
let to = self.head_around_bend_segment(&to_head, inner, false, width)?;
let from = self.head_around_bend_segment(
&from_head,
inner,
RotationSense::Clockwise,
width,
)?;
let to = self.head_around_bend_segment(
&to_head,
inner,
RotationSense::Counterclockwise,
width,
)?;
let offset = self.head_around_bend_offset(&from_head, inner, width);
(from, to, offset)
} else {
let core = rail_primitive.core().into();
let from = self.head_around_dot_segment(&from_head, core, true, width)?;
let to = self.head_around_dot_segment(&to_head, core, false, width)?;
let from = self.head_around_dot_segment(
&from_head,
core,
RotationSense::Clockwise,
width,
)?;
let to = self.head_around_dot_segment(
&to_head,
core,
RotationSense::Counterclockwise,
width,
)?;
let offset = self.head_around_dot_offset(&from_head, core, width);
(from, to, offset)
};
@ -621,7 +644,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
dot_weight: LooseDotWeight,
seg_weight: SeqLooseSegWeight,
bend_weight: LooseBendWeight,
cw: bool,
sense: RotationSense,
) -> Result<Cane, DrawingException> {
self.add_cane_with_infringables(
recorder,
@ -630,7 +653,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
dot_weight,
seg_weight,
bend_weight,
cw,
sense,
Some(&[]),
)
}
@ -647,7 +670,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
dot_weight: LooseDotWeight,
seg_weight: SeqLooseSegWeight,
bend_weight: LooseBendWeight,
cw: bool,
sense: RotationSense,
infringables: Option<&[PrimitiveIndex]>,
) -> Result<Cane, DrawingException> {
let seg_to = self.add_dot_with_infringables(recorder, dot_weight, infringables)?;
@ -667,7 +690,10 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
.remove_dot(recorder, seg_to.into());
})?;
let (bend_from, bend_to) = if cw { (to, seg_to) } else { (seg_to, to) };
let (bend_from, bend_to) = match sense {
RotationSense::Counterclockwise => (seg_to, to),
RotationSense::Clockwise => (to, seg_to),
};
let bend = self
.add_loose_bend_with_infringables(

View File

@ -9,7 +9,7 @@ use crate::{
primitive::{AccessPrimitiveShape, PrimitiveShape},
shape::AccessShape,
},
math::{self, Circle, NoTangents},
math::{self, Circle, NoTangents, RotationSense},
};
use super::{
@ -41,7 +41,7 @@ pub trait Guide {
&self,
head: &Head,
around: DotIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<Line, NoTangents>;
@ -58,13 +58,13 @@ pub trait Guide {
&self,
head: &Head,
around: BendIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<Line, NoTangents>;
fn head_around_bend_offset(&self, head: &Head, around: BendIndex, _width: f64) -> f64;
fn head_cw(&self, head: &Head) -> Option<bool>;
fn head_sense(&self, head: &Head) -> Option<RotationSense>;
fn cane_head(&self, face: LooseDotIndex) -> CaneHead;
@ -86,8 +86,8 @@ impl<CW: Copy, R: AccessRules> Guide for Drawing<CW, R> {
r: 0.0,
};
let from_cw = self.head_cw(head);
math::tangent_segment(from_circle, from_cw, to_circle, None)
let from_sense = self.head_sense(head);
math::tangent_segment(from_circle, from_sense, to_circle, None)
}
fn head_around_dot_segments(
@ -100,9 +100,9 @@ impl<CW: Copy, R: AccessRules> Guide for Drawing<CW, R> {
let to_circle =
self.dot_circle(around, width, self.conditions(head.face().into()).as_ref());
let from_cw = self.head_cw(head);
let from_sense = self.head_sense(head);
let tangents: Vec<Line> =
math::tangent_segments(from_circle, from_cw, to_circle, None)?.collect();
math::tangent_segments(from_circle, from_sense, to_circle, None)?.collect();
Ok((tangents[0], tangents[1]))
}
@ -110,15 +110,15 @@ impl<CW: Copy, R: AccessRules> Guide for Drawing<CW, R> {
&self,
head: &Head,
around: DotIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<Line, NoTangents> {
let from_circle = self.head_circle(head, width);
let to_circle =
self.dot_circle(around, width, self.conditions(head.face().into()).as_ref());
let from_cw = self.head_cw(head);
math::tangent_segment(from_circle, from_cw, to_circle, Some(cw))
let from_sense = self.head_sense(head);
math::tangent_segment(from_circle, from_sense, to_circle, Some(sense))
}
fn head_around_dot_offset(&self, head: &Head, around: DotIndex, _width: f64) -> f64 {
@ -138,9 +138,9 @@ impl<CW: Copy, R: AccessRules> Guide for Drawing<CW, R> {
let to_circle =
self.bend_circle(around, width, self.conditions(head.face().into()).as_ref());
let from_cw = self.head_cw(head);
let from_sense = self.head_sense(head);
let tangents: Vec<Line> =
math::tangent_segments(from_circle, from_cw, to_circle, None)?.collect();
math::tangent_segments(from_circle, from_sense, to_circle, None)?.collect();
Ok((tangents[0], tangents[1]))
}
@ -148,15 +148,15 @@ impl<CW: Copy, R: AccessRules> Guide for Drawing<CW, R> {
&self,
head: &Head,
around: BendIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<Line, NoTangents> {
let from_circle = self.head_circle(head, width);
let to_circle =
self.bend_circle(around, width, self.conditions(head.face().into()).as_ref());
let from_cw = self.head_cw(head);
math::tangent_segment(from_circle, from_cw, to_circle, Some(cw))
let from_sense = self.head_sense(head);
math::tangent_segment(from_circle, from_sense, to_circle, Some(sense))
}
fn head_around_bend_offset(&self, head: &Head, around: BendIndex, _width: f64) -> f64 {
@ -166,14 +166,14 @@ impl<CW: Copy, R: AccessRules> Guide for Drawing<CW, R> {
)
}
fn head_cw(&self, head: &Head) -> Option<bool> {
fn head_sense(&self, head: &Head) -> Option<RotationSense> {
if let Head::Cane(head) = head {
let joints = self.primitive(head.cane.bend).joints();
if head.face() == joints.0.into() {
Some(false)
Some(RotationSense::Counterclockwise)
} else {
Some(true)
Some(RotationSense::Clockwise)
}
} else {
None

View File

@ -39,7 +39,7 @@ use crate::{
poly::{is_apex, MakePolygon, PolyWeight},
via::{Via, ViaWeight},
},
math::{Circle, LineIntersection, NormalLine},
math::{Circle, LineIntersection, NormalLine, RotationSense},
};
/// Represents a weight for various compounds
@ -78,7 +78,7 @@ impl<R: AccessRules> Layout<R> {
dot_weight: LooseDotWeight,
seg_weight: SeqLooseSegWeight,
bend_weight: LooseBendWeight,
cw: bool,
sense: RotationSense,
) -> Result<Cane, DrawingException> {
self.drawing.insert_cane(
recorder,
@ -87,7 +87,7 @@ impl<R: AccessRules> Layout<R> {
dot_weight,
seg_weight,
bend_weight,
cw,
sense,
)
}

View File

@ -9,6 +9,12 @@ pub use specctra_core::math::{Circle, PointWithRotation};
mod tangents;
pub use tangents::*;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RotationSense {
Counterclockwise,
Clockwise,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LineIntersection {
Empty,

View File

@ -6,7 +6,7 @@ use geo::{geometry::Point, Line};
use specctra_core::math::Circle;
use thiserror::Error;
use super::{seq_perp_dot_product, NormalLine};
use super::{seq_perp_dot_product, NormalLine, RotationSense};
#[derive(Error, Debug, Clone, Copy, PartialEq)]
#[error("no tangents for {0:?} and {1:?}")] // TODO add real error message
@ -84,27 +84,31 @@ fn tangent_point_pairs(
pub fn tangent_segments(
circle1: Circle,
cw1: Option<bool>,
maybe_sense1: Option<RotationSense>,
circle2: Circle,
cw2: Option<bool>,
maybe_sense2: Option<RotationSense>,
) -> Result<impl Iterator<Item = Line>, NoTangents> {
Ok(tangent_point_pairs(circle1, circle2)?
.into_iter()
.filter_map(move |tangent_point_pair| {
if let Some(cw1) = cw1 {
if let Some(sense1) = maybe_sense1 {
let cross1 =
seq_perp_dot_product(tangent_point_pair.0, tangent_point_pair.1, circle1.pos);
if (cw1 && cross1 <= 0.0) || (!cw1 && cross1 >= 0.0) {
if (sense1 == RotationSense::Clockwise && cross1 <= 0.0)
|| (sense1 == RotationSense::Counterclockwise && cross1 >= 0.0)
{
return None;
}
}
if let Some(cw2) = cw2 {
if let Some(sense2) = maybe_sense2 {
let cross2 =
seq_perp_dot_product(tangent_point_pair.0, tangent_point_pair.1, circle2.pos);
if (cw2 && cross2 >= 0.0) || (!cw2 && cross2 <= 0.0) {
if (sense2 == RotationSense::Clockwise && cross2 >= 0.0)
|| (sense2 == RotationSense::Counterclockwise && cross2 <= 0.0)
{
return None;
}
}
@ -115,11 +119,13 @@ pub fn tangent_segments(
pub fn tangent_segment(
circle1: Circle,
cw1: Option<bool>,
maybe_sense1: Option<RotationSense>,
circle2: Circle,
cw2: Option<bool>,
maybe_sense2: Option<RotationSense>,
) -> Result<Line, NoTangents> {
Ok(tangent_segments(circle1, cw1, circle2, cw2)?
.next()
.unwrap())
Ok(
tangent_segments(circle1, maybe_sense1, circle2, maybe_sense2)?
.next()
.unwrap(),
)
}

View File

@ -23,7 +23,7 @@ use crate::{
},
geometry::GetLayer,
layout::{Layout, LayoutEdit},
math::{Circle, NoTangents},
math::{Circle, NoTangents, RotationSense},
};
#[derive(Error, Debug, Clone, Copy)]
@ -53,7 +53,7 @@ pub trait Draw {
recorder: &mut LayoutEdit,
head: Head,
around: FixedDotIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<CaneHead, DrawException>;
@ -62,7 +62,7 @@ pub trait Draw {
recorder: &mut LayoutEdit,
head: Head,
around: BendIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<CaneHead, DrawException>;
@ -130,12 +130,12 @@ impl<R: AccessRules> Draw for Layout<R> {
recorder: &mut LayoutEdit,
head: Head,
around: FixedDotIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<CaneHead, DrawException> {
let tangent = self
.drawing()
.head_around_dot_segment(&head, around.into(), cw, width)?;
.head_around_dot_segment(&head, around.into(), sense, width)?;
let offset = self
.drawing()
.head_around_dot_offset(&head, around.into(), width);
@ -145,7 +145,7 @@ impl<R: AccessRules> Draw for Layout<R> {
around.into(),
tangent.start_point(),
tangent.end_point(),
cw,
sense,
width,
offset,
)
@ -159,12 +159,12 @@ impl<R: AccessRules> Draw for Layout<R> {
recorder: &mut LayoutEdit,
head: Head,
around: BendIndex,
cw: bool,
sense: RotationSense,
width: f64,
) -> Result<CaneHead, DrawException> {
let tangent = self
.drawing()
.head_around_bend_segment(&head, around, cw, width)?;
.head_around_bend_segment(&head, around, sense, width)?;
let offset = self.drawing().head_around_bend_offset(&head, around, width);
self.cane_around(
@ -173,7 +173,7 @@ impl<R: AccessRules> Draw for Layout<R> {
around.into(),
tangent.start_point(),
tangent.end_point(),
cw,
sense,
width,
offset,
)
@ -203,7 +203,7 @@ trait DrawPrivate {
around: GearIndex,
from: Point,
to: Point,
cw: bool,
sense: RotationSense,
width: f64,
offset: f64,
) -> Result<CaneHead, DrawingException>;
@ -221,7 +221,7 @@ trait DrawPrivate {
head: Head,
around: GearIndex,
to: Point,
cw: bool,
sense: RotationSense,
width: f64,
offset: f64,
) -> Result<CaneHead, DrawingException>;
@ -239,12 +239,12 @@ impl<R: AccessRules> DrawPrivate for Layout<R> {
around: GearIndex,
from: Point,
to: Point,
cw: bool,
sense: RotationSense,
width: f64,
offset: f64,
) -> Result<CaneHead, DrawingException> {
let head = self.extend_head(recorder, head, from)?;
self.cane(recorder, head, around, to, cw, width, offset)
self.cane(recorder, head, around, to, sense, width, offset)
}
#[debug_ensures(self.drawing().node_count() == old(self.drawing().node_count()))]
@ -270,7 +270,7 @@ impl<R: AccessRules> DrawPrivate for Layout<R> {
head: Head,
around: GearIndex,
to: Point,
cw: bool,
sense: RotationSense,
width: f64,
offset: f64,
) -> Result<CaneHead, DrawingException> {
@ -299,7 +299,7 @@ impl<R: AccessRules> DrawPrivate for Layout<R> {
layer,
maybe_net,
}),
cw,
sense,
)?;
Ok(CaneHead {
face: self.drawing().primitive(cane.bend).other_joint(cane.dot),

View File

@ -60,20 +60,28 @@ impl NavcordStepper {
around: NavvertexIndex,
) -> Result<CaneHead, NavcorderException> {
let around_node_weight = navmesh.node_weight(around).unwrap();
let cw = around_node_weight
.maybe_cw
let sense = around_node_weight
.maybe_sense
.ok_or(NavcorderException::CannotWrap)?;
match around_node_weight.node {
BinavvertexNodeIndex::FixedDot(dot) => {
layout.cane_around_dot(&mut self.recorder, head, dot, cw, self.width)
}
BinavvertexNodeIndex::FixedBend(fixed_bend) => {
layout.cane_around_bend(&mut self.recorder, head, fixed_bend.into(), cw, self.width)
}
BinavvertexNodeIndex::LooseBend(loose_bend) => {
layout.cane_around_bend(&mut self.recorder, head, loose_bend.into(), cw, self.width)
layout.cane_around_dot(&mut self.recorder, head, dot, sense, self.width)
}
BinavvertexNodeIndex::FixedBend(fixed_bend) => layout.cane_around_bend(
&mut self.recorder,
head,
fixed_bend.into(),
sense,
self.width,
),
BinavvertexNodeIndex::LooseBend(loose_bend) => layout.cane_around_bend(
&mut self.recorder,
head,
loose_bend.into(),
sense,
self.width,
),
}
.map_err(NavcorderException::CannotDraw)
}

View File

@ -31,6 +31,7 @@ use crate::{
geometry::{shape::AccessShape, GetLayer},
graph::{GetPetgraphIndex, MakeRef},
layout::Layout,
math::RotationSense,
router::astar::MakeEdgeRef,
triangulation::{GetTrianvertexNodeIndex, Triangulation},
};
@ -136,7 +137,7 @@ pub struct NavvertexWeight {
/// one is clockwise (`Some(true)`), the other counterclockwise (`Some(false)`).
/// The origin and destination nodes however have
/// only one corresponding navmesh vertex each (`None`).
pub maybe_cw: Option<bool>,
pub maybe_sense: Option<RotationSense>,
}
#[derive(Error, Debug, Clone)]
@ -222,7 +223,7 @@ impl Navmesh {
if trianvertex == origin.into() {
let navvertex = graph.add_node(NavvertexWeight {
node: trianvertex.into(),
maybe_cw: None,
maybe_sense: None,
});
origin_navvertex = Some(navvertex);
@ -230,7 +231,7 @@ impl Navmesh {
} else if trianvertex == destination.into() {
let navvertex = graph.add_node(NavvertexWeight {
node: trianvertex.into(),
maybe_cw: None,
maybe_sense: None,
});
destination_navvertex = Some(navvertex);
@ -313,12 +314,12 @@ impl Navmesh {
) {
let navvertex1 = graph.add_node(NavvertexWeight {
node,
maybe_cw: Some(false),
maybe_sense: Some(RotationSense::Counterclockwise),
});
let navvertex2 = graph.add_node(NavvertexWeight {
node,
maybe_cw: Some(true),
maybe_sense: Some(RotationSense::Clockwise),
});
map.get_mut(&trianvertex)