diff --git a/Cargo.toml b/Cargo.toml index f03c5d2..1aeb82c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ resolver = "2" [workspace.dependencies] derive-getters = "0.5" derive_more = { version = "2.1", features = ["full"] } +num-traits = "0.2" serde = { version = "1", features = ["derive", "rc"] } thiserror = "2.0" undoredo = { version = "0.10", features = ["derive", "bidimap", "stable-vec", "rstar"] } diff --git a/topola-egui/src/display.rs b/topola-egui/src/display.rs index 2c2347e..e6625b3 100644 --- a/topola-egui/src/display.rs +++ b/topola-egui/src/display.rs @@ -39,7 +39,11 @@ impl Display { let layout = board.layout(); // Start from the bottom layer so that top layers are drawn on top. - for layer in (0..*layout.layer_count()).rev().map(LayerId::new) { + // The active layer is drawn last so it stays visible above the rest. + for layer in workspace + .appearance_panel + .layers_in_display_order(*layout.layer_count()) + { if !workspace.appearance_panel.visible[layer.index()] { continue; } @@ -212,7 +216,10 @@ impl Display { let board = workspace.workspace.board(); let layout = board.layout(); - for layer in (0..*layout.layer_count()).rev().map(LayerId::new) { + for layer in workspace + .appearance_panel + .layers_in_display_order(*layout.layer_count()) + { if !workspace.appearance_panel.visible[layer.index()] { continue; } @@ -294,7 +301,10 @@ impl Display { }; let autorouter = &autorouter_workspace.autorouter; - for layer in (0..*workspace.workspace.board().layout().layer_count()).map(LayerId::new) { + for layer in workspace + .appearance_panel + .layers_in_display_order(*workspace.workspace.board().layout().layer_count()) + { if workspace.appearance_panel.visible[layer.index()] { for navmesh in autorouter .router() diff --git a/topola-egui/src/layers_panel.rs b/topola-egui/src/layers_panel.rs index 3393d23..b1e5c40 100644 --- a/topola-egui/src/layers_panel.rs +++ b/topola-egui/src/layers_panel.rs @@ -146,7 +146,7 @@ impl LayersPanel { Self { dark_colors, light_colors, - active: LayerId::new(0), + active: board.layer_id("F.Cu").unwrap_or(LayerId::new(0)), visible, } } @@ -207,4 +207,17 @@ impl LayersPanel { self.colors(ctx).layers.colors(layer_desc).normal } } + + pub fn layers_in_display_order(&self, layer_count: usize) -> Vec { + let active = self.active.index(); + let mut layers = (0..layer_count) + .rev() + .filter(|&layer| layer != active) + .map(LayerId::new) + .collect::>(); + + // The active layer should be drawn on top. + layers.push(self.active); + layers + } } diff --git a/topola/Cargo.toml b/topola/Cargo.toml index fb40f4a..96a6597 100644 --- a/topola/Cargo.toml +++ b/topola/Cargo.toml @@ -14,6 +14,7 @@ dearcut = { version = "0.3", features = ["serde", "undoredo"] } derive-getters.workspace = true derive_more.workspace = true i_triangle = "0.40" +num-traits.workspace = true polygon_unionfind = "0.7" rstar = "0.12" serde.workspace = true diff --git a/topola/src/board/interactors/drag_selection.rs b/topola/src/board/interactors/drag_selection.rs index d1f7b82..d805808 100644 --- a/topola/src/board/interactors/drag_selection.rs +++ b/topola/src/board/interactors/drag_selection.rs @@ -64,12 +64,12 @@ impl DragSelectionInteractor { SelectionContainMode::Crossing => { self.selection .components - .add(board.locate_components_intersecting_rect(rect)); + .add(board.locate_components_prefer_layer_intersecting_rect(rect)); } SelectionContainMode::Window => { self.selection .components - .add(board.locate_components_inside_rect(rect)); + .add(board.locate_components_prefer_layer_inside_rect(rect)); } } @@ -77,10 +77,12 @@ impl DragSelectionInteractor { SelectionContainMode::Crossing => { self.selection .nets - .add(board.locate_nets_intersecting_rect(rect)); + .add(board.locate_nets_prefer_layer_intersecting_rect(rect)); } SelectionContainMode::Window => { - self.selection.nets.add(board.locate_nets_inside_rect(rect)); + self.selection + .nets + .add(board.locate_nets_prefer_layer_inside_rect(rect)); } } @@ -88,10 +90,12 @@ impl DragSelectionInteractor { SelectionContainMode::Crossing => { self.selection .pins - .add(board.locate_pins_intersecting_rect(rect)); + .add(board.locate_pins_prefer_layer_intersecting_rect(rect)); } SelectionContainMode::Window => { - self.selection.pins.add(board.locate_pins_inside_rect(rect)); + self.selection + .pins + .add(board.locate_pins_prefer_layer_inside_rect(rect)); } } diff --git a/topola/src/board/interactors/selection.rs b/topola/src/board/interactors/selection.rs index 20cc315..30d8daa 100644 --- a/topola/src/board/interactors/selection.rs +++ b/topola/src/board/interactors/selection.rs @@ -47,25 +47,16 @@ impl SelectionInteractor { if input.release && input.pointer == self.origin { let mut selection = self.original_selection.clone(); + let point = Vector3::new(input.pointer.x, input.pointer.y, layer.index() as i64); // Pins have intentional precedence over nets and components. - if let Some(pin_selector) = board.locate_pin_at_point(Vector3::new( - input.pointer.x, - input.pointer.y, - layer.index() as i64, - )) { + if let Some(pin_selector) = board.locate_pins_prefer_layer_at_point(point).next() { selection.pins.xor(std::iter::once(pin_selector)); - } else if let Some(net_selector) = board.locate_net_at_point(Vector3::new( - input.pointer.x, - input.pointer.y, - layer.index() as i64, - )) { + } else if let Some(net_selector) = board.locate_nets_prefer_layer_at_point(point).next() { selection.nets.xor(std::iter::once(net_selector)); - } else if let Some(component_selector) = board.locate_component_at_point(Vector3::new( - input.pointer.x, - input.pointer.y, - layer.index() as i64, - )) { + } else if let Some(component_selector) = + board.locate_components_prefer_layer_at_point(point).next() + { selection .components .xor(std::iter::once(component_selector)); diff --git a/topola/src/board/locate.rs b/topola/src/board/locate.rs index 47104b1..c426741 100644 --- a/topola/src/board/locate.rs +++ b/topola/src/board/locate.rs @@ -11,24 +11,76 @@ use crate::{ }; impl Board { - pub fn locate_component_at_point(&self, point: Vector3) -> Option { - if let Some(joint_id) = self.layout.locate_joints_at_point(point).next() { - return self.joint_component_selector(joint_id); - } + pub fn locate_components_prefer_layer_at_point( + &self, + point: Vector3, + ) -> impl Iterator + '_ { + self.layout + .locate_joints_prefer_layer_at_point(point) + .filter_map(|joint_id| self.joint_component_selector(joint_id)) + .chain( + self.layout + .locate_segments_prefer_layer_at_point(point) + .filter_map(|segment_id| self.segment_component_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_prefer_layer_at_point(point) + .filter_map(|via_id| self.via_component_selector(via_id)), + ) + .chain( + self.layout + .locate_polygons_prefer_layer_at_point(point) + .filter_map(|polygon_id| self.polygon_component_selector(polygon_id)), + ) + } - if let Some(segment_id) = self.layout.locate_segments_at_point(point).next() { - return self.segment_component_selector(segment_id); - } + pub fn locate_components_at_point( + &self, + point: Vector3, + ) -> impl Iterator + '_ { + self.layout + .locate_joints_at_point(point) + .filter_map(|joint_id| self.joint_component_selector(joint_id)) + .chain( + self.layout + .locate_segments_at_point(point) + .filter_map(|segment_id| self.segment_component_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_at_point(point) + .filter_map(|via_id| self.via_component_selector(via_id)), + ) + .chain( + self.layout + .locate_polygons_at_point(point) + .filter_map(|polygon_id| self.polygon_component_selector(polygon_id)), + ) + } - if let Some(via_id) = self.layout.locate_vias_at_point(point).next() { - return self.via_component_selector(via_id); - } - - if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() { - return self.polygon_component_selector(polygon_id); - } - - None + pub fn locate_components_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + self.layout + .locate_joints_prefer_layer_intersecting_rect(rect) + .filter_map(|joint_id| self.joint_component_selector(joint_id)) + .chain( + self.layout + .locate_segments_prefer_layer_intersecting_rect(rect) + .filter_map(|segment_id| self.segment_component_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_prefer_layer_intersecting_rect(rect) + .filter_map(|via_id| self.via_component_selector(via_id)), + ) + .chain( + self.layout + .locate_polygons_prefer_layer_intersecting_rect(rect) + .filter_map(|polygon_id| self.polygon_component_selector(polygon_id)), + ) } pub fn locate_components_intersecting_rect( @@ -55,48 +107,117 @@ impl Board { ) } + pub fn locate_components_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + self.layout + .locate_joints_prefer_layer_inside_rect(rect) + .filter_map(|joint_id| self.joint_component_selector(joint_id)) + .chain( + self.layout + .locate_segments_prefer_layer_inside_rect(rect) + .filter_map(|segment_id| self.segment_component_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_prefer_layer_inside_rect(rect) + .filter_map(|via_id| self.via_component_selector(via_id)), + ) + .chain( + self.layout + .locate_polygons_prefer_layer_inside_rect(rect) + .filter_map(|polygon_id| self.polygon_component_selector(polygon_id)), + ) + } + pub fn locate_components_inside_rect( &self, rect: Rect3, ) -> impl Iterator + '_ { self.layout - .locate_joints_inside_rect(rect) + .locate_joints_prefer_layer_inside_rect(rect) .filter_map(|joint_id| self.joint_component_selector(joint_id)) .chain( self.layout - .locate_segments_inside_rect(rect) + .locate_segments_prefer_layer_inside_rect(rect) .filter_map(|segment_id| self.segment_component_selector(segment_id)), ) .chain( self.layout - .locate_vias_inside_rect(rect) + .locate_vias_prefer_layer_inside_rect(rect) .filter_map(|via_id| self.via_component_selector(via_id)), ) .chain( self.layout - .locate_polygons_inside_rect(rect) + .locate_polygons_prefer_layer_inside_rect(rect) .filter_map(|polygon_id| self.polygon_component_selector(polygon_id)), ) } - pub fn locate_net_at_point(&self, point: Vector3) -> Option { - if let Some(joint_id) = self.layout.locate_joints_at_point(point).next() { - return self.joint_net_selector(joint_id); + pub fn locate_nets_prefer_layer_at_point( + &self, + point: Vector3, + ) -> impl Iterator + '_ { + self.layout + .locate_joints_prefer_layer_at_point(point) + .filter_map(|joint_id| self.joint_net_selector(joint_id)) + .chain( + self.layout + .locate_segments_prefer_layer_at_point(point) + .filter_map(|segment_id| self.segment_net_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_prefer_layer_at_point(point) + .filter_map(|via_id| self.via_net_selector(via_id)), + ) + .chain( + self.layout + .locate_polygons_prefer_layer_at_point(point) + .filter_map(|polygon_id| self.polygon_net_selector(polygon_id)), + ) + } + + pub fn locate_nets_at_point( + &self, + point: Vector3, + ) -> impl Iterator + '_ { + self.layout + .locate_joints_at_point(point) + .filter_map(|joint_id| self.joint_net_selector(joint_id)) + .chain( + self.layout + .locate_segments_at_point(point) + .filter_map(|segment_id| self.segment_net_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_at_point(point) + .filter_map(|via_id| self.via_net_selector(via_id)), + ) + .chain( + self.layout + .locate_polygons_at_point(point) + .filter_map(|polygon_id| self.polygon_net_selector(polygon_id)), + ) + } + + pub fn locate_nets_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + let mut selectors = BTreeSet::new(); + + for net_id in self.layout.locate_nets_prefer_layer_intersecting_rect(rect) { + let Some(net_name) = self.net_name(net_id) else { + continue; + }; + + selectors.insert(NetSelector::new(net_name.to_string())); } - if let Some(segment_id) = self.layout.locate_segments_at_point(point).next() { - return self.segment_net_selector(segment_id); - } - - if let Some(via_id) = self.layout.locate_vias_at_point(point).next() { - return self.via_net_selector(via_id); - } - - if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() { - return self.polygon_net_selector(polygon_id); - } - - None + selectors.into_iter() } pub fn locate_nets_intersecting_rect( @@ -116,6 +237,23 @@ impl Board { selectors.into_iter() } + pub fn locate_nets_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + let mut selectors = BTreeSet::new(); + + for net_id in self.layout.locate_nets_prefer_layer_inside_rect(rect) { + let Some(net_name) = self.net_name(net_id) else { + continue; + }; + + selectors.insert(NetSelector::new(net_name.to_string())); + } + + selectors.into_iter() + } + pub fn locate_nets_inside_rect( &self, rect: Rect3, @@ -133,25 +271,76 @@ impl Board { selectors.into_iter() } - pub fn locate_pin_at_point(&self, point: Vector3) -> Option { - // Polygons have intentional precedence for pins. - if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() { - return self.polygon_pin_selector(polygon_id); - } + pub fn locate_pins_prefer_layer_at_point( + &self, + point: Vector3, + ) -> impl Iterator + '_ { + self.layout + .locate_polygons_prefer_layer_at_point(point) + .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)) + .chain( + self.layout + .locate_joints_prefer_layer_at_point(point) + .filter_map(|joint_id| self.joint_pin_selector(joint_id)), + ) + .chain( + self.layout + .locate_segments_prefer_layer_at_point(point) + .filter_map(|segment_id| self.segment_pin_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_prefer_layer_at_point(point) + .filter_map(|via_id| self.via_pin_selector(via_id)), + ) + } - if let Some(joint_id) = self.layout.locate_joints_at_point(point).next() { - return self.joint_pin_selector(joint_id); - } + pub fn locate_pins_at_point( + &self, + point: Vector3, + ) -> impl Iterator + '_ { + self.layout + .locate_polygons_at_point(point) + .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)) + .chain( + self.layout + .locate_joints_at_point(point) + .filter_map(|joint_id| self.joint_pin_selector(joint_id)), + ) + .chain( + self.layout + .locate_segments_at_point(point) + .filter_map(|segment_id| self.segment_pin_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_at_point(point) + .filter_map(|via_id| self.via_pin_selector(via_id)), + ) + } - if let Some(segment_id) = self.layout.locate_segments_at_point(point).next() { - return self.segment_pin_selector(segment_id); - } - - if let Some(via_id) = self.layout.locate_vias_at_point(point).next() { - return self.via_pin_selector(via_id); - } - - None + pub fn locate_pins_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + self.layout + .locate_polygons_prefer_layer_intersecting_rect(rect) + .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)) + .chain( + self.layout + .locate_joints_prefer_layer_intersecting_rect(rect) + .filter_map(|joint_id| self.joint_pin_selector(joint_id)), + ) + .chain( + self.layout + .locate_segments_prefer_layer_intersecting_rect(rect) + .filter_map(|segment_id| self.segment_pin_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_prefer_layer_intersecting_rect(rect) + .filter_map(|via_id| self.via_pin_selector(via_id)), + ) } pub fn locate_pins_intersecting_rect( @@ -159,8 +348,13 @@ impl Board { rect: Rect3, ) -> impl Iterator + '_ { self.layout - .locate_joints_intersecting_rect(rect) - .filter_map(|joint_id| self.joint_pin_selector(joint_id)) + .locate_polygons_intersecting_rect(rect) + .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)) + .chain( + self.layout + .locate_joints_intersecting_rect(rect) + .filter_map(|joint_id| self.joint_pin_selector(joint_id)), + ) .chain( self.layout .locate_segments_intersecting_rect(rect) @@ -171,10 +365,29 @@ impl Board { .locate_vias_intersecting_rect(rect) .filter_map(|via_id| self.via_pin_selector(via_id)), ) + } + + pub fn locate_pins_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator + '_ { + self.layout + .locate_polygons_prefer_layer_inside_rect(rect) + .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)) .chain( self.layout - .locate_polygons_intersecting_rect(rect) - .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)), + .locate_joints_prefer_layer_inside_rect(rect) + .filter_map(|joint_id| self.joint_pin_selector(joint_id)), + ) + .chain( + self.layout + .locate_segments_prefer_layer_inside_rect(rect) + .filter_map(|segment_id| self.segment_pin_selector(segment_id)), + ) + .chain( + self.layout + .locate_vias_prefer_layer_inside_rect(rect) + .filter_map(|via_id| self.via_pin_selector(via_id)), ) } @@ -183,8 +396,13 @@ impl Board { rect: Rect3, ) -> impl Iterator + '_ { self.layout - .locate_joints_inside_rect(rect) - .filter_map(|joint_id| self.joint_pin_selector(joint_id)) + .locate_polygons_inside_rect(rect) + .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)) + .chain( + self.layout + .locate_joints_inside_rect(rect) + .filter_map(|joint_id| self.joint_pin_selector(joint_id)), + ) .chain( self.layout .locate_segments_inside_rect(rect) @@ -195,10 +413,5 @@ impl Board { .locate_vias_inside_rect(rect) .filter_map(|via_id| self.via_pin_selector(via_id)), ) - .chain( - self.layout - .locate_polygons_inside_rect(rect) - .filter_map(|polygon_id| self.polygon_pin_selector(polygon_id)), - ) } } diff --git a/topola/src/layout/locate.rs b/topola/src/layout/locate.rs index 8e53736..3a922af 100644 --- a/topola/src/layout/locate.rs +++ b/topola/src/layout/locate.rs @@ -5,19 +5,64 @@ use std::collections::BTreeSet; use crate::{ - Rect3, Vector3, - layout::{LayerId, Layout, compounds::NetId}, + Rect2, Rect3, Vector2, Vector3, + layout::{Layout, compounds::NetId}, primitives::{JointId, PolygonId, SegmentId, ViaId}, }; impl Layout { + pub fn locate_joints_prefer_layer_at_point( + &self, + point: Vector3, + ) -> impl Iterator { + let at_point = self.locate_joints_at_point(point).collect::>(); + + let joints = if at_point.is_empty() { + self.locate_joints_any_layer_at_point(point.xy()).collect() + } else { + at_point + }; + + joints.into_iter() + } + pub fn locate_joints_at_point(&self, point: Vector3) -> impl Iterator { - let point2 = point.xy(); self.joints_rtree .as_ref() .locate_all_at_point(&[point.x, point.y, point.z]) .map(|geom_with_data| geom_with_data.data) - .filter(move |&joint_id| self.joints[joint_id.index()].contains_point(point2)) + .filter(move |&joint_id| self.joints[joint_id.index()].contains_point2(point.xy())) + } + + pub fn locate_joints_any_layer_at_point( + &self, + point: Vector2, + ) -> impl Iterator { + let envelope = point.z_extruded_infinitely().aabb(); + + self.joints_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&joint_id| self.joints[joint_id.index()].contains_point2(point)) + } + + pub fn locate_joints_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_rect = self + .locate_joints_intersecting_rect(rect) + .collect::>(); + + let joints = if at_rect.is_empty() { + self.locate_joints_any_layer_intersecting_rect(rect.xy()) + .collect() + } else { + at_rect + }; + + joints.into_iter() } pub fn locate_joints_intersecting_rect( @@ -26,33 +71,128 @@ impl Layout { ) -> impl Iterator { self.joints_rtree .as_ref() - .locate_in_envelope_intersecting(&rect.aabb3()) + .locate_in_envelope_intersecting(&rect.aabb()) .map(|geom_with_data| geom_with_data.data) .filter(move |&joint_id| { let joint = self.joint(joint_id); - rect.rect2() + rect.xy() .intersects_circle(joint.spec.position, joint.spec.radius as i64) }) } + pub fn locate_joints_any_layer_intersecting_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.joints_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&joint_id| { + let joint = self.joint(joint_id); + rect.intersects_circle(joint.spec.position, joint.spec.radius as i64) + }) + } + + pub fn locate_joints_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_rect = self.locate_joints_inside_rect(rect).collect::>(); + + let joints = if at_rect.is_empty() { + self.locate_joints_any_layer_inside_rect(rect.xy()) + .collect() + } else { + at_rect + }; + + joints.into_iter() + } + pub fn locate_joints_inside_rect(&self, rect: Rect3) -> impl Iterator { self.joints_rtree .as_ref() - .locate_in_envelope(&rect.aabb3()) + .locate_in_envelope(&rect.aabb()) .map(|geom_with_data| geom_with_data.data) .filter(move |&joint_id| { let joint = self.joint(joint_id); - rect.rect2() + rect.xy() .contains_circle(joint.spec.position, joint.spec.radius as i64) }) } + pub fn locate_joints_any_layer_inside_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.joints_rtree + .as_ref() + .locate_in_envelope(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&joint_id| { + let joint = self.joint(joint_id); + rect.intersects_circle(joint.spec.position, joint.spec.radius as i64) + }) + } + + pub fn locate_segments_prefer_layer_at_point( + &self, + point: Vector3, + ) -> impl Iterator { + let at_point = self.locate_segments_at_point(point).collect::>(); + + let segments = if at_point.is_empty() { + self.locate_segments_any_layer_at_point(point.xy()) + .collect() + } else { + at_point + }; + + segments.into_iter() + } + pub fn locate_segments_at_point(&self, point: Vector3) -> impl Iterator { 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(point.xy())) + .filter(move |&segment_id| self.segment(segment_id).contains_point2(point.xy())) + } + + pub fn locate_segments_any_layer_at_point( + &self, + point: Vector2, + ) -> impl Iterator { + let envelope = point.z_extruded_infinitely().aabb(); + + self.segments_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&segment_id| self.segments[segment_id.index()].contains_point2(point)) + } + + pub fn locate_segments_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_point = self + .locate_segments_intersecting_rect(rect) + .collect::>(); + + let joints = if at_point.is_empty() { + self.locate_segments_any_layer_intersecting_rect(rect.xy()) + .collect() + } else { + at_point + }; + + joints.into_iter() } pub fn locate_segments_intersecting_rect( @@ -61,66 +201,251 @@ impl Layout { ) -> impl Iterator { self.segments_rtree .as_ref() - .locate_in_envelope_intersecting(&rect.aabb3()) + .locate_in_envelope_intersecting(&rect.aabb()) .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()) + rect.xy().intersects_polygon(&segment.bounding_rectangle()) }) } + pub fn locate_segments_any_layer_intersecting_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.segments_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&segment_id| { + let segment = self.segment(segment_id); + rect.intersects_polygon(&segment.bounding_rectangle()) + }) + } + + pub fn locate_segments_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_point = self.locate_segments_inside_rect(rect).collect::>(); + + let joints = if at_point.is_empty() { + self.locate_segments_any_layer_inside_rect(rect.xy()) + .collect() + } else { + at_point + }; + + joints.into_iter() + } + pub fn locate_segments_inside_rect(&self, rect: Rect3) -> impl Iterator { self.segments_rtree .as_ref() - .locate_in_envelope(&rect.aabb3()) + .locate_in_envelope(&rect.aabb()) .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()) + rect.xy().contains_polygon(&segment.bounding_rectangle()) }) } + pub fn locate_segments_any_layer_inside_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.segments_rtree + .as_ref() + .locate_in_envelope(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&segment_id| { + let segment = self.segment(segment_id); + rect.contains_polygon(&segment.bounding_rectangle()) + }) + } + + pub fn locate_vias_prefer_layer_at_point( + &self, + point: Vector3, + ) -> impl Iterator { + let at_point = self.locate_vias_at_point(point).collect::>(); + + let vias = if at_point.is_empty() { + self.locate_vias_any_layer_at_point(point.xy()).collect() + } else { + at_point + }; + + vias.into_iter() + } + pub fn locate_vias_at_point(&self, point: Vector3) -> impl Iterator { - let layer = LayerId::new(point.z as usize); 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, point.xy())) + .filter(move |&via_id| self.vias[via_id.index()].contains_point2(point.xy())) + } + + pub fn locate_vias_any_layer_at_point( + &self, + point: Vector2, + ) -> impl Iterator { + let envelope = point.z_extruded_infinitely().aabb(); + + self.vias_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&via_id| self.vias[via_id.index()].contains_point2(point)) + } + + pub fn locate_vias_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_point = self.locate_vias_intersecting_rect(rect).collect::>(); + + let vias = if at_point.is_empty() { + self.locate_vias_any_layer_intersecting_rect(rect.xy()) + .collect() + } else { + at_point + }; + + vias.into_iter() } pub fn locate_vias_intersecting_rect(&self, rect: Rect3) -> impl Iterator { self.vias_rtree .as_ref() - .locate_in_envelope_intersecting(&rect.aabb3()) + .locate_in_envelope_intersecting(&rect.aabb()) .map(|geom_with_data| geom_with_data.data) .filter(move |&via_id| { let via = self.via(via_id); - rect.rect2() + rect.xy() .intersects_circle(via.position, via.spec.radius as i64) }) } + pub fn locate_vias_any_layer_intersecting_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.vias_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&via_id| { + let via = self.via(via_id); + rect.intersects_circle(via.position, via.spec.radius as i64) + }) + } + + pub fn locate_vias_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_rect = self.locate_vias_inside_rect(rect).collect::>(); + + let vias = if at_rect.is_empty() { + self.locate_vias_any_layer_inside_rect(rect.xy()).collect() + } else { + at_rect + }; + + vias.into_iter() + } + pub fn locate_vias_inside_rect(&self, rect: Rect3) -> impl Iterator { self.vias_rtree .as_ref() - .locate_in_envelope(&rect.aabb3()) + .locate_in_envelope(&rect.aabb()) .map(|geom_with_data| geom_with_data.data) .filter(move |&via_id| { let via = self.via(via_id); - rect.rect2() + rect.xy() .contains_circle(via.position, via.spec.radius as i64) }) } + pub fn locate_vias_any_layer_inside_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.vias_rtree + .as_ref() + .locate_in_envelope(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&via_id| { + let via = self.via(via_id); + rect.contains_circle(via.position, via.spec.radius as i64) + }) + } + + pub fn locate_polygons_prefer_layer_at_point( + &self, + point: Vector3, + ) -> impl Iterator { + let at_point = self.locate_polygons_at_point(point).collect::>(); + + let polygons = if at_point.is_empty() { + self.locate_polygons_any_layer_at_point(point.xy()) + .collect() + } else { + at_point + }; + + polygons.into_iter() + } + pub fn locate_polygons_at_point(&self, point: Vector3) -> impl Iterator { - let point2 = point.xy(); self.polygons_rtree .as_ref() .locate_all_at_point(&[point.x, point.y, point.z]) .map(|geom_with_data| geom_with_data.data) - .filter(move |&polygon_id| self.polygons[polygon_id.index()].contains_point(point2)) + .filter(move |&polygon_id| { + self.polygons[polygon_id.index()].contains_point2(point.xy()) + }) + } + + pub fn locate_polygons_any_layer_at_point( + &self, + point: Vector2, + ) -> impl Iterator { + let envelope = point.z_extruded_infinitely().aabb(); + + self.polygons_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&polygon_id| self.polygons[polygon_id.index()].contains_point2(point)) + } + + pub fn locate_polygons_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_rect = self + .locate_polygons_intersecting_rect(rect) + .collect::>(); + + let polygons = if at_rect.is_empty() { + self.locate_polygons_any_layer_intersecting_rect(rect.xy()) + .collect() + } else { + at_rect + }; + + polygons.into_iter() } pub fn locate_polygons_intersecting_rect( @@ -129,25 +454,98 @@ impl Layout { ) -> impl Iterator { self.polygons_rtree .as_ref() - .locate_in_envelope_intersecting(&rect.aabb3()) + .locate_in_envelope_intersecting(&rect.aabb()) .map(|geom_with_data| geom_with_data.data) .filter(move |&polygon_id| { let polygon = self.polygon(polygon_id); - rect.rect2().intersects_polygon(&polygon.vertices) + rect.xy().intersects_polygon(&polygon.vertices) }) } + pub fn locate_polygons_any_layer_intersecting_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.polygons_rtree + .as_ref() + .locate_in_envelope_intersecting(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&polygon_id| { + let polygon = self.polygon(polygon_id); + rect.intersects_polygon(&polygon.vertices) + }) + } + + pub fn locate_polygons_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let at_rect = self.locate_polygons_inside_rect(rect).collect::>(); + + let polygons = if at_rect.is_empty() { + self.locate_polygons_any_layer_inside_rect(rect.xy()) + .collect() + } else { + at_rect + }; + + polygons.into_iter() + } + pub fn locate_polygons_inside_rect(&self, rect: Rect3) -> impl Iterator { self.polygons_rtree .as_ref() - .locate_in_envelope(&rect.aabb3()) + .locate_in_envelope(&rect.aabb()) .map(|geom_with_data| geom_with_data.data) .filter(move |&polygon_id| { let polygon = self.polygon(polygon_id); - rect.rect2().contains_polygon(&polygon.vertices) + rect.xy().contains_polygon(&polygon.vertices) }) } + pub fn locate_polygons_any_layer_inside_rect( + &self, + rect: Rect2, + ) -> impl Iterator { + let envelope = rect.z_extruded_infinitely().aabb(); + + self.polygons_rtree + .as_ref() + .locate_in_envelope(&envelope) + .map(|geom_with_data| geom_with_data.data) + .filter(move |&polygon_id| { + let polygon = self.polygon(polygon_id); + rect.contains_polygon(&polygon.vertices) + }) + } + + pub fn locate_nets_prefer_layer_intersecting_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let mut nets = BTreeSet::new(); + + for joint_id in self.locate_joints_prefer_layer_intersecting_rect(rect) { + nets.insert(self.joint(joint_id).spec.net); + } + + for segment_id in self.locate_segments_prefer_layer_intersecting_rect(rect) { + nets.insert(self.segment(segment_id).net); + } + + for via_id in self.locate_vias_prefer_layer_intersecting_rect(rect) { + nets.insert(self.via(via_id).net); + } + + for polygon_id in self.locate_polygons_prefer_layer_intersecting_rect(rect) { + nets.insert(self.polygon(polygon_id).net); + } + + nets.into_iter() + } + pub fn locate_nets_intersecting_rect(&self, rect: Rect3) -> impl Iterator { let mut nets = BTreeSet::new(); @@ -170,6 +568,31 @@ impl Layout { nets.into_iter() } + pub fn locate_nets_prefer_layer_inside_rect( + &self, + rect: Rect3, + ) -> impl Iterator { + let mut nets = BTreeSet::new(); + + for joint_id in self.locate_joints_prefer_layer_inside_rect(rect) { + nets.insert(self.joint(joint_id).spec.net); + } + + for segment_id in self.locate_segments_prefer_layer_inside_rect(rect) { + nets.insert(self.segment(segment_id).net); + } + + for via_id in self.locate_vias_prefer_layer_inside_rect(rect) { + nets.insert(self.via(via_id).net); + } + + for polygon_id in self.locate_polygons_prefer_layer_inside_rect(rect) { + nets.insert(self.polygon(polygon_id).net); + } + + nets.into_iter() + } + pub fn locate_nets_inside_rect(&self, rect: Rect3) -> impl Iterator { let mut nets = BTreeSet::new(); diff --git a/topola/src/layout/primitives/joint.rs b/topola/src/layout/primitives/joint.rs index 41b4890..1cb22d0 100644 --- a/topola/src/layout/primitives/joint.rs +++ b/topola/src/layout/primitives/joint.rs @@ -72,7 +72,7 @@ impl Joint { )) } - pub fn contains_point(&self, point: Vector2) -> bool { + pub fn contains_point2(&self, point: Vector2) -> bool { (point.x - self.spec.position.x).pow(2) as u64 + (point.y - self.spec.position.y).pow(2) as u64 <= self.spec.radius.pow(2) diff --git a/topola/src/layout/primitives/polygon.rs b/topola/src/layout/primitives/polygon.rs index 3c498e6..05db7fe 100644 --- a/topola/src/layout/primitives/polygon.rs +++ b/topola/src/layout/primitives/polygon.rs @@ -61,7 +61,7 @@ impl Polygon { Vector2::::polygon_centroid(&self.vertices) } - pub fn contains_point(&self, point: Vector2) -> bool { + pub fn contains_point2(&self, point: Vector2) -> bool { point.inside_polygon(&self.vertices) } } diff --git a/topola/src/layout/primitives/segment.rs b/topola/src/layout/primitives/segment.rs index beac50a..a9ee387 100644 --- a/topola/src/layout/primitives/segment.rs +++ b/topola/src/layout/primitives/segment.rs @@ -57,7 +57,7 @@ impl Segment { (self.endpoints[0] + self.endpoints[1]) / 2 } - pub fn contains_point(&self, point: Vector2) -> bool { + pub fn contains_point2(&self, point: Vector2) -> bool { let vertices = crate::math::inflated_segment( self.endpoints[0].x, self.endpoints[0].y, diff --git a/topola/src/layout/primitives/via.rs b/topola/src/layout/primitives/via.rs index 1782316..f8b5c63 100644 --- a/topola/src/layout/primitives/via.rs +++ b/topola/src/layout/primitives/via.rs @@ -54,11 +54,7 @@ pub struct Via { } impl Via { - pub fn contains_point(&self, layer: LayerId, point: Vector2) -> bool { - if layer < self.min_layer || layer > self.max_layer { - return false; - } - + pub fn contains_point2(&self, point: Vector2) -> bool { (point.x - self.position.x).pow(2) as u64 + (point.y - self.position.y).pow(2) as u64 <= self.spec.radius.pow(2) } diff --git a/topola/src/rect.rs b/topola/src/rect.rs index 326baeb..66aaf65 100644 --- a/topola/src/rect.rs +++ b/topola/src/rect.rs @@ -3,15 +3,37 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use derive_getters::Getters; +use derive_more::Constructor; +use num_traits::Bounded; use rstar::{AABB, RTreeNum}; use serde::{Deserialize, Serialize}; use crate::{Vector2, Vector3}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)] +#[derive( + Clone, Constructor, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize, +)] pub struct Rect2 { - min: Vector2, - max: Vector2, + pub min: Vector2, + pub max: Vector2, +} + +impl Rect2 { + pub fn z_extruded(self, from: T, to: T) -> Rect3 { + Rect3 { + min: Vector3::new(self.min.x, self.min.y, std::cmp::min(from, to)), + max: Vector3::new(self.max.x, self.max.y, std::cmp::max(from, to)), + } + } +} + +impl Rect2 { + pub fn z_extruded_infinitely(self) -> Rect3 { + Rect3 { + min: Vector3::new(self.min.x, self.min.y, Bounded::min_value()), + max: Vector3::new(self.max.x, self.max.y, Bounded::max_value()), + } + } } impl Rect2 { @@ -25,18 +47,9 @@ impl Rect2 { } } -impl Rect2 { - pub fn new(from: Vector2, to: Vector2) -> 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 { - 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]) + pub fn aabb(self) -> AABB<[T; 2]> { + AABB::from_corners([self.min.x, self.min.y], [self.max.x, self.max.y]) } } @@ -172,8 +185,8 @@ impl_rect2_intersects_polygon!(f64); #[derive(Clone, Copy, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize)] pub struct Rect3 { - min: Vector3, - max: Vector3, + pub min: Vector3, + pub max: Vector3, } impl Rect3 { @@ -194,13 +207,13 @@ impl Rect3 { } impl Rect3 { - pub fn rect2(&self) -> Rect2 { + pub fn xy(self) -> Rect2 { Rect2::new(self.min.xy(), self.max.xy()) } } impl Rect3 { - pub fn aabb3(&self) -> AABB<[T; 3]> { + pub fn aabb(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], diff --git a/topola/src/vector.rs b/topola/src/vector.rs index aa5aa1c..801d2e5 100644 --- a/topola/src/vector.rs +++ b/topola/src/vector.rs @@ -5,8 +5,11 @@ use derive_more::{ Add, AddAssign, Constructor, Div, DivAssign, From, Into, Mul, MulAssign, Sub, SubAssign, }; +use num_traits::Bounded; use serde::{Deserialize, Serialize}; +use crate::Rect3; + #[derive( Add, AddAssign, @@ -34,40 +37,6 @@ pub struct Vector2 { 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 { - pub x: T, - pub y: T, - pub z: T, -} - -impl Vector3 { - pub fn xy(self) -> Vector2 { - Vector2::new(self.x, self.y) - } -} - impl From<[T; 2]> for Vector2 { fn from(from: [T; 2]) -> Self { Self { @@ -83,6 +52,24 @@ impl From> for [T; 2] { } } +impl Vector2 { + pub fn z_extruded(self, from: T, to: T) -> Rect3 { + Rect3 { + min: Vector3::new(self.x, self.y, std::cmp::min(from, to)), + max: Vector3::new(self.x, self.y, std::cmp::max(from, to)), + } + } +} + +impl Vector2 { + pub fn z_extruded_infinitely(self) -> Rect3 { + Rect3 { + min: Vector3::new(self.x, self.y, Bounded::min_value()), + max: Vector3::new(self.x, self.y, Bounded::max_value()), + } + } +} + macro_rules! impl_vector2_inside_polygon { ($type:ty) => { impl Vector2<$type> { @@ -216,3 +203,37 @@ impl_polygon_centroid!(u32); impl_polygon_centroid!(u64); impl_polygon_centroid!(f32); impl_polygon_centroid!(f64); + +#[derive( + Add, + AddAssign, + Clone, + Constructor, + Copy, + Debug, + Deserialize, + Div, + DivAssign, + Eq, + From, + Into, + Mul, + MulAssign, + Ord, + PartialEq, + PartialOrd, + Serialize, + Sub, + SubAssign, +)] +pub struct Vector3 { + pub x: T, + pub y: T, + pub z: T, +} + +impl Vector3 { + pub fn xy(self) -> Vector2 { + Vector2::new(self.x, self.y) + } +}