/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2013-2017 CERN * Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors. * * @author Maciej Suminski * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef PCBNEW_CONNECTIVITY_ITEMS_H #define PCBNEW_CONNECTIVITY_ITEMS_H #include #include #include #include #include #include #include #include #include #include #include #include #include class CN_ITEM; class CN_CLUSTER; /** * CN_ANCHOR represents a physical location that can be connected: a pad or a track/arc/via * endpoint. */ class CN_ANCHOR { public: CN_ANCHOR( const VECTOR2I& aPos, CN_ITEM* aItem ) : m_pos( aPos ), m_item( aItem ), m_tag( -1 ), m_noline( false ) { } bool Valid() const; bool Dirty() const; CN_ITEM* Item() const { return m_item; } void SetItem( CN_ITEM* aItem ) { m_item = aItem; } BOARD_CONNECTED_ITEM* Parent() const; const VECTOR2I& Pos() const { return m_pos; } void Move( const VECTOR2I& aPos ) { m_pos += aPos; } unsigned int Dist( const CN_ANCHOR& aSecond ) { return ( m_pos - aSecond.Pos() ).EuclideanNorm(); } ///< @return tag, a common identifier for connected nodes. int GetTag() const { return m_tag; } void SetTag( int aTag ) { m_tag = aTag; } ///< @return true if this node can be a target for ratsnest lines. const bool& GetNoLine() const { return m_noline; } void SetNoLine( bool aEnable ) { m_noline = aEnable; } const std::shared_ptr& GetCluster() const { return m_cluster; } void SetCluster( std::shared_ptr& aCluster ) { m_cluster = aCluster; } /** * The anchor point is dangling if the parent is a track and this anchor point is not * connected to another item ( track, vias pad or zone) or if the parent is a via and * this anchor point is connected to only one track and not to another item. * * @return true if this anchor is dangling. */ bool IsDangling() const; /** * @return the count of tracks and vias connected to this anchor. */ int ConnectedItemsCount() const; // Tag used for unconnected items. static const int TAG_UNCONNECTED = -1; private: VECTOR2I m_pos; ///< Position of the anchor. CN_ITEM* m_item; ///< Pad or track/arc/via owning the anchor. int m_tag; ///< Tag for quick connection resolution. bool m_noline; ///< Whether it the node can be a target for ratsnest lines. std::shared_ptr m_cluster; ///< Cluster to which the anchor belongs. }; /** * CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via, * or zone). */ class CN_ITEM { public: void Dump(); CN_ITEM( BOARD_CONNECTED_ITEM* aParent, bool aCanChangeNet, int aAnchorCount = 2 ) { m_parent = aParent; m_canChangeNet = aCanChangeNet; m_visited = false; m_valid = true; m_dirty = true; m_anchors.reserve( std::max( 6, aAnchorCount ) ); m_layers = LAYER_RANGE( 0, PCB_LAYER_ID_COUNT ); m_connected.reserve( 8 ); } virtual ~CN_ITEM() { for( const std::shared_ptr& anchor : m_anchors ) anchor->SetItem( nullptr ); }; std::shared_ptr AddAnchor( const VECTOR2I& aPos ) { m_anchors.emplace_back( std::make_shared( aPos, this ) ); return m_anchors.at( m_anchors.size() - 1 ); } std::vector>& Anchors() { return m_anchors; } void SetValid( bool aValid ) { m_valid = aValid; } bool Valid() const { return m_valid; } void SetDirty( bool aDirty ) { m_dirty = aDirty; } bool Dirty() const { return m_dirty; } /** * Set the layers spanned by the item to aLayers. */ void SetLayers( const LAYER_RANGE& aLayers ) { m_layers = aLayers; } /** * Set the layers spanned by the item to a single layer aLayer. */ void SetLayer( int aLayer ) { m_layers = LAYER_RANGE( aLayer, aLayer ); } /** * Return the contiguous set of layers spanned by the item. */ const LAYER_RANGE& Layers() const { return m_layers; } /** * Return the item's layer, for single-layered items only. */ virtual int Layer() const { return Layers().Start(); } const BOX2I& BBox() { if( m_dirty && m_valid ) m_bbox = m_parent->GetBoundingBox(); return m_bbox; } BOARD_CONNECTED_ITEM* Parent() const { return m_parent; } const std::vector& ConnectedItems() const { return m_connected; } void ClearConnections() { m_connected.clear(); } void SetVisited( bool aVisited ) { m_visited = aVisited; } bool Visited() const { return m_visited; } bool CanChangeNet() const { return m_canChangeNet; } void Connect( CN_ITEM* b ) { std::lock_guard lock( m_listLock ); auto i = std::lower_bound( m_connected.begin(), m_connected.end(), b ); if( i != m_connected.end() && *i == b ) return; m_connected.insert( i, b ); } void RemoveInvalidRefs(); virtual int AnchorCount() const; virtual const VECTOR2I GetAnchor( int n ) const; int Net() const { return ( !m_parent || !m_valid ) ? -1 : m_parent->GetNetCode(); } protected: bool m_dirty; ///< used to identify recently added item not yet ///< scanned into the connectivity search LAYER_RANGE m_layers; ///< layer range over which the item exists BOX2I m_bbox; ///< bounding box for the item private: BOARD_CONNECTED_ITEM* m_parent; std::vector m_connected; ///< list of physically touching items std::vector> m_anchors; bool m_canChangeNet; ///< can the net propagator modify the netcode? bool m_visited; ///< visited flag for the BFS scan bool m_valid; ///< used to identify garbage items (we use lazy removal) std::mutex m_listLock; ///< mutex protecting this item's connected_items set to }; /* * Represents a single outline of a zone fill on a particular layer. \a aSubpolyIndex indicates * which outline in the fill's SHAPE_POLY_SET. */ class CN_ZONE_LAYER : public CN_ITEM { public: CN_ZONE_LAYER( ZONE* aParent, PCB_LAYER_ID aLayer, int aSubpolyIndex ) : CN_ITEM( aParent, false ), m_zone( aParent ), m_subpolyIndex( aSubpolyIndex ), m_layer( aLayer ) { m_fillPoly = aParent->GetFilledPolysList( aLayer ); SetLayers( aLayer ); } void BuildRTree() { if( m_zone->IsTeardropArea() ) return; for( unsigned int ii = 0; ii < m_fillPoly->TriangulatedPolyCount(); ++ii ) { const auto* triangleSet = m_fillPoly->TriangulatedPolygon( ii ); if( triangleSet->GetSourceOutlineIndex() != m_subpolyIndex ) continue; for( const SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRI& tri : triangleSet->Triangles() ) { BOX2I bbox = tri.BBox(); const int mmin[2] = { bbox.GetX(), bbox.GetY() }; const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() }; m_rTree.Insert( mmin, mmax, &tri ); } } } int SubpolyIndex() const { return m_subpolyIndex; } PCB_LAYER_ID GetLayer() const { return m_layer; } bool ContainsPoint( const VECTOR2I& p ) const { if( m_zone->IsTeardropArea() ) return m_fillPoly->Outline( m_subpolyIndex ).Collide( p ) ; int min[2] = { p.x, p.y }; int max[2] = { p.x, p.y }; bool collision = false; auto visitor = [&]( const SHAPE* aShape ) -> bool { if( aShape->Collide( p ) ) { collision = true; return false; } return true; }; m_rTree.Search( min, max, visitor ); return collision; } PCB_LAYER_ID GetLayer() { return m_layer; } virtual int AnchorCount() const override; virtual const VECTOR2I GetAnchor( int n ) const override; const SHAPE_LINE_CHAIN& GetOutline() const { return m_fillPoly->Outline( m_subpolyIndex ); } bool Collide( SHAPE* aRefShape ) const { if( m_zone->IsTeardropArea() ) return m_fillPoly->Collide( aRefShape ); BOX2I bbox = aRefShape->BBox(); int min[2] = { bbox.GetX(), bbox.GetY() }; int max[2] = { bbox.GetRight(), bbox.GetBottom() }; bool collision = false; auto visitor = [&]( const SHAPE* aShape ) -> bool { if( aRefShape->Collide( aShape ) ) { collision = true; return false; } return true; }; m_rTree.Search( min, max, visitor ); return collision; } bool HasSingleConnection(); private: ZONE* m_zone; int m_subpolyIndex; PCB_LAYER_ID m_layer; std::shared_ptr m_fillPoly; RTree m_rTree; }; class CN_LIST { public: CN_LIST() { m_dirty = false; m_hasInvalid = false; } void Clear() { for( CN_ITEM* item : m_items ) delete item; m_items.clear(); m_index.RemoveAll(); } std::vector::iterator begin() { return m_items.begin(); }; std::vector::iterator end() { return m_items.end(); }; std::vector::const_iterator begin() const { return m_items.begin(); } std::vector::const_iterator end() const { return m_items.end(); } CN_ITEM* operator[] ( int aIndex ) { return m_items[aIndex]; } template void FindNearby( CN_ITEM* aItem, T aFunc ) { m_index.Query( aItem->BBox(), aItem->Layers(), aFunc ); } void SetHasInvalid( bool aInvalid = true ) { m_hasInvalid = aInvalid; } void SetDirty( bool aDirty = true ) { m_dirty = aDirty; } bool IsDirty() const { return m_dirty; } void RemoveInvalidItems( std::vector& aGarbage ); void ClearDirtyFlags() { for( CN_ITEM* item : m_items ) item->SetDirty( false ); SetDirty( false ); } int Size() const { return m_items.size(); } CN_ITEM* Add( PAD* pad ); CN_ITEM* Add( PCB_TRACK* track ); CN_ITEM* Add( PCB_ARC* track ); CN_ITEM* Add( PCB_VIA* via ); CN_ITEM* Add( CN_ZONE_LAYER* zitem ); const std::vector Add( ZONE* zone, PCB_LAYER_ID aLayer ); protected: void addItemtoTree( CN_ITEM* item ) { m_index.Insert( item ); } protected: std::vector m_items; private: bool m_dirty; bool m_hasInvalid; CN_RTREE m_index; }; class CN_CLUSTER { public: CN_CLUSTER(); ~CN_CLUSTER(); bool HasValidNet() const { return m_originNet > 0; } int OriginNet() const { return m_originNet; } wxString OriginNetName() const; bool Contains( const CN_ITEM* aItem ); bool Contains( const BOARD_CONNECTED_ITEM* aItem ); void Dump(); int Size() const { return m_items.size(); } bool IsOrphaned() const { return m_originPad == nullptr; } bool IsConflicting() const { return m_conflicting; } void Add( CN_ITEM* item ); std::vector::iterator begin() { return m_items.begin(); }; std::vector::iterator end() { return m_items.end(); }; private: bool m_conflicting; int m_originNet; CN_ITEM* m_originPad; std::vector m_items; std::unordered_map m_netRanks; }; #endif /* PCBNEW_CONNECTIVITY_ITEMS_H */