// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT //! Layout module for handling board geometry. mod collect_bands; pub mod poly; use derive_getters::Getters; use geo::Point; use rstar::AABB; use crate::{ drawing::{ band::{BandTermsegIndex, BandUid}, bend::{BendIndex, BendWeight, LooseBendWeight}, dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, LooseDotIndex, LooseDotWeight}, gear::GearIndex, graph::{MakePrimitive, PrimitiveIndex}, loose::LooseIndex, primitive::{GetWeight, MakePrimitiveShape, Primitive}, rules::AccessRules, seg::{ FixedSegIndex, FixedSegWeight, LoneLooseSegIndex, LoneLooseSegWeight, SegIndex, SegWeight, SeqLooseSegIndex, SeqLooseSegWeight, }, Cane, Collect, Drawing, DrawingEdit, DrawingException, Infringement, }, geometry::{ compound::ManageCompounds, edit::ApplyGeometryEdit, primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape}, shape::{AccessShape, Shape}, GenericNode, GetSetPos, NodeMetadata, }, graph::{GenericIndex, GetPetgraphIndex, MakeRef}, layout::poly::{add_poly_with_nodes_intern, MakePolygon, PolyWeight}, math::{LineIntersection, NormalLine, RotationSense}, }; /// Represents a weight for various compounds #[derive(Clone, Copy, Debug)] pub enum CompoundWeight { /// Represents the weight of a polygon compound, includes its basic [`Layout`] information Poly(PolyWeight), } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CompoundEntryLabel { Normal, NotInConvexHull, } /// The alias to differ node types pub type NodeIndex = GenericNode>; pub type LayoutEdit = DrawingEdit; #[derive(Clone, Debug, Getters)] /// Structure for managing the Layout design pub struct Layout { pub(super) drawing: Drawing, } impl Layout { pub fn new(drawing: Drawing) -> Self { Self { drawing } } } impl Layout { /// Insert [`Cane`] object into the [`Layout`] pub fn insert_cane( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, from: DotIndex, around: GearIndex, dot_weight: LooseDotWeight, seg_weight: SeqLooseSegWeight, bend_weight: LooseBendWeight, sense: RotationSense, ) -> Result { self.drawing.insert_cane( recorder, meta, from, around, dot_weight, seg_weight, bend_weight, sense, ) } /// Remove [`Cane`] object from the [`Layout`] pub fn remove_cane(&mut self, recorder: &mut LayoutEdit, cane: &Cane, face: LooseDotIndex) { self.drawing.remove_cane(recorder, cane, face) } pub fn remove_termseg(&mut self, recorder: &mut LayoutEdit, termseg: BandTermsegIndex) { self.drawing.remove_termseg(recorder, termseg) } pub fn add_fixed_dot( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, weight: FixedDotWeight, ) -> Result { self.drawing.add_fixed_dot(recorder, meta, weight) } pub fn add_fixed_dot_infringably( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, weight: FixedDotWeight, ) -> FixedDotIndex { self.drawing .add_fixed_dot_infringably(recorder, meta, weight) } pub fn add_fixed_seg( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, from: FixedDotIndex, to: FixedDotIndex, weight: FixedSegWeight, ) -> Result { self.drawing.add_fixed_seg(recorder, meta, from, to, weight) } pub fn add_fixed_seg_infringably( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, from: FixedDotIndex, to: FixedDotIndex, weight: FixedSegWeight, ) -> FixedSegIndex { self.drawing .add_fixed_seg_infringably(recorder, meta, from, to, weight) } pub fn add_lone_loose_seg( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, from: FixedDotIndex, to: FixedDotIndex, weight: LoneLooseSegWeight, ) -> Result { self.drawing .add_lone_loose_seg(recorder, meta, from, to, weight) } pub fn add_seq_loose_seg( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, from: DotIndex, to: LooseDotIndex, weight: SeqLooseSegWeight, ) -> Result { self.drawing .add_seq_loose_seg(recorder, meta, from, to, weight) } pub fn move_dot( &mut self, recorder: &mut LayoutEdit, dot: DotIndex, to: Point, ) -> Result<(), Infringement> { self.drawing.move_dot(recorder, dot, to) } pub fn add_poly( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, weight: PolyWeight, ) -> GenericIndex { GenericIndex::::new( self.drawing .add_compound(recorder, meta, CompoundWeight::Poly(weight)) .petgraph_index(), ) } /// insert a polygon based upon the border nodes, and computes + returns the /// associated apex pub fn add_poly_with_nodes( &mut self, recorder: &mut LayoutEdit, meta: NodeMetadata, weight: PolyWeight, nodes: &[PrimitiveIndex], ) -> (GenericIndex, FixedDotIndex) { let poly = self.add_poly(recorder, meta, weight); ( poly, add_poly_with_nodes_intern(self, recorder, poly, nodes, meta), ) } pub fn remove_band( &mut self, recorder: &mut LayoutEdit, band: BandTermsegIndex, ) -> Result<(), DrawingException> { self.drawing.remove_band(recorder, band) } pub fn poly_nodes(&self) -> impl Iterator> + '_ { self.drawing.rtree().iter().filter_map(|wrapper| { if let NodeIndex::Compound(compound) = wrapper.data { let CompoundWeight::Poly(..) = self.drawing.compound_weight(compound); return Some(GenericIndex::::new(compound.petgraph_index())); } None }) } pub fn layer_poly_nodes( &self, layer: usize, ) -> impl Iterator> + '_ { self.drawing .rtree() .locate_in_envelope_intersecting(&AABB::from_corners( [-f64::INFINITY, -f64::INFINITY, layer as f64], [f64::INFINITY, f64::INFINITY, layer as f64], )) .filter_map(|wrapper| { if let NodeIndex::Compound(compound) = wrapper.data { let CompoundWeight::Poly(..) = self.drawing.compound_weight(compound); return Some(GenericIndex::::new(compound.petgraph_index())); } None }) } pub fn poly_members( &self, poly: GenericIndex, ) -> impl Iterator + '_ { self.drawing .geometry() .compound_members(GenericIndex::new(poly.petgraph_index())) } fn compound_shape(&self, compound: GenericIndex) -> Shape { match self.drawing.compound_weight(compound) { CompoundWeight::Poly(_) => GenericIndex::::new(compound.petgraph_index()) .ref_(self) .shape() .into(), } } pub fn node_shape(&self, index: NodeIndex) -> Shape { match index { NodeIndex::Primitive(primitive) => primitive.primitive(&self.drawing).shape().into(), NodeIndex::Compound(compound) => self.compound_shape(compound), } } /// Checks if a node is not a primitive part of a compound, and if yes, returns its apex and center pub fn apex_of_compoundless_node( &self, node: NodeIndex, active_layer: usize, ) -> Option<(FixedDotIndex, Point)> { fn handle_fixed_dot( drawing: &Drawing, index: PrimitiveIndex, ) -> Option<(FixedDotIndex, &FixedDotWeight)> { let PrimitiveIndex::FixedDot(dot) = index else { return None; }; if let GenericNode::Primitive(PrimitiveWeight::FixedDot(weight)) = drawing .geometry() .graph() .node_weight(dot.petgraph_index()) .unwrap() { Some((dot, weight)) } else { unreachable!() } } match node { NodeIndex::Primitive(primitive) => { if self .drawing() .geometry() .compounds(GenericIndex::<()>::new(primitive.petgraph_index())) .next() .is_some() { return None; } handle_fixed_dot(&self.drawing, primitive).map(|(dot, weight)| (dot, weight.pos())) } NodeIndex::Compound(compound) => Some(match self.drawing.compound_weight(compound) { CompoundWeight::Poly(_) => { let poly = GenericIndex::::new(compound.petgraph_index()).ref_(self); (poly.apex(), poly.shape().center()) } }), } } pub fn rules(&self) -> &R { self.drawing.rules() } pub fn rules_mut(&mut self) -> &mut R { self.drawing.rules_mut() } } impl ApplyGeometryEdit< DotWeight, SegWeight, BendWeight, CompoundWeight, CompoundEntryLabel, PrimitiveIndex, DotIndex, SegIndex, BendIndex, > for Layout { fn apply(&mut self, edit: &LayoutEdit) { self.drawing.apply(edit); } }