mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
The use of printf, wxLogDebug, and std::err/std::out causes excessive debugging output which makes finding specific debugging messages more difficult than it needs to be. There is still some debugging output in test code that really needs to be moved into a unit test. Add debugging output section to the coding policy regarding debugging output.
485 lines
12 KiB
C++
485 lines
12 KiB
C++
/*
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2016-2018 CERN
|
|
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* @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
|
|
*/
|
|
|
|
#include <connectivity/connectivity_items.h>
|
|
|
|
int CN_ITEM::AnchorCount() const
|
|
{
|
|
if( !m_valid )
|
|
return 0;
|
|
|
|
switch( m_parent->Type() )
|
|
{
|
|
case PCB_PAD_T:
|
|
return 5; // center, north, south, east and west
|
|
case PCB_TRACE_T:
|
|
case PCB_ARC_T:
|
|
return 2; // start and end
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
const VECTOR2I CN_ITEM::GetAnchor( int n ) const
|
|
{
|
|
VECTOR2I pt0;
|
|
|
|
if( !m_valid )
|
|
return pt0;
|
|
|
|
switch( m_parent->Type() )
|
|
{
|
|
case PCB_PAD_T:
|
|
{
|
|
D_PAD* pad = (D_PAD*) m_parent;
|
|
|
|
if( n == 0 )
|
|
return VECTOR2I( pad->GetPosition() );
|
|
|
|
// ShapePos() is the geometric center (not anchor) for the pad
|
|
pt0 = pad->ShapePos();
|
|
VECTOR2I pt1 = pt0;
|
|
|
|
switch( pad->GetShape() )
|
|
{
|
|
case PAD_SHAPE_TRAPEZOID:
|
|
// Because the trap delta is applied as +1/2 at one end and -1/2 at the other,
|
|
// the midpoint is actually unchanged. Therefore all the cardinal points are
|
|
// the same as for a rectangle.
|
|
KI_FALLTHROUGH;
|
|
|
|
case PAD_SHAPE_RECT:
|
|
case PAD_SHAPE_CIRCLE:
|
|
case PAD_SHAPE_OVAL:
|
|
case PAD_SHAPE_ROUNDRECT:
|
|
case PAD_SHAPE_CHAMFERED_RECT:
|
|
switch( n )
|
|
{
|
|
case 1: pt1.y -= pad->GetSize().y / 2; break; // North
|
|
case 2: pt1.y += pad->GetSize().y / 2; break; // South
|
|
case 3: pt1.x -= pad->GetSize().x / 2; break; // East
|
|
case 4: pt1.x += pad->GetSize().x / 2; break; // West
|
|
default: break; // Wicked witch
|
|
}
|
|
|
|
if( pad->GetOrientation() )
|
|
RotatePoint( pt1, pad->ShapePos(), pad->GetOrientation() );
|
|
|
|
// Thermal spokes on circular pads form an 'X' instead of a '+'
|
|
if( pad->GetShape() == PAD_SHAPE_CIRCLE )
|
|
RotatePoint( pt1, pad->ShapePos(), 450 );
|
|
|
|
return pt1;
|
|
|
|
case PAD_SHAPE_CUSTOM:
|
|
{
|
|
switch( n )
|
|
{
|
|
case 1: pt1.y = INT_MIN / 2; break; // North
|
|
case 2: pt1.y = INT_MAX / 2; break; // South
|
|
case 3: pt1.x = INT_MIN / 2; break; // East
|
|
case 4: pt1.x = INT_MAX / 2; break; // West
|
|
default: break; // Wicked witch
|
|
}
|
|
|
|
if( pad->GetOrientation() )
|
|
RotatePoint( pt1, pad->ShapePos(), pad->GetOrientation() );
|
|
|
|
const std::shared_ptr<SHAPE_POLY_SET>& padPolySet = pad->GetEffectivePolygon();
|
|
const SHAPE_LINE_CHAIN& padOutline = padPolySet->COutline( 0 );
|
|
SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
|
|
|
|
padOutline.Intersect( SEG( pt0, pt1 ), intersections );
|
|
|
|
if( intersections.empty() )
|
|
{
|
|
// There should always be at least some copper outside the hole and/or
|
|
// shapePos center
|
|
assert( false );
|
|
return pt0;
|
|
}
|
|
|
|
return intersections[ intersections.size() - 1 ].p;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case PCB_TRACE_T:
|
|
case PCB_ARC_T:
|
|
if( n == 0 )
|
|
return static_cast<const TRACK*>( m_parent )->GetStart();
|
|
else
|
|
return static_cast<const TRACK*>( m_parent )->GetEnd();
|
|
|
|
case PCB_VIA_T:
|
|
return static_cast<const VIA*>( m_parent )->GetStart();
|
|
|
|
default:
|
|
assert( false );
|
|
break;
|
|
}
|
|
|
|
return pt0;
|
|
}
|
|
|
|
|
|
void CN_ITEM::Dump()
|
|
{
|
|
wxLogDebug(" valid: %d, connected: \n", !!Valid());
|
|
|
|
for( auto i : m_connected )
|
|
{
|
|
TRACK* t = static_cast<TRACK*>( i->Parent() );
|
|
wxLogDebug( " - %p %d\n", t, t->Type() );
|
|
}
|
|
}
|
|
|
|
|
|
int CN_ZONE::AnchorCount() const
|
|
{
|
|
if( !Valid() )
|
|
return 0;
|
|
|
|
const auto zone = static_cast<const ZONE_CONTAINER*>( Parent() );
|
|
const auto& outline = zone->GetFilledPolysList( m_layer ).COutline( m_subpolyIndex );
|
|
|
|
return outline.PointCount() ? 1 : 0;
|
|
}
|
|
|
|
|
|
const VECTOR2I CN_ZONE::GetAnchor( int n ) const
|
|
{
|
|
if( !Valid() )
|
|
return VECTOR2I();
|
|
|
|
const auto zone = static_cast<const ZONE_CONTAINER*>( Parent() );
|
|
const auto& outline = zone->GetFilledPolysList( m_layer ).COutline( m_subpolyIndex );
|
|
|
|
return outline.CPoint( 0 );
|
|
}
|
|
|
|
|
|
void CN_ITEM::RemoveInvalidRefs()
|
|
{
|
|
for( auto it = m_connected.begin(); it != m_connected.end(); )
|
|
{
|
|
if( !(*it)->Valid() )
|
|
it = m_connected.erase( it );
|
|
else
|
|
++it;
|
|
}
|
|
}
|
|
|
|
|
|
CN_ITEM* CN_LIST::Add( D_PAD* pad )
|
|
{
|
|
if( !pad->IsOnCopperLayer() )
|
|
return nullptr;
|
|
|
|
auto item = new CN_ITEM( pad, false, 1 );
|
|
item->AddAnchor( pad->ShapePos() );
|
|
item->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) );
|
|
|
|
switch( pad->GetAttribute() )
|
|
{
|
|
case PAD_ATTRIB_SMD:
|
|
case PAD_ATTRIB_HOLE_NOT_PLATED:
|
|
case PAD_ATTRIB_CONN:
|
|
{
|
|
LSET lmsk = pad->GetLayerSet();
|
|
|
|
for( int i = 0; i <= MAX_CU_LAYERS; i++ )
|
|
{
|
|
if( lmsk[i] )
|
|
{
|
|
item->SetLayer( i );
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
addItemtoTree( item );
|
|
m_items.push_back( item );
|
|
SetDirty();
|
|
return item;
|
|
}
|
|
|
|
CN_ITEM* CN_LIST::Add( TRACK* track )
|
|
{
|
|
auto item = new CN_ITEM( track, true );
|
|
m_items.push_back( item );
|
|
item->AddAnchor( track->GetStart() );
|
|
item->AddAnchor( track->GetEnd() );
|
|
item->SetLayer( track->GetLayer() );
|
|
addItemtoTree( item );
|
|
SetDirty();
|
|
return item;
|
|
}
|
|
|
|
CN_ITEM* CN_LIST::Add( ARC* aArc )
|
|
{
|
|
auto item = new CN_ITEM( aArc, true );
|
|
m_items.push_back( item );
|
|
item->AddAnchor( aArc->GetStart() );
|
|
item->AddAnchor( aArc->GetEnd() );
|
|
item->SetLayer( aArc->GetLayer() );
|
|
addItemtoTree( item );
|
|
SetDirty();
|
|
return item;
|
|
}
|
|
|
|
CN_ITEM* CN_LIST::Add( VIA* via )
|
|
{
|
|
auto item = new CN_ITEM( via, true, 1 );
|
|
|
|
m_items.push_back( item );
|
|
item->AddAnchor( via->GetStart() );
|
|
|
|
item->SetLayers( LAYER_RANGE( via->TopLayer(), via->BottomLayer() ) );
|
|
addItemtoTree( item );
|
|
SetDirty();
|
|
return item;
|
|
}
|
|
|
|
const std::vector<CN_ITEM*> CN_LIST::Add( ZONE_CONTAINER* zone, PCB_LAYER_ID aLayer )
|
|
{
|
|
const auto& polys = zone->GetFilledPolysList( aLayer );
|
|
|
|
std::vector<CN_ITEM*> rv;
|
|
|
|
for( int j = 0; j < polys.OutlineCount(); j++ )
|
|
{
|
|
CN_ZONE* zitem = new CN_ZONE( zone, aLayer, false, j );
|
|
const auto& outline = zone->GetFilledPolysList( aLayer ).COutline( j );
|
|
|
|
for( int k = 0; k < outline.PointCount(); k++ )
|
|
zitem->AddAnchor( outline.CPoint( k ) );
|
|
|
|
m_items.push_back( zitem );
|
|
zitem->SetLayer( aLayer );
|
|
addItemtoTree( zitem );
|
|
rv.push_back( zitem );
|
|
SetDirty();
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
|
|
{
|
|
if( !m_hasInvalid )
|
|
return;
|
|
|
|
auto lastItem = std::remove_if(m_items.begin(), m_items.end(), [&aGarbage] ( CN_ITEM* item )
|
|
{
|
|
if( !item->Valid() )
|
|
{
|
|
aGarbage.push_back ( item );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} );
|
|
|
|
m_items.resize( lastItem - m_items.begin() );
|
|
|
|
for( auto item : m_items )
|
|
item->RemoveInvalidRefs();
|
|
|
|
for( auto item : aGarbage )
|
|
m_index.Remove( item );
|
|
|
|
m_hasInvalid = false;
|
|
}
|
|
|
|
|
|
BOARD_CONNECTED_ITEM* CN_ANCHOR::Parent() const
|
|
{
|
|
assert( m_item->Valid() );
|
|
return m_item->Parent();
|
|
}
|
|
|
|
|
|
bool CN_ANCHOR::Valid() const
|
|
{
|
|
if( !m_item )
|
|
return false;
|
|
|
|
return m_item->Valid();
|
|
}
|
|
|
|
|
|
bool CN_ANCHOR::IsDangling() const
|
|
{
|
|
int accuracy = 0;
|
|
|
|
if( !m_cluster )
|
|
return true;
|
|
|
|
// the minimal number of items connected to item_ref
|
|
// at this anchor point to decide the anchor is *not* dangling
|
|
size_t minimal_count = 1;
|
|
size_t connected_count = m_item->ConnectedItems().size();
|
|
|
|
// a via can be removed if connected to only one other item.
|
|
if( Parent()->Type() == PCB_VIA_T )
|
|
return connected_count < 2;
|
|
|
|
if( m_item->AnchorCount() == 1 )
|
|
return connected_count < minimal_count;
|
|
|
|
if( Parent()->Type() == PCB_TRACE_T || Parent()->Type() == PCB_ARC_T )
|
|
accuracy = ( static_cast<const TRACK*>( Parent() )->GetWidth() + 1 )/ 2;
|
|
|
|
// Items with multiple anchors have usually items connected to each anchor.
|
|
// We want only the item count of this anchor point
|
|
connected_count = 0;
|
|
for( auto item : m_item->ConnectedItems() )
|
|
{
|
|
if( item->Parent()->Type() == PCB_ZONE_AREA_T )
|
|
{
|
|
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item->Parent() );
|
|
|
|
if( zone->HitTestFilledArea( static_cast<PCB_LAYER_ID>( item->Layer() ),
|
|
wxPoint( Pos() ), accuracy ) )
|
|
connected_count++;
|
|
}
|
|
else if( item->Parent()->HitTest( wxPoint( Pos() ), accuracy ) )
|
|
connected_count++;
|
|
}
|
|
|
|
return connected_count < minimal_count;
|
|
}
|
|
|
|
|
|
int CN_ANCHOR::ConnectedItemsCount() const
|
|
{
|
|
if( !m_cluster )
|
|
return 0;
|
|
|
|
int connected_count = 0;
|
|
|
|
for( auto item : m_item->ConnectedItems() )
|
|
{
|
|
if( item->Parent()->Type() == PCB_ZONE_AREA_T )
|
|
{
|
|
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item->Parent() );
|
|
|
|
if( zone->HitTestFilledArea( static_cast<PCB_LAYER_ID>( item->Layer() ),
|
|
wxPoint( Pos().x, Pos().y ) ) )
|
|
connected_count++;
|
|
}
|
|
else if( item->Parent()->HitTest( wxPoint( Pos().x, Pos().y ) ) )
|
|
connected_count++;
|
|
}
|
|
|
|
return connected_count;
|
|
}
|
|
|
|
|
|
CN_CLUSTER::CN_CLUSTER()
|
|
{
|
|
m_items.reserve( 64 );
|
|
m_originPad = nullptr;
|
|
m_originNet = -1;
|
|
m_conflicting = false;
|
|
}
|
|
|
|
|
|
CN_CLUSTER::~CN_CLUSTER()
|
|
{
|
|
}
|
|
|
|
|
|
wxString CN_CLUSTER::OriginNetName() const
|
|
{
|
|
if( !m_originPad || !m_originPad->Valid() )
|
|
return "<none>";
|
|
else
|
|
return m_originPad->Parent()->GetNetname();
|
|
}
|
|
|
|
|
|
bool CN_CLUSTER::Contains( const CN_ITEM* aItem )
|
|
{
|
|
return std::find( m_items.begin(), m_items.end(), aItem ) != m_items.end();
|
|
}
|
|
|
|
|
|
bool CN_CLUSTER::Contains( const BOARD_CONNECTED_ITEM* aItem )
|
|
{
|
|
return std::find_if( m_items.begin(), m_items.end(), [ &aItem ] ( const CN_ITEM* item )
|
|
{ return item->Valid() && item->Parent() == aItem; } ) != m_items.end();
|
|
}
|
|
|
|
|
|
void CN_CLUSTER::Dump()
|
|
{
|
|
for( auto item : m_items )
|
|
{
|
|
wxLogTrace( "CN", " - item : %p bitem : %p type : %d inet %s\n", item, item->Parent(),
|
|
item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() );
|
|
wxLogTrace( "CN", "- item : %p bitem : %p type : %d inet %s\n", item, item->Parent(),
|
|
item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() );
|
|
item->Dump();
|
|
}
|
|
}
|
|
|
|
|
|
void CN_CLUSTER::Add( CN_ITEM* item )
|
|
{
|
|
m_items.push_back( item );
|
|
|
|
if( item->Net() <= 0 )
|
|
return;
|
|
|
|
if( m_originNet <= 0 )
|
|
{
|
|
m_originNet = item->Net();
|
|
}
|
|
|
|
if( item->Parent()->Type() == PCB_PAD_T )
|
|
{
|
|
if( !m_originPad )
|
|
{
|
|
m_originPad = item;
|
|
m_originNet = item->Net();
|
|
}
|
|
|
|
if( m_originPad && item->Net() != m_originNet )
|
|
{
|
|
m_conflicting = true;
|
|
}
|
|
}
|
|
}
|