578 lines
12 KiB
C++
578 lines
12 KiB
C++
/*
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2013-2017 CERN
|
|
* Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
|
*
|
|
* 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_CONNECTIVITY_ITEMS_H_
|
|
#define PCBNEW_CONNECTIVITY_CONNECTIVITY_ITEMS_H_
|
|
|
|
#include <class_board.h>
|
|
#include <class_pad.h>
|
|
#include <class_module.h>
|
|
#include <class_track.h>
|
|
#include <class_zone.h>
|
|
|
|
#include <geometry/shape_poly_set.h>
|
|
#include <geometry/poly_grid_partition.h>
|
|
|
|
#include <memory>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <intrusive_list.h>
|
|
|
|
#include <connectivity/connectivity_rtree.h>
|
|
#include <connectivity/connectivity_data.h>
|
|
|
|
class CN_ITEM;
|
|
class CN_CLUSTER;
|
|
|
|
class CN_ANCHOR
|
|
{
|
|
public:
|
|
CN_ANCHOR()
|
|
{
|
|
m_item = nullptr;
|
|
}
|
|
|
|
CN_ANCHOR( const VECTOR2I& aPos, CN_ITEM* aItem )
|
|
{
|
|
m_pos = aPos;
|
|
m_item = aItem;
|
|
assert( m_item );
|
|
}
|
|
|
|
bool Valid() const;
|
|
|
|
|
|
CN_ITEM* Item() const
|
|
{
|
|
return m_item;
|
|
}
|
|
|
|
BOARD_CONNECTED_ITEM* Parent() const;
|
|
|
|
const VECTOR2I& Pos() const
|
|
{
|
|
return m_pos;
|
|
}
|
|
|
|
void Move( const VECTOR2I& aPos )
|
|
{
|
|
m_pos += aPos;
|
|
}
|
|
|
|
const unsigned int Dist( const CN_ANCHOR& aSecond )
|
|
{
|
|
return ( m_pos - aSecond.Pos() ).EuclideanNorm();
|
|
}
|
|
|
|
/// Returns tag, common identifier for connected nodes
|
|
inline int GetTag() const
|
|
{
|
|
return m_tag;
|
|
}
|
|
|
|
/// Sets tag, common identifier for connected nodes
|
|
inline void SetTag( int aTag )
|
|
{
|
|
m_tag = aTag;
|
|
}
|
|
|
|
/// Decides whether this node can be a ratsnest line target
|
|
inline void SetNoLine( bool aEnable )
|
|
{
|
|
m_noline = aEnable;
|
|
}
|
|
|
|
/// Returns true if this node can be a target for ratsnest lines
|
|
inline const bool& GetNoLine() const
|
|
{
|
|
return m_noline;
|
|
}
|
|
|
|
inline void SetCluster( std::shared_ptr<CN_CLUSTER> aCluster )
|
|
{
|
|
m_cluster = aCluster;
|
|
}
|
|
|
|
inline const std::shared_ptr<CN_CLUSTER>& GetCluster() const
|
|
{
|
|
return m_cluster;
|
|
}
|
|
|
|
/**
|
|
* has meaning only for tracks and vias.
|
|
* @return true if this anchor is dangling
|
|
* The anchor point is dangling if the parent is a track
|
|
* and this anchor point is not connected to another item
|
|
* ( track, vas 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
|
|
*/
|
|
bool IsDangling() const;
|
|
|
|
/**
|
|
* has meaning only for tracks and vias.
|
|
* @return the count of items connected to this anchor
|
|
*/
|
|
int ConnectedItemsCount() const;
|
|
|
|
// Tag used for unconnected items.
|
|
static const int TAG_UNCONNECTED = -1;
|
|
|
|
private:
|
|
/// Position of the anchor
|
|
VECTOR2I m_pos;
|
|
|
|
/// Item owning the anchor
|
|
CN_ITEM* m_item = nullptr;
|
|
|
|
/// Tag for quick connection resolution
|
|
int m_tag = -1;
|
|
|
|
/// Whether it the node can be a target for ratsnest lines
|
|
bool m_noline = false;
|
|
|
|
/// Cluster to which the anchor belongs
|
|
std::shared_ptr<CN_CLUSTER> m_cluster;
|
|
};
|
|
|
|
|
|
typedef std::shared_ptr<CN_ANCHOR> CN_ANCHOR_PTR;
|
|
typedef std::vector<CN_ANCHOR_PTR> CN_ANCHORS;
|
|
|
|
|
|
// basic connectivity item
|
|
class CN_ITEM
|
|
{
|
|
public:
|
|
using CONNECTED_ITEMS = std::vector<CN_ITEM*>;
|
|
|
|
private:
|
|
BOARD_CONNECTED_ITEM* m_parent;
|
|
|
|
///> list of items physically connected (touching)
|
|
CONNECTED_ITEMS m_connected;
|
|
|
|
CN_ANCHORS m_anchors;
|
|
|
|
///> visited flag for the BFS scan
|
|
bool m_visited;
|
|
|
|
///> can the net propagator modify the netcode?
|
|
bool m_canChangeNet;
|
|
|
|
///> valid flag, used to identify garbage items (we use lazy removal)
|
|
bool m_valid;
|
|
|
|
///> mutex protecting this item's connected_items set to allow parallel connection threads
|
|
std::mutex m_listLock;
|
|
|
|
protected:
|
|
///> dirty flag, used to identify recently added item not yet scanned into the connectivity search
|
|
bool m_dirty;
|
|
|
|
///> layer range over which the item exists
|
|
LAYER_RANGE m_layers;
|
|
|
|
///> bounding box for the item
|
|
BOX2I m_bbox;
|
|
|
|
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() {};
|
|
|
|
void AddAnchor( const VECTOR2I& aPos )
|
|
{
|
|
m_anchors.emplace_back( std::make_shared<CN_ANCHOR>( aPos, this ) );
|
|
}
|
|
|
|
CN_ANCHORS& 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;
|
|
}
|
|
|
|
/**
|
|
* Function SetLayers()
|
|
*
|
|
* Sets the layers spanned by the item to aLayers.
|
|
*/
|
|
void SetLayers( const LAYER_RANGE& aLayers )
|
|
{
|
|
m_layers = aLayers;
|
|
}
|
|
|
|
/**
|
|
* Function SetLayer()
|
|
*
|
|
* Sets the layers spanned by the item to a single layer aLayer.
|
|
*/
|
|
void SetLayer( int aLayer )
|
|
{
|
|
m_layers = LAYER_RANGE( aLayer, aLayer );
|
|
}
|
|
|
|
/**
|
|
* Function Layers()
|
|
*
|
|
* Returns the contiguous set of layers spanned by the item.
|
|
*/
|
|
const LAYER_RANGE& Layers() const
|
|
{
|
|
return m_layers;
|
|
}
|
|
|
|
/**
|
|
* Function Layer()
|
|
*
|
|
* Returns the item's layer, for single-layered items only.
|
|
*/
|
|
virtual int Layer() const
|
|
{
|
|
return Layers().Start();
|
|
}
|
|
|
|
const BOX2I& BBox()
|
|
{
|
|
if( m_dirty && m_valid )
|
|
{
|
|
EDA_RECT box = m_parent->GetBoundingBox();
|
|
m_bbox = BOX2I( box.GetPosition(), box.GetSize() );
|
|
}
|
|
return m_bbox;
|
|
}
|
|
|
|
BOARD_CONNECTED_ITEM* Parent() const
|
|
{
|
|
return m_parent;
|
|
}
|
|
|
|
const CONNECTED_ITEMS& 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<std::mutex> 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();
|
|
}
|
|
};
|
|
|
|
typedef std::shared_ptr<CN_ITEM> CN_ITEM_PTR;
|
|
|
|
class CN_ZONE : public CN_ITEM
|
|
{
|
|
public:
|
|
CN_ZONE( ZONE_CONTAINER* aParent, PCB_LAYER_ID aLayer, bool aCanChangeNet, int aSubpolyIndex ) :
|
|
CN_ITEM( aParent, aCanChangeNet ),
|
|
m_subpolyIndex( aSubpolyIndex ),
|
|
m_layer( aLayer )
|
|
{
|
|
SHAPE_LINE_CHAIN outline = aParent->GetFilledPolysList( aLayer ).COutline( aSubpolyIndex );
|
|
|
|
outline.SetClosed( true );
|
|
outline.Simplify();
|
|
|
|
m_cachedPoly = std::make_unique<POLY_GRID_PARTITION>( outline, 16 );
|
|
}
|
|
|
|
int SubpolyIndex() const
|
|
{
|
|
return m_subpolyIndex;
|
|
}
|
|
|
|
bool ContainsAnchor( const CN_ANCHOR_PTR anchor ) const
|
|
{
|
|
return ContainsPoint( anchor->Pos(), 0 );
|
|
}
|
|
|
|
bool ContainsPoint( const VECTOR2I p, int aAccuracy = 0 ) const
|
|
{
|
|
auto zone = static_cast<ZONE_CONTAINER*> ( Parent() );
|
|
int clearance = aAccuracy;
|
|
|
|
if( zone->GetFilledPolysUseThickness() )
|
|
clearance += ( zone->GetMinThickness() + 1 ) / 2;
|
|
|
|
return m_cachedPoly->ContainsPoint( p, clearance );
|
|
}
|
|
|
|
const BOX2I& BBox()
|
|
{
|
|
if( m_dirty )
|
|
m_bbox = m_cachedPoly->BBox();
|
|
|
|
return m_bbox;
|
|
}
|
|
|
|
virtual int AnchorCount() const override;
|
|
virtual const VECTOR2I GetAnchor( int n ) const override;
|
|
|
|
private:
|
|
std::vector<VECTOR2I> m_testOutlinePoints;
|
|
std::unique_ptr<POLY_GRID_PARTITION> m_cachedPoly;
|
|
int m_subpolyIndex;
|
|
PCB_LAYER_ID m_layer;
|
|
};
|
|
|
|
class CN_LIST
|
|
{
|
|
private:
|
|
bool m_dirty;
|
|
bool m_hasInvalid;
|
|
|
|
CN_RTREE<CN_ITEM*> m_index;
|
|
|
|
protected:
|
|
std::vector<CN_ITEM*> m_items;
|
|
|
|
void addItemtoTree( CN_ITEM* item )
|
|
{
|
|
m_index.Insert( item );
|
|
}
|
|
|
|
public:
|
|
CN_LIST()
|
|
{
|
|
m_dirty = false;
|
|
m_hasInvalid = false;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
for( auto item : m_items )
|
|
delete item;
|
|
|
|
m_items.clear();
|
|
m_index.RemoveAll();
|
|
}
|
|
|
|
using ITER = decltype( m_items )::iterator;
|
|
using CONST_ITER = decltype( m_items )::const_iterator;
|
|
|
|
ITER begin() { return m_items.begin(); };
|
|
ITER end() { return m_items.end(); };
|
|
|
|
CONST_ITER begin() const
|
|
{
|
|
return m_items.begin();
|
|
}
|
|
|
|
CONST_ITER end() const
|
|
{
|
|
return m_items.end();
|
|
}
|
|
|
|
CN_ITEM* operator[] ( int aIndex ) { return m_items[aIndex]; }
|
|
|
|
template <class T>
|
|
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<CN_ITEM*>& aGarbage );
|
|
|
|
void ClearDirtyFlags()
|
|
{
|
|
for( auto item : m_items )
|
|
item->SetDirty( false );
|
|
|
|
SetDirty( false );
|
|
}
|
|
|
|
void MarkAllAsDirty()
|
|
{
|
|
for( auto item : m_items )
|
|
item->SetDirty( true );
|
|
|
|
SetDirty( true );
|
|
}
|
|
|
|
int Size() const
|
|
{
|
|
return m_items.size();
|
|
}
|
|
|
|
CN_ITEM* Add( D_PAD* pad );
|
|
|
|
CN_ITEM* Add( TRACK* track );
|
|
|
|
CN_ITEM* Add( ARC* track );
|
|
|
|
CN_ITEM* Add( VIA* via );
|
|
|
|
const std::vector<CN_ITEM*> Add( ZONE_CONTAINER* zone, PCB_LAYER_ID aLayer );
|
|
};
|
|
|
|
class CN_CLUSTER
|
|
{
|
|
private:
|
|
|
|
bool m_conflicting = false;
|
|
int m_originNet = 0;
|
|
CN_ITEM* m_originPad = nullptr;
|
|
std::vector<CN_ITEM*> m_items;
|
|
|
|
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 HasNet() const
|
|
{
|
|
return m_originNet > 0;
|
|
}
|
|
|
|
bool IsOrphaned() const
|
|
{
|
|
return m_originPad == nullptr;
|
|
}
|
|
|
|
bool IsConflicting() const
|
|
{
|
|
return m_conflicting;
|
|
}
|
|
|
|
void Add( CN_ITEM* item );
|
|
|
|
using ITER = decltype(m_items)::iterator;
|
|
|
|
ITER begin() { return m_items.begin(); };
|
|
ITER end() { return m_items.end(); };
|
|
};
|
|
|
|
typedef std::shared_ptr<CN_CLUSTER> CN_CLUSTER_PTR;
|
|
|
|
|
|
#endif /* PCBNEW_CONNECTIVITY_CONNECTIVITY_ITEMS_H_ */
|