mirror of https://codeberg.org/topola/topola.git
Exactly filter upon location after bbox hit
This commit is contained in:
parent
e1795413bd
commit
85f33f1a6c
|
|
@ -22,7 +22,7 @@ use crate::{
|
|||
LayerId, Layout, LayoutHalfDelta,
|
||||
compounds::{ComponentId, NetId, PinId},
|
||||
},
|
||||
math::Vector2,
|
||||
vector::Vector2,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Getters, Delta)]
|
||||
|
|
|
|||
|
|
@ -24,73 +24,94 @@ impl Layout {
|
|||
&self,
|
||||
rect: Rect3<i64>,
|
||||
) -> impl Iterator<Item = JointId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.joints_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope_intersecting(&rect_aabb)
|
||||
.locate_in_envelope_intersecting(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&joint_id| {
|
||||
let joint = self.joint(joint_id);
|
||||
rect.rect2()
|
||||
.intersects_circle(joint.spec.position, joint.spec.radius as i64)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_joints_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = JointId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.joints_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope(&rect_aabb)
|
||||
.locate_in_envelope(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&joint_id| {
|
||||
let joint = self.joint(joint_id);
|
||||
rect.rect2()
|
||||
.contains_circle(joint.spec.position, joint.spec.radius as i64)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_segments_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = SegmentId> {
|
||||
let point2 = point.xy();
|
||||
self.segments_rtree
|
||||
.as_ref()
|
||||
.locate_all_at_point(&[point.x, point.y, point.z])
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&segment_id| self.segment(segment_id).contains_point(point2))
|
||||
.filter(move |&segment_id| self.segment(segment_id).contains_point(point.xy()))
|
||||
}
|
||||
|
||||
pub fn locate_segments_intersecting_rect(
|
||||
&self,
|
||||
rect: Rect3<i64>,
|
||||
) -> impl Iterator<Item = SegmentId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.segments_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope_intersecting(&rect_aabb)
|
||||
.locate_in_envelope_intersecting(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&segment_id| {
|
||||
let segment = self.segment(segment_id);
|
||||
rect.rect2()
|
||||
.intersects_polygon(&segment.bounding_rectangle())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_segments_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = SegmentId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.segments_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope(&rect_aabb)
|
||||
.locate_in_envelope(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&segment_id| {
|
||||
let segment = self.segment(segment_id);
|
||||
rect.rect2().contains_polygon(&segment.bounding_rectangle())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_vias_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = ViaId> {
|
||||
let layer = LayerId::new(point.z as usize);
|
||||
let point2 = point.xy();
|
||||
self.vias_rtree
|
||||
.as_ref()
|
||||
.locate_all_at_point(&[point.x, point.y, point.z])
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&via_id| self.vias[via_id.index()].contains_point(layer, point2))
|
||||
.filter(move |&via_id| self.vias[via_id.index()].contains_point(layer, point.xy()))
|
||||
}
|
||||
|
||||
pub fn locate_vias_intersecting_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = ViaId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.vias_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope_intersecting(&rect_aabb)
|
||||
.locate_in_envelope_intersecting(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&via_id| {
|
||||
let via = self.via(via_id);
|
||||
rect.rect2()
|
||||
.intersects_circle(via.position, via.spec.radius as i64)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_vias_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = ViaId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.vias_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope(&rect_aabb)
|
||||
.locate_in_envelope(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&via_id| {
|
||||
let via = self.via(via_id);
|
||||
rect.rect2()
|
||||
.contains_circle(via.position, via.spec.radius as i64)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_polygons_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = PolygonId> {
|
||||
|
|
@ -106,19 +127,25 @@ impl Layout {
|
|||
&self,
|
||||
rect: Rect3<i64>,
|
||||
) -> impl Iterator<Item = PolygonId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.polygons_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope_intersecting(&rect_aabb)
|
||||
.locate_in_envelope_intersecting(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&polygon_id| {
|
||||
let polygon = self.polygon(polygon_id);
|
||||
rect.rect2().intersects_polygon(&polygon.vertices)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_polygons_inside_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = PolygonId> {
|
||||
let rect_aabb = rect.aabb3();
|
||||
self.polygons_rtree
|
||||
.as_ref()
|
||||
.locate_in_envelope(&rect_aabb)
|
||||
.locate_in_envelope(&rect.aabb3())
|
||||
.map(|geom_with_data| geom_with_data.data)
|
||||
.filter(move |&polygon_id| {
|
||||
let polygon = self.polygon(polygon_id);
|
||||
rect.rect2().contains_polygon(&polygon.vertices)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn locate_nets_intersecting_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = NetId> {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::layout::LayerId;
|
||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
use crate::primitives::{SegmentId, ViaId};
|
||||
use crate::vector::Vector2;
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::layout::LayerId;
|
||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
use crate::vector::Vector2;
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::layout::LayerId;
|
||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
use crate::vector::Vector2;
|
||||
|
||||
use super::JointId;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::layout::LayerId;
|
||||
use crate::layout::compounds::{ComponentId, NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
use crate::vector::Vector2;
|
||||
|
||||
use super::JointId;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use crate::{Layout, layout::compounds::ComponentId, math::Vector2};
|
||||
use crate::{Layout, layout::compounds::ComponentId, vector::Vector2};
|
||||
|
||||
impl Layout {
|
||||
pub fn move_component_by(&mut self, id: ComponentId, translation: Vector2<i64>) {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ mod math;
|
|||
mod navmesher;
|
||||
mod pathfinder;
|
||||
mod ratsnest;
|
||||
mod rect;
|
||||
mod router;
|
||||
mod specctra;
|
||||
mod vector;
|
||||
mod workspace;
|
||||
|
||||
pub use crate::autorouter::Autorouter;
|
||||
|
|
@ -28,6 +30,7 @@ pub use crate::layout::LayerId;
|
|||
pub use crate::layout::Layout;
|
||||
pub use crate::layout::compounds::{Pin, PinId};
|
||||
pub use crate::layout::primitives;
|
||||
pub use crate::math::{Rect2, Rect3, Vector2, Vector3};
|
||||
pub use crate::ratsnest::{Ratline, Ratsnest};
|
||||
pub use crate::rect::{Rect2, Rect3};
|
||||
pub use crate::vector::{Vector2, Vector3};
|
||||
pub use crate::workspace::{AutorouterWorkspace, BoardWorkspace, Workspace};
|
||||
|
|
|
|||
|
|
@ -2,208 +2,9 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_getters::Getters;
|
||||
use derive_more::{
|
||||
Add, AddAssign, Constructor, Div, DivAssign, From, Into, Mul, MulAssign, Sub, SubAssign,
|
||||
};
|
||||
use polygon_unionfind::UnionFind;
|
||||
use rstar::AABB;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct Rect2<T> {
|
||||
min: Vector2<T>,
|
||||
max: Vector2<T>,
|
||||
}
|
||||
|
||||
impl<T: Ord + Copy> Rect2<T> {
|
||||
pub fn new(from: Vector2<T>, to: Vector2<T>) -> Self {
|
||||
Self {
|
||||
min: Vector2::new(std::cmp::min(from.x, to.x), std::cmp::min(from.y, to.y)),
|
||||
max: Vector2::new(std::cmp::max(from.x, to.x), std::cmp::max(from.y, to.y)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect2<i64> {
|
||||
pub fn aabb3(self, z: i64) -> AABB<[i64; 3]> {
|
||||
AABB::from_corners([self.min.x, self.min.y, z], [self.max.x, self.max.y, z])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct Rect3<T> {
|
||||
min: Vector3<T>,
|
||||
max: Vector3<T>,
|
||||
}
|
||||
|
||||
impl<T: Ord + Copy> Rect3<T> {
|
||||
pub fn new(from: Vector3<T>, to: Vector3<T>) -> Self {
|
||||
Self {
|
||||
min: Vector3::new(
|
||||
std::cmp::min(from.x, to.x),
|
||||
std::cmp::min(from.y, to.y),
|
||||
std::cmp::min(from.z, to.z),
|
||||
),
|
||||
max: Vector3::new(
|
||||
std::cmp::max(from.x, to.x),
|
||||
std::cmp::max(from.y, to.y),
|
||||
std::cmp::max(from.z, to.z),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect3<i64> {
|
||||
pub fn aabb3(self) -> AABB<[i64; 3]> {
|
||||
AABB::from_corners(
|
||||
[self.min.x, self.min.y, self.min.z],
|
||||
[self.max.x, self.max.y, self.max.z],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Add,
|
||||
AddAssign,
|
||||
Clone,
|
||||
Constructor,
|
||||
Copy,
|
||||
Debug,
|
||||
Deserialize,
|
||||
Div,
|
||||
DivAssign,
|
||||
Eq,
|
||||
From,
|
||||
Into,
|
||||
Mul,
|
||||
MulAssign,
|
||||
Ord,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Sub,
|
||||
SubAssign,
|
||||
)]
|
||||
pub struct Vector2<T> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Add,
|
||||
AddAssign,
|
||||
Clone,
|
||||
Constructor,
|
||||
Copy,
|
||||
Debug,
|
||||
Deserialize,
|
||||
Div,
|
||||
DivAssign,
|
||||
Eq,
|
||||
From,
|
||||
Into,
|
||||
Mul,
|
||||
MulAssign,
|
||||
Ord,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Sub,
|
||||
SubAssign,
|
||||
)]
|
||||
pub struct Vector3<T> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
}
|
||||
|
||||
impl<T: Copy> Vector3<T> {
|
||||
pub fn xy(self) -> Vector2<T> {
|
||||
Vector2::new(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<[T; 2]> for Vector2<T> {
|
||||
fn from(from: [T; 2]) -> Self {
|
||||
Self {
|
||||
x: from[0],
|
||||
y: from[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<Vector2<T>> for [T; 2] {
|
||||
fn from(from: Vector2<T>) -> Self {
|
||||
[from.x, from.y]
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_inside_polygon {
|
||||
($type:ty) => {
|
||||
impl Vector2<$type> {
|
||||
// Checks if the point is inside a polygon by casting a ray to the
|
||||
// right. Division is not used to avoid integer truncation errors.
|
||||
pub fn inside_polygon(&self, polygon: &[Vector2<$type>]) -> bool {
|
||||
let mut inside = false;
|
||||
let n = polygon.len();
|
||||
|
||||
// `self` is `v0`.
|
||||
|
||||
// `v1` is the previous vertex.
|
||||
let mut v1 = &polygon[n - 1];
|
||||
|
||||
// `v2` is the current vertex.
|
||||
for v2 in polygon.iter() {
|
||||
let dx12 = v2.x - v1.x;
|
||||
let dy12 = v2.y - v1.y;
|
||||
|
||||
// First, check if the line of the horizontal rightward ray
|
||||
// cast to actually crosses the vertical span of the current
|
||||
// `(v1, v2)` edge.
|
||||
if dy12 != (0 as $type) && (self.y > v1.y) != (self.y > v2.y) {
|
||||
let dx01 = self.x - v1.x;
|
||||
let dy01 = self.y - v1.y;
|
||||
|
||||
// Now check if the (v1, v2) edge is actually on the
|
||||
// right side of the ray and not on the left.
|
||||
//
|
||||
// This just compares the X coordinate of `self` (`v0`)
|
||||
// to the X coordinate of the intersection between the
|
||||
// horizontal rightward ray and the current `(v1, v2)` edge:
|
||||
//
|
||||
// `self.x < v1.x + (self.y - v1.y) * (dx12 / dy12)`
|
||||
//
|
||||
// but is algebraically simplified and rewritten to not
|
||||
// use division.
|
||||
let crosses = if dy12 > (0 as $type) {
|
||||
dx01 * dy12 < dx12 * dy01
|
||||
} else {
|
||||
dx01 * dy12 > dx12 * dy01
|
||||
};
|
||||
|
||||
// Even-odd rule: flip whether the point is inside or
|
||||
// outside upon each detected crossing.
|
||||
if crosses {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the current vertex previous for the next loop
|
||||
// iteration.
|
||||
v1 = v2;
|
||||
}
|
||||
|
||||
inside
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_inside_polygon!(f32);
|
||||
impl_inside_polygon!(f64);
|
||||
impl_inside_polygon!(i32);
|
||||
impl_inside_polygon!(i64);
|
||||
use crate::Vector2;
|
||||
|
||||
/// Returns the four vertices of a segment inflated by `half_width`, forming a
|
||||
/// convex quadrilateral. The segment goes from (x1, y1) to (x2, y2).
|
||||
|
|
@ -225,60 +26,6 @@ pub fn inflated_segment(x1: i64, y1: i64, x2: i64, y2: i64, half_width: u64) ->
|
|||
]
|
||||
}
|
||||
|
||||
macro_rules! impl_rotate_around_point {
|
||||
($type:ty) => {
|
||||
impl Vector2<$type> {
|
||||
pub fn rotate_around_point(&mut self, angle: $type, origin: Vector2<$type>) -> Self {
|
||||
let sin = angle.sin();
|
||||
let cos = angle.cos();
|
||||
|
||||
let tx = self.x - origin.x;
|
||||
let ty = self.y - origin.y;
|
||||
|
||||
let rx = tx * cos - ty * sin;
|
||||
let ry = tx * sin + ty * cos;
|
||||
|
||||
self.x = rx + origin.x;
|
||||
self.y = ry + origin.y;
|
||||
|
||||
*self
|
||||
}
|
||||
|
||||
pub fn rotate_around_point_degrees(
|
||||
&mut self,
|
||||
angle: $type,
|
||||
origin: Vector2<$type>,
|
||||
) -> Self {
|
||||
self.rotate_around_point(angle.to_radians(), origin)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_rotate_around_point!(f32);
|
||||
impl_rotate_around_point!(f64);
|
||||
|
||||
macro_rules! impl_polygon_centroid {
|
||||
($type:ty) => {
|
||||
impl Vector2<$type> {
|
||||
pub fn polygon_centroid(polygon: &[Vector2<$type>]) -> Self {
|
||||
let mut sum = Vector2::new(0 as $type, 0 as $type);
|
||||
|
||||
for vertex in polygon.iter() {
|
||||
sum += *vertex;
|
||||
}
|
||||
|
||||
sum / polygon.len() as $type
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_polygon_centroid!(f32);
|
||||
impl_polygon_centroid!(f64);
|
||||
impl_polygon_centroid!(i32);
|
||||
impl_polygon_centroid!(i64);
|
||||
|
||||
/// Kruskal's minimum spanning tree algorithm.
|
||||
pub fn kruskal_mst<W: Copy + Ord>(
|
||||
vertex_count: usize,
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ use undoredo::Recorder;
|
|||
use crate::{
|
||||
Board,
|
||||
layout::LayerId,
|
||||
math::Vector2,
|
||||
primitives::{Joint, JointId, JointSpec, Polygon, PolygonId, Segment, SegmentId},
|
||||
vector::Vector2,
|
||||
};
|
||||
|
||||
#[derive(
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ use crate::{
|
|||
Board,
|
||||
layout::LayerId,
|
||||
layout::compounds::NetId,
|
||||
math::Vector2,
|
||||
primitives::{JointId, PolygonId, PrimitiveId, SegmentId},
|
||||
vector::Vector2,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_getters::Getters;
|
||||
use rstar::{AABB, RTreeNum};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Vector2, Vector3};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct Rect2<T> {
|
||||
min: Vector2<T>,
|
||||
max: Vector2<T>,
|
||||
}
|
||||
|
||||
impl<T: Copy> Rect2<T> {
|
||||
pub fn corners(&self) -> [Vector2<T>; 4] {
|
||||
[
|
||||
self.min,
|
||||
Vector2::new(self.max.x, self.min.y),
|
||||
self.max,
|
||||
Vector2::new(self.min.x, self.max.y),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Rect2<T> {
|
||||
pub fn new(from: Vector2<T>, to: Vector2<T>) -> Self {
|
||||
Self {
|
||||
min: Vector2::new(std::cmp::min(from.x, to.x), std::cmp::min(from.y, to.y)),
|
||||
max: Vector2::new(std::cmp::max(from.x, to.x), std::cmp::max(from.y, to.y)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RTreeNum> Rect2<T> {
|
||||
pub fn aabb3(self, z: T) -> AABB<[T; 3]> {
|
||||
AABB::from_corners([self.min.x, self.min.y, z], [self.max.x, self.max.y, z])
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_rect2_contains_circle {
|
||||
($type:ty) => {
|
||||
impl Rect2<$type> {
|
||||
pub fn contains_circle(&self, center: Vector2<$type>, radius: $type) -> bool {
|
||||
let min_x = self.min.x as f64;
|
||||
let min_y = self.min.y as f64;
|
||||
let max_x = self.max.x as f64;
|
||||
let max_y = self.max.y as f64;
|
||||
let cx = center.x as f64;
|
||||
let cy = center.y as f64;
|
||||
let r = radius as f64;
|
||||
|
||||
cx - r >= min_x && cx + r <= max_x && cy - r >= min_y && cy + r <= max_y
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_rect2_contains_circle!(i8);
|
||||
impl_rect2_contains_circle!(i16);
|
||||
impl_rect2_contains_circle!(i32);
|
||||
impl_rect2_contains_circle!(i64);
|
||||
impl_rect2_contains_circle!(i128);
|
||||
impl_rect2_contains_circle!(u8);
|
||||
impl_rect2_contains_circle!(u16);
|
||||
impl_rect2_contains_circle!(u32);
|
||||
impl_rect2_contains_circle!(u64);
|
||||
impl_rect2_contains_circle!(u128);
|
||||
impl_rect2_contains_circle!(f32);
|
||||
impl_rect2_contains_circle!(f64);
|
||||
|
||||
macro_rules! impl_rect2_intersects_circle {
|
||||
($type:ty) => {
|
||||
impl Rect2<$type> {
|
||||
pub fn intersects_circle(&self, center: Vector2<$type>, radius: $type) -> bool {
|
||||
let min_x = self.min.x as f64;
|
||||
let min_y = self.min.y as f64;
|
||||
let max_x = self.max.x as f64;
|
||||
let max_y = self.max.y as f64;
|
||||
let cx = center.x as f64;
|
||||
let cy = center.y as f64;
|
||||
let r = radius as f64;
|
||||
|
||||
let closest_x = cx.clamp(min_x, max_x);
|
||||
let closest_y = cy.clamp(min_y, max_y);
|
||||
let dx = cx - closest_x;
|
||||
let dy = cy - closest_y;
|
||||
|
||||
dx * dx + dy * dy <= r * r
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_rect2_intersects_circle!(i8);
|
||||
impl_rect2_intersects_circle!(i16);
|
||||
impl_rect2_intersects_circle!(i32);
|
||||
impl_rect2_intersects_circle!(i64);
|
||||
impl_rect2_intersects_circle!(i128);
|
||||
impl_rect2_intersects_circle!(u8);
|
||||
impl_rect2_intersects_circle!(u16);
|
||||
impl_rect2_intersects_circle!(u32);
|
||||
impl_rect2_intersects_circle!(u64);
|
||||
impl_rect2_intersects_circle!(u128);
|
||||
impl_rect2_intersects_circle!(f32);
|
||||
impl_rect2_intersects_circle!(f64);
|
||||
|
||||
macro_rules! impl_rect2_contains_polygon {
|
||||
($type:ty) => {
|
||||
impl Rect2<$type> {
|
||||
pub fn contains_polygon(&self, polygon: &[Vector2<$type>]) -> bool {
|
||||
let corners = self.corners();
|
||||
polygon.iter().all(|point| point.inside_polygon(&corners))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_rect2_contains_polygon!(i8);
|
||||
impl_rect2_contains_polygon!(i16);
|
||||
impl_rect2_contains_polygon!(i32);
|
||||
impl_rect2_contains_polygon!(i64);
|
||||
impl_rect2_contains_polygon!(i128);
|
||||
impl_rect2_contains_polygon!(u8);
|
||||
impl_rect2_contains_polygon!(u16);
|
||||
impl_rect2_contains_polygon!(u32);
|
||||
impl_rect2_contains_polygon!(u64);
|
||||
impl_rect2_contains_polygon!(u128);
|
||||
impl_rect2_contains_polygon!(f32);
|
||||
impl_rect2_contains_polygon!(f64);
|
||||
|
||||
macro_rules! impl_rect2_intersects_polygon {
|
||||
($type:ty) => {
|
||||
impl Rect2<$type> {
|
||||
pub fn intersects_polygon(&self, polygon: &[Vector2<$type>]) -> bool {
|
||||
if polygon.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.contains_polygon(polygon) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let corners = self.corners();
|
||||
if corners.iter().any(|corner| corner.inside_polygon(polygon)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
|
||||
// TODO: Now handle cases where no vertex is inside but only
|
||||
// edges intersect.
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_rect2_intersects_polygon!(i8);
|
||||
impl_rect2_intersects_polygon!(i16);
|
||||
impl_rect2_intersects_polygon!(i32);
|
||||
impl_rect2_intersects_polygon!(i64);
|
||||
impl_rect2_intersects_polygon!(i128);
|
||||
impl_rect2_intersects_polygon!(u8);
|
||||
impl_rect2_intersects_polygon!(u16);
|
||||
impl_rect2_intersects_polygon!(u32);
|
||||
impl_rect2_intersects_polygon!(u64);
|
||||
impl_rect2_intersects_polygon!(u128);
|
||||
impl_rect2_intersects_polygon!(f32);
|
||||
impl_rect2_intersects_polygon!(f64);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct Rect3<T> {
|
||||
min: Vector3<T>,
|
||||
max: Vector3<T>,
|
||||
}
|
||||
|
||||
impl<T: Ord + Copy> Rect3<T> {
|
||||
pub fn new(from: Vector3<T>, to: Vector3<T>) -> Self {
|
||||
Self {
|
||||
min: Vector3::new(
|
||||
std::cmp::min(from.x, to.x),
|
||||
std::cmp::min(from.y, to.y),
|
||||
std::cmp::min(from.z, to.z),
|
||||
),
|
||||
max: Vector3::new(
|
||||
std::cmp::max(from.x, to.x),
|
||||
std::cmp::max(from.y, to.y),
|
||||
std::cmp::max(from.z, to.z),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Ord> Rect3<T> {
|
||||
pub fn rect2(&self) -> Rect2<T> {
|
||||
Rect2::new(self.min.xy(), self.max.xy())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RTreeNum> Rect3<T> {
|
||||
pub fn aabb3(&self) -> AABB<[T; 3]> {
|
||||
AABB::from_corners(
|
||||
[self.min.x, self.min.y, self.min.z],
|
||||
[self.max.x, self.max.y, self.max.z],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@ use crate::{
|
|||
board::{Board, LayerDesc, LayerSide, LayerType},
|
||||
layout::LayerId,
|
||||
layout::compounds::{ComponentId, NetId, PinId},
|
||||
math::Vector2,
|
||||
primitives::{JointSpec, Polygon, Segment, SegmentSpec},
|
||||
vector::Vector2,
|
||||
};
|
||||
|
||||
impl Board {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,218 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_more::{
|
||||
Add, AddAssign, Constructor, Div, DivAssign, From, Into, Mul, MulAssign, Sub, SubAssign,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(
|
||||
Add,
|
||||
AddAssign,
|
||||
Clone,
|
||||
Constructor,
|
||||
Copy,
|
||||
Debug,
|
||||
Deserialize,
|
||||
Div,
|
||||
DivAssign,
|
||||
Eq,
|
||||
From,
|
||||
Into,
|
||||
Mul,
|
||||
MulAssign,
|
||||
Ord,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Sub,
|
||||
SubAssign,
|
||||
)]
|
||||
pub struct Vector2<T> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Add,
|
||||
AddAssign,
|
||||
Clone,
|
||||
Constructor,
|
||||
Copy,
|
||||
Debug,
|
||||
Deserialize,
|
||||
Div,
|
||||
DivAssign,
|
||||
Eq,
|
||||
From,
|
||||
Into,
|
||||
Mul,
|
||||
MulAssign,
|
||||
Ord,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Sub,
|
||||
SubAssign,
|
||||
)]
|
||||
pub struct Vector3<T> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
}
|
||||
|
||||
impl<T: Copy> Vector3<T> {
|
||||
pub fn xy(self) -> Vector2<T> {
|
||||
Vector2::new(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<[T; 2]> for Vector2<T> {
|
||||
fn from(from: [T; 2]) -> Self {
|
||||
Self {
|
||||
x: from[0],
|
||||
y: from[1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<Vector2<T>> for [T; 2] {
|
||||
fn from(from: Vector2<T>) -> Self {
|
||||
[from.x, from.y]
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_vector2_inside_polygon {
|
||||
($type:ty) => {
|
||||
impl Vector2<$type> {
|
||||
// Checks if the point is inside a polygon by casting a ray to the
|
||||
// right. Division is not used to avoid integer truncation errors.
|
||||
pub fn inside_polygon(&self, polygon: &[Vector2<$type>]) -> bool {
|
||||
let mut inside = false;
|
||||
let n = polygon.len();
|
||||
|
||||
// `self` is `v0`.
|
||||
|
||||
// `v1` is the previous vertex.
|
||||
let mut v1 = &polygon[n - 1];
|
||||
|
||||
// `v2` is the current vertex.
|
||||
for v2 in polygon.iter() {
|
||||
let dx12 = v2.x - v1.x;
|
||||
let dy12 = v2.y - v1.y;
|
||||
|
||||
// First, check if the line of the horizontal rightward ray
|
||||
// cast to actually crosses the vertical span of the current
|
||||
// `(v1, v2)` edge.
|
||||
if dy12 != (0 as $type) && (self.y > v1.y) != (self.y > v2.y) {
|
||||
let dx01 = self.x - v1.x;
|
||||
let dy01 = self.y - v1.y;
|
||||
|
||||
// Now check if the (v1, v2) edge is actually on the
|
||||
// right side of the ray and not on the left.
|
||||
//
|
||||
// This just compares the X coordinate of `self` (`v0`)
|
||||
// to the X coordinate of the intersection between the
|
||||
// horizontal rightward ray and the current `(v1, v2)` edge:
|
||||
//
|
||||
// `self.x < v1.x + (self.y - v1.y) * (dx12 / dy12)`
|
||||
//
|
||||
// but is algebraically simplified and rewritten to not
|
||||
// use division.
|
||||
let crosses = if dy12 > (0 as $type) {
|
||||
dx01 * dy12 < dx12 * dy01
|
||||
} else {
|
||||
dx01 * dy12 > dx12 * dy01
|
||||
};
|
||||
|
||||
// Even-odd rule: flip whether the point is inside or
|
||||
// outside upon each detected crossing.
|
||||
if crosses {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the current vertex previous for the next loop
|
||||
// iteration.
|
||||
v1 = v2;
|
||||
}
|
||||
|
||||
inside
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_vector2_inside_polygon!(i8);
|
||||
impl_vector2_inside_polygon!(i16);
|
||||
impl_vector2_inside_polygon!(i32);
|
||||
impl_vector2_inside_polygon!(i64);
|
||||
impl_vector2_inside_polygon!(i128);
|
||||
impl_vector2_inside_polygon!(u8);
|
||||
impl_vector2_inside_polygon!(u16);
|
||||
impl_vector2_inside_polygon!(u32);
|
||||
impl_vector2_inside_polygon!(u64);
|
||||
impl_vector2_inside_polygon!(u128);
|
||||
impl_vector2_inside_polygon!(f32);
|
||||
impl_vector2_inside_polygon!(f64);
|
||||
|
||||
macro_rules! impl_vector2_rotate_around_point {
|
||||
($type:ty) => {
|
||||
impl Vector2<$type> {
|
||||
pub fn rotate_around_point(&mut self, angle: $type, origin: Vector2<$type>) -> Self {
|
||||
let sin = angle.sin();
|
||||
let cos = angle.cos();
|
||||
|
||||
let tx = self.x - origin.x;
|
||||
let ty = self.y - origin.y;
|
||||
|
||||
let rx = tx * cos - ty * sin;
|
||||
let ry = tx * sin + ty * cos;
|
||||
|
||||
self.x = rx + origin.x;
|
||||
self.y = ry + origin.y;
|
||||
|
||||
*self
|
||||
}
|
||||
|
||||
pub fn rotate_around_point_degrees(
|
||||
&mut self,
|
||||
angle: $type,
|
||||
origin: Vector2<$type>,
|
||||
) -> Self {
|
||||
self.rotate_around_point(angle.to_radians(), origin)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_vector2_rotate_around_point!(f32);
|
||||
impl_vector2_rotate_around_point!(f64);
|
||||
|
||||
macro_rules! impl_polygon_centroid {
|
||||
($type:ty) => {
|
||||
impl Vector2<$type> {
|
||||
pub fn polygon_centroid(polygon: &[Vector2<$type>]) -> Self {
|
||||
let mut sum = Vector2::new(0 as $type, 0 as $type);
|
||||
|
||||
for vertex in polygon.iter() {
|
||||
sum += *vertex;
|
||||
}
|
||||
|
||||
sum / polygon.len() as $type
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_polygon_centroid!(i8);
|
||||
impl_polygon_centroid!(i16);
|
||||
impl_polygon_centroid!(i32);
|
||||
impl_polygon_centroid!(i64);
|
||||
impl_polygon_centroid!(u8);
|
||||
impl_polygon_centroid!(u16);
|
||||
impl_polygon_centroid!(u32);
|
||||
impl_polygon_centroid!(u64);
|
||||
impl_polygon_centroid!(f32);
|
||||
impl_polygon_centroid!(f64);
|
||||
Loading…
Reference in New Issue