mirror of https://codeberg.org/topola/topola.git
Add compass directions
This commit is contained in:
parent
1264c3183a
commit
49cbf8b314
|
|
@ -0,0 +1,265 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use std::ops::{Add, Neg};
|
||||||
|
|
||||||
|
use num_traits::{One, Signed, Zero};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::Vector2;
|
||||||
|
|
||||||
|
pub trait CompassDirection<T>: Copy + PartialEq + Into<Vector2<T>> {
|
||||||
|
fn nearest_from_vector(vector: Vector2<T>) -> Self;
|
||||||
|
fn turn_clockwise(self) -> Self;
|
||||||
|
fn turn_counterclockwise(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub enum CardinalDirection {
|
||||||
|
East,
|
||||||
|
North,
|
||||||
|
West,
|
||||||
|
South,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + Neg<Output = T> + One + Zero> From<CardinalDirection> for Vector2<T> {
|
||||||
|
fn from(cardinal_direction: CardinalDirection) -> Vector2<T> {
|
||||||
|
match cardinal_direction {
|
||||||
|
CardinalDirection::East => [T::one(), T::zero()].into(),
|
||||||
|
CardinalDirection::North => [T::zero(), -T::one()].into(),
|
||||||
|
CardinalDirection::West => [-T::one(), T::zero()].into(),
|
||||||
|
CardinalDirection::South => [T::zero(), T::one()].into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + PartialOrd + Signed + Zero> CompassDirection<T> for CardinalDirection {
|
||||||
|
fn nearest_from_vector(vector: Vector2<T>) -> Self {
|
||||||
|
if vector.x.abs() > vector.y.abs() {
|
||||||
|
if vector.x > T::zero() {
|
||||||
|
Self::East
|
||||||
|
} else {
|
||||||
|
Self::West
|
||||||
|
}
|
||||||
|
} else if vector.y > T::zero() {
|
||||||
|
Self::South
|
||||||
|
} else {
|
||||||
|
Self::North
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_clockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::East => Self::South,
|
||||||
|
Self::North => Self::East,
|
||||||
|
Self::West => Self::North,
|
||||||
|
Self::South => Self::West,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_counterclockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::East => Self::North,
|
||||||
|
Self::North => Self::West,
|
||||||
|
Self::West => Self::South,
|
||||||
|
Self::South => Self::East,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub enum OrdinalDirection {
|
||||||
|
NorthWest,
|
||||||
|
SouthWest,
|
||||||
|
SouthEast,
|
||||||
|
NorthEast,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + Neg<Output = T> + One + Zero> From<OrdinalDirection> for Vector2<T> {
|
||||||
|
fn from(ordinal_direction: OrdinalDirection) -> Vector2<T> {
|
||||||
|
match ordinal_direction {
|
||||||
|
OrdinalDirection::NorthEast => [T::one(), -T::one()].into(),
|
||||||
|
OrdinalDirection::NorthWest => [-T::one(), -T::one()].into(),
|
||||||
|
OrdinalDirection::SouthWest => [-T::one(), T::one()].into(),
|
||||||
|
OrdinalDirection::SouthEast => [T::one(), T::one()].into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + PartialOrd + Signed + Zero> CompassDirection<T> for OrdinalDirection {
|
||||||
|
fn nearest_from_vector(vector: Vector2<T>) -> Self {
|
||||||
|
let zero = T::zero();
|
||||||
|
|
||||||
|
if vector.x > zero && vector.y > zero {
|
||||||
|
Self::SouthEast
|
||||||
|
} else if vector.x > zero && vector.y < zero {
|
||||||
|
Self::NorthEast
|
||||||
|
} else if vector.x < zero && vector.y < zero {
|
||||||
|
Self::NorthWest
|
||||||
|
} else {
|
||||||
|
Self::SouthWest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_clockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::NorthWest => Self::NorthEast,
|
||||||
|
Self::NorthEast => Self::SouthEast,
|
||||||
|
Self::SouthEast => Self::SouthWest,
|
||||||
|
Self::SouthWest => Self::NorthWest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_counterclockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::NorthWest => Self::SouthWest,
|
||||||
|
Self::SouthWest => Self::SouthEast,
|
||||||
|
Self::SouthEast => Self::NorthEast,
|
||||||
|
Self::NorthEast => Self::NorthWest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub enum PrincipalWind {
|
||||||
|
North,
|
||||||
|
NorthWest,
|
||||||
|
West,
|
||||||
|
SouthWest,
|
||||||
|
South,
|
||||||
|
SouthEast,
|
||||||
|
East,
|
||||||
|
NorthEast,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + Neg<Output = T> + One + Zero> From<PrincipalWind> for Vector2<T> {
|
||||||
|
fn from(principal_wind: PrincipalWind) -> Vector2<T> {
|
||||||
|
match principal_wind {
|
||||||
|
PrincipalWind::North => [T::zero(), -T::one()].into(),
|
||||||
|
PrincipalWind::NorthWest => [-T::one(), -T::one()].into(),
|
||||||
|
PrincipalWind::West => [-T::one(), T::zero()].into(),
|
||||||
|
PrincipalWind::SouthWest => [-T::one(), T::one()].into(),
|
||||||
|
PrincipalWind::South => [T::zero(), T::one()].into(),
|
||||||
|
PrincipalWind::SouthEast => [T::one(), T::one()].into(),
|
||||||
|
PrincipalWind::East => [T::one(), T::zero()].into(),
|
||||||
|
PrincipalWind::NorthEast => [T::one(), -T::one()].into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + Add<Output = T> + PartialOrd + Signed + Zero> CompassDirection<T> for PrincipalWind {
|
||||||
|
fn nearest_from_vector(vector: Vector2<T>) -> Self {
|
||||||
|
if vector.x.is_zero() && vector.y.is_zero() {
|
||||||
|
panic!("Zero vector has no direction");
|
||||||
|
}
|
||||||
|
|
||||||
|
if vector.x.is_zero() {
|
||||||
|
return if vector.y < T::zero() {
|
||||||
|
Self::North
|
||||||
|
} else {
|
||||||
|
Self::South
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if vector.y.is_zero() {
|
||||||
|
return if vector.x > T::zero() {
|
||||||
|
Self::East
|
||||||
|
} else {
|
||||||
|
Self::West
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if vector.x > T::zero() && vector.y < T::zero() {
|
||||||
|
if vector.x.abs() > vector.y.abs() {
|
||||||
|
if vector.y.abs() + vector.y.abs() <= vector.x.abs() {
|
||||||
|
Self::East
|
||||||
|
} else {
|
||||||
|
Self::NorthEast
|
||||||
|
}
|
||||||
|
} else if vector.y.abs() > vector.x.abs() {
|
||||||
|
if vector.x.abs() + vector.x.abs() <= vector.y.abs() {
|
||||||
|
Self::North
|
||||||
|
} else {
|
||||||
|
Self::NorthEast
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::NorthEast
|
||||||
|
}
|
||||||
|
} else if vector.x > T::zero() && vector.y > T::zero() {
|
||||||
|
if vector.x.abs() > vector.y.abs() {
|
||||||
|
if vector.y.abs() + vector.y.abs() <= vector.x.abs() {
|
||||||
|
Self::East
|
||||||
|
} else {
|
||||||
|
Self::SouthEast
|
||||||
|
}
|
||||||
|
} else if vector.y.abs() > vector.x.abs() {
|
||||||
|
if vector.x.abs() + vector.x.abs() <= vector.y.abs() {
|
||||||
|
Self::South
|
||||||
|
} else {
|
||||||
|
Self::SouthEast
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::SouthEast
|
||||||
|
}
|
||||||
|
} else if vector.x < T::zero() && vector.y > T::zero() {
|
||||||
|
if vector.x.abs() > vector.y.abs() {
|
||||||
|
if vector.y.abs() + vector.y.abs() <= vector.x.abs() {
|
||||||
|
Self::West
|
||||||
|
} else {
|
||||||
|
Self::SouthWest
|
||||||
|
}
|
||||||
|
} else if vector.y.abs() > vector.x.abs() {
|
||||||
|
if vector.x.abs() + vector.x.abs() <= vector.y.abs() {
|
||||||
|
Self::South
|
||||||
|
} else {
|
||||||
|
Self::SouthWest
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::SouthWest
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if vector.x.abs() > vector.y.abs() {
|
||||||
|
if vector.y.abs() + vector.y.abs() <= vector.x.abs() {
|
||||||
|
Self::West
|
||||||
|
} else {
|
||||||
|
Self::NorthWest
|
||||||
|
}
|
||||||
|
} else if vector.y.abs() > vector.x.abs() {
|
||||||
|
if vector.x.abs() + vector.x.abs() <= vector.y.abs() {
|
||||||
|
Self::North
|
||||||
|
} else {
|
||||||
|
Self::NorthWest
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::NorthWest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_clockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::North => Self::NorthEast,
|
||||||
|
Self::NorthEast => Self::East,
|
||||||
|
Self::East => Self::SouthEast,
|
||||||
|
Self::SouthEast => Self::South,
|
||||||
|
Self::South => Self::SouthWest,
|
||||||
|
Self::SouthWest => Self::West,
|
||||||
|
Self::West => Self::NorthWest,
|
||||||
|
Self::NorthWest => Self::North,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_counterclockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::North => Self::NorthWest,
|
||||||
|
Self::NorthWest => Self::West,
|
||||||
|
Self::West => Self::SouthWest,
|
||||||
|
Self::SouthWest => Self::South,
|
||||||
|
Self::South => Self::SouthEast,
|
||||||
|
Self::SouthEast => Self::East,
|
||||||
|
Self::East => Self::NorthEast,
|
||||||
|
Self::NorthEast => Self::North,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,8 +39,10 @@ impl Layout {
|
||||||
self.joints.modify(id.index(), |joint| f(joint));
|
self.joints.modify(id.index(), |joint| f(joint));
|
||||||
|
|
||||||
let new_joint = self.joints[id.index()].clone();
|
let new_joint = self.joints[id.index()].clone();
|
||||||
self.joints_rtree
|
self.joints_rtree.insert(
|
||||||
.insert(GeomWithData::new(new_joint.bbox().rtree_rectangle(), id), ());
|
GeomWithData::new(new_joint.bbox().rtree_rectangle(), id),
|
||||||
|
(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modify_segment<F>(&mut self, id: SegmentId, f: F)
|
pub fn modify_segment<F>(&mut self, id: SegmentId, f: F)
|
||||||
|
|
@ -55,8 +57,10 @@ impl Layout {
|
||||||
.modify(id.index(), |segment| f(&mut segment.spec));
|
.modify(id.index(), |segment| f(&mut segment.spec));
|
||||||
|
|
||||||
let new_segment = &self.segments[id.index()];
|
let new_segment = &self.segments[id.index()];
|
||||||
self.segments_rtree
|
self.segments_rtree.insert(
|
||||||
.insert(GeomWithData::new(new_segment.bbox().rtree_rectangle(), id), ());
|
GeomWithData::new(new_segment.bbox().rtree_rectangle(), id),
|
||||||
|
(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn update_segment(&mut self, id: SegmentId) {
|
pub(super) fn update_segment(&mut self, id: SegmentId) {
|
||||||
|
|
@ -76,8 +80,10 @@ impl Layout {
|
||||||
});
|
});
|
||||||
|
|
||||||
let new_segment = &self.segments[id.index()];
|
let new_segment = &self.segments[id.index()];
|
||||||
self.segments_rtree
|
self.segments_rtree.insert(
|
||||||
.insert(GeomWithData::new(new_segment.bbox().rtree_rectangle(), id), ());
|
GeomWithData::new(new_segment.bbox().rtree_rectangle(), id),
|
||||||
|
(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modify_via<F>(&mut self, id: ViaId, f: F)
|
pub fn modify_via<F>(&mut self, id: ViaId, f: F)
|
||||||
|
|
@ -128,7 +134,9 @@ impl Layout {
|
||||||
self.polygons.modify(id.index(), |polygon| f(polygon));
|
self.polygons.modify(id.index(), |polygon| f(polygon));
|
||||||
|
|
||||||
let new_polygon = &self.polygons[id.index()];
|
let new_polygon = &self.polygons[id.index()];
|
||||||
self.polygons_rtree
|
self.polygons_rtree.insert(
|
||||||
.insert(GeomWithData::new(new_polygon.bbox().rtree_rectangle(), id), ());
|
GeomWithData::new(new_polygon.bbox().rtree_rectangle(), id),
|
||||||
|
(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
use derive_more::{Constructor, From};
|
use derive_more::{Constructor, From};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Rect3, Vector3, layout::LayerId};
|
|
||||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||||
use crate::primitives::{SegmentId, ViaId};
|
use crate::primitives::{SegmentId, ViaId};
|
||||||
use crate::vector::Vector2;
|
use crate::vector::Vector2;
|
||||||
|
use crate::{Rect3, Vector3, layout::LayerId};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
use derive_more::{Constructor, From};
|
use derive_more::{Constructor, From};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Rect3, Vector3, layout::LayerId};
|
|
||||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||||
use crate::vector::Vector2;
|
use crate::vector::Vector2;
|
||||||
|
use crate::{Rect3, Vector3, layout::LayerId};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
use derive_more::{Constructor, From};
|
use derive_more::{Constructor, From};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Rect3, Vector3, layout::LayerId};
|
|
||||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||||
use crate::vector::Vector2;
|
use crate::vector::Vector2;
|
||||||
|
use crate::{Rect3, Vector3, layout::LayerId};
|
||||||
|
|
||||||
use super::JointId;
|
use super::JointId;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
use derive_more::{Constructor, From};
|
use derive_more::{Constructor, From};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Rect3, Vector3, layout::LayerId};
|
|
||||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||||
use crate::vector::Vector2;
|
use crate::vector::Vector2;
|
||||||
|
use crate::{Rect3, Vector3, layout::LayerId};
|
||||||
|
|
||||||
use super::JointId;
|
use super::JointId;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
mod autorouter;
|
mod autorouter;
|
||||||
mod board;
|
mod board;
|
||||||
|
mod compass;
|
||||||
mod drawer;
|
mod drawer;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod math;
|
mod math;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue