Clean up hole shapes for safety (smart pointer) and consistency.

This commit is contained in:
Jeff Young 2022-07-22 23:05:25 +01:00
parent a2c002e1da
commit f41af10007
25 changed files with 269 additions and 323 deletions

View File

@ -429,28 +429,26 @@ void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aCo
OBJECT_2D* BOARD_ADAPTER::createPadWithDrill( const PAD* aPad, int aInflateValue )
{
VECTOR2I drillSize = aPad->GetDrillSize();
if( !drillSize.x || !drillSize.y )
if( !aPad->HasHole() )
{
wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::createPadWithDrill - found an invalid pad" ) );
return nullptr;
}
if( drillSize.x == drillSize.y ) // usual round hole
std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
if( slot->GetSeg().A == slot->GetSeg().B )
{
const int radius = ( drillSize.x / 2 ) + aInflateValue;
return new FILLED_CIRCLE_2D( TO_SFVEC2F( aPad->GetPosition() ), TO_3DU( radius ), *aPad );
return new FILLED_CIRCLE_2D( TO_SFVEC2F( slot->GetSeg().A ),
TO_3DU( slot->GetWidth() / 2 + aInflateValue ),
*aPad );
}
else // Oblong hole
else
{
const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
float width = seg->GetWidth() + aInflateValue * 2;
return new ROUND_SEGMENT_2D( TO_SFVEC2F( seg->GetSeg().A ), TO_SFVEC2F( seg->GetSeg().B ),
TO_3DU( width ), *aPad );
return new ROUND_SEGMENT_2D( TO_SFVEC2F( slot->GetSeg().A ),
TO_SFVEC2F( slot->GetSeg().B ),
TO_3DU( slot->GetWidth() + aInflateValue * 2 ),
*aPad );
}
}

View File

@ -687,15 +687,15 @@ void GERBER_DRAW_ITEM::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_
if( m_Flashed )
{
msg.Printf( wxT( "(%.4f, %.4f)" ), xStart, yStart );
aList.emplace_back( MSG_PANEL_ITEM( _( "Position" ), msg, BLUE ) );
aList.emplace_back( _( "Position" ), msg );
}
else
{
msg.Printf( wxT( "(%.4f, %.4f)" ), xStart, yStart );
aList.emplace_back( MSG_PANEL_ITEM( _( "Start" ), msg, BLUE ) );
aList.emplace_back( _( "Start" ), msg );
msg.Printf( wxT( "(%.4f, %.4f)" ), xEnd, yEnd );
aList.emplace_back( MSG_PANEL_ITEM( _( "End" ), msg, BLUE ) );
aList.emplace_back( _( "End" ), msg );
}
// Display item rotation

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wandadoo.fr
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -38,6 +38,7 @@
class BOARD;
class BOARD_ITEM_CONTAINER;
class SHAPE_POLY_SET;
class SHAPE_SEGMENT;
class PCB_BASE_FRAME;
class SHAPE;
class PCB_GROUP;
@ -147,6 +148,8 @@ public:
virtual std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
FLASHING aFlash = FLASHING::DEFAULT ) const;
virtual std::shared_ptr<SHAPE_SEGMENT> GetEffectiveHoleShape() const;
BOARD_ITEM_CONTAINER* GetParent() const { return (BOARD_ITEM_CONTAINER*) m_parent; }
BOARD_ITEM_CONTAINER* GetParentFootprint() const;

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -196,7 +196,7 @@ bool BOARD_ITEM::ptr_cmp::operator() ( const BOARD_ITEM* a, const BOARD_ITEM* b
std::shared_ptr<SHAPE> BOARD_ITEM::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
{
std::shared_ptr<SHAPE> shape;
static std::shared_ptr<SHAPE> shape;
UNIMPLEMENTED_FOR( GetClass() );
@ -204,6 +204,16 @@ std::shared_ptr<SHAPE> BOARD_ITEM::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASH
}
std::shared_ptr<SHAPE_SEGMENT> BOARD_ITEM::GetEffectiveHoleShape() const
{
static std::shared_ptr<SHAPE_SEGMENT> slot;
UNIMPLEMENTED_FOR( GetClass() );
return slot;
}
BOARD_ITEM_CONTAINER* BOARD_ITEM::GetParentFootprint() const
{
BOARD_ITEM_CONTAINER* ancestor = GetParent();

View File

@ -398,14 +398,12 @@ bool CONNECTIVITY_DATA::IsConnectedOnLayer( const BOARD_CONNECTED_ITEM *aItem, i
if( aItem->Type() == PCB_PAD_T && zoneLayer )
{
const PAD* pad = static_cast<const PAD*>( aItem );
SHAPE_SEGMENT hole( *pad->GetEffectiveHoleShape() );
PCB_LAYER_ID layer = ToLAYER_ID( aLayer );
ZONE* zone = static_cast<ZONE*>( zoneLayer->Parent() );
int islandIdx = zoneLayer->SubpolyIndex();
if( zone->IsFilled() )
{
const SHAPE_POLY_SET* zoneFill = zone->GetFill( layer );
const SHAPE_POLY_SET* zoneFill = zone->GetFill( ToLAYER_ID( aLayer ) );
const SHAPE_LINE_CHAIN& padHull = pad->GetEffectivePolygon()->Outline( 0 );
for( const VECTOR2I& pt : zoneFill->COutline( islandIdx ).CPoints() )
@ -424,14 +422,12 @@ bool CONNECTIVITY_DATA::IsConnectedOnLayer( const BOARD_CONNECTED_ITEM *aItem, i
else if( aItem->Type() == PCB_VIA_T && zoneLayer )
{
const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
SHAPE_CIRCLE hole( via->GetCenter(), via->GetDrillValue() / 2 );
PCB_LAYER_ID layer = ToLAYER_ID( aLayer );
ZONE* zone = static_cast<ZONE*>( zoneLayer->Parent() );
int islandIdx = zoneLayer->SubpolyIndex();
if( zone->IsFilled() )
{
const SHAPE_POLY_SET* zoneFill = zone->GetFill( layer );
const SHAPE_POLY_SET* zoneFill = zone->GetFill( ToLAYER_ID( aLayer ) );
SHAPE_CIRCLE viaHull( via->GetCenter(), via->GetWidth() / 2 );
for( const VECTOR2I& pt : zoneFill->COutline( islandIdx ).CPoints() )

View File

@ -1281,14 +1281,13 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK()
}
else if( m_dummyPad->GetAttribute() == PAD_ATTRIB::PTH )
{
const SHAPE_SEGMENT* drillShape = m_dummyPad->GetEffectiveHoleShape();
const SEG drillSeg = drillShape->GetSeg();
SHAPE_POLY_SET drillOutline;
std::shared_ptr<SHAPE_SEGMENT> slot = m_dummyPad->GetEffectiveHoleShape();
SHAPE_POLY_SET slotOutline;
TransformOvalToPolygon( drillOutline, drillSeg.A, drillSeg.B,
drillShape->GetWidth(), maxError, ERROR_LOC::ERROR_INSIDE );
TransformOvalToPolygon( slotOutline, slot->GetSeg().A, slot->GetSeg().B,
slot->GetWidth(), maxError, ERROR_LOC::ERROR_INSIDE );
padOutline.BooleanSubtract( drillOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
padOutline.BooleanSubtract( slotOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
if( padOutline.IsEmpty() )
warning_msgs.Add( _( "Warning: Pad hole will leave no copper." ) );

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2020 CERN
*
* This program is free software; you can redistribute it and/or
@ -55,11 +55,21 @@ public:
std::shared_ptr<SHAPE> aParentShape = nullptr ) :
parent( aParent ),
shape( aShape ),
shapeStorage( nullptr ),
parentShape( aParentShape )
{};
ITEM_WITH_SHAPE( BOARD_ITEM *aParent, std::shared_ptr<SHAPE> aShape,
std::shared_ptr<SHAPE> aParentShape = nullptr ) :
parent( aParent ),
shape( aShape.get() ),
shapeStorage( aShape ),
parentShape( aParentShape )
{};
BOARD_ITEM* parent;
const SHAPE* shape;
std::shared_ptr<SHAPE> shapeStorage;
std::shared_ptr<SHAPE> parentShape;
};
@ -79,9 +89,9 @@ public:
~DRC_RTREE()
{
for( auto tree : m_tree )
for( drc_rtree* tree : m_tree )
{
for( auto el : *tree )
for( DRC_RTREE::ITEM_WITH_SHAPE* el : *tree )
delete el;
delete tree;
@ -110,24 +120,12 @@ public:
std::vector<const SHAPE*> subshapes;
std::shared_ptr<SHAPE> shape = aItem->GetEffectiveShape( aRefLayer );
subshapes.clear();
if( shape->HasIndexableSubshapes() )
shape->GetIndexableSubshapes( subshapes );
else
subshapes.push_back( shape.get() );
if( aItem->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( aItem );
if( pad->HasHole() )
{
const SHAPE* hole = pad->GetEffectiveHoleShape();
subshapes.push_back( const_cast<SHAPE*>( hole ) );
}
}
for( const SHAPE* subshape : subshapes )
{
if( dynamic_cast<const SHAPE_NULL*>( subshape ) )
@ -144,6 +142,22 @@ public:
m_tree[aTargetLayer]->Insert( mmin, mmax, itemShape );
m_count++;
}
if( aItem->Type() == PCB_PAD_T && aItem->HasHole() )
{
std::shared_ptr<SHAPE_SEGMENT> hole = aItem->GetEffectiveHoleShape();
BOX2I bbox = hole->BBox();
bbox.Inflate( aWorstClearance );
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
ITEM_WITH_SHAPE* itemShape = new ITEM_WITH_SHAPE( aItem, hole, shape );
m_tree[aTargetLayer]->Insert( mmin, mmax, itemShape );
m_count++;
}
}
/**

View File

@ -229,24 +229,18 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( PCB_TRACK* track,
}
}
if( testHoles && ( other->Type() == PCB_VIA_T || other->Type() == PCB_PAD_T ) )
if( testHoles && other->HasHole() )
{
std::unique_ptr<SHAPE_SEGMENT> holeShape;
std::shared_ptr<SHAPE_SEGMENT> holeShape;
if( other->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( other );
pos = via->GetPosition();
if( via->GetLayerSet().Contains( layer ) )
holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
if( other->GetLayerSet().Contains( layer ) )
holeShape = other->GetEffectiveHoleShape();
}
else if( other->Type() == PCB_PAD_T )
else
{
PAD* pad = static_cast<PAD*>( other );
if( pad->GetDrillSize().x )
holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
holeShape = other->GetEffectiveHoleShape();
}
if( holeShape )
@ -312,31 +306,22 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
int clearance = -1;
int actual;
VECTOR2I pos;
PCB_VIA* via = nullptr;
PAD* pad = nullptr;
bool flashed = false;
bool hasHole = false;
bool unflashedPad = false;
bool platedHole = false;
if( aItem->Type() == PCB_VIA_T )
if( aItem->Type() == PCB_PAD_T )
{
via = static_cast<PCB_VIA*>( aItem );
}
else if( aItem->Type() == PCB_PAD_T )
{
pad = static_cast<PAD*>( aItem );
flashed = pad->FlashLayer( aLayer );
hasHole = pad->GetDrillSize().x > 0 || pad->GetDrillSize().y > 0;
unflashedPad = !static_cast<PAD*>( aItem )->FlashLayer( aLayer );
if( !flashed && !hasHole )
if( unflashedPad && !aItem->HasHole() )
return;
platedHole = hasHole && pad->GetAttribute() == PAD_ATTRIB::PTH;
platedHole = static_cast<PAD*>( aItem )->GetAttribute() == PAD_ATTRIB::PTH;
}
if( zoneTree && testClearance )
{
if( pad && !flashed && hasHole && !platedHole )
if( unflashedPad && !platedHole )
constraintType = HOLE_CLEARANCE_CONSTRAINT;
constraint = m_drcEngine->EvalRules( constraintType, aItem, aZone, aLayer );
@ -347,9 +332,9 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
{
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
if( pad && !flashed && hasHole )
if( unflashedPad )
{
const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
std::shared_ptr<SHAPE_SEGMENT> hole = aItem->GetEffectiveHoleShape();
int size = hole->GetWidth();
// Note: drill size represents finish size, which means the actual hole size is
@ -380,18 +365,18 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
}
}
if( zoneTree && testHoles && ( pad || via ) )
if( zoneTree && testHoles && aItem->HasHole() )
{
std::unique_ptr<SHAPE_SEGMENT> holeShape;
std::shared_ptr<SHAPE_SEGMENT> holeShape;
if( via && via->GetLayerSet().Contains( aLayer ) )
if( aItem->Type() == PCB_VIA_T )
{
holeShape.reset( new SHAPE_SEGMENT( via->GetPosition(), via->GetPosition(),
via->GetDrill() ) );
if( aItem->GetLayerSet().Contains( aLayer ) )
holeShape = aItem->GetEffectiveHoleShape();
}
else if( pad && pad->GetDrillSize().x )
else
{
holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
holeShape = aItem->GetEffectiveHoleShape();
}
if( holeShape )
@ -628,9 +613,9 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa
testHoles = false;
}
if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->GetDrillSize().x )
if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->HasHole() )
{
if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape(),
if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape().get(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{
@ -651,9 +636,9 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa
}
}
if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->GetDrillSize().x )
if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->HasHole() )
{
if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape(),
if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape().get(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{
@ -676,10 +661,7 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa
if( testHoles && otherVia && otherVia->IsOnLayer( aLayer ) )
{
pos = otherVia->GetPosition();
otherShape.reset( new SHAPE_SEGMENT( pos, pos, otherVia->GetDrill() ) );
if( clearance > 0 && padShape->Collide( otherShape.get(),
if( clearance > 0 && padShape->Collide( otherVia->GetEffectiveHoleShape().get(),
std::max( 0, clearance - m_drcEpsilon ),
&actual, &pos ) )
{

View File

@ -259,7 +259,7 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
}
auto testPadAgainstCourtyards =
[&]( const PAD* pad, const FOOTPRINT* footprint )
[&]( const PAD* pad, const FOOTPRINT* fp )
{
int errorCode = 0;
@ -273,22 +273,25 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
if( m_drcEngine->IsErrorLimitExceeded( errorCode ) )
return;
const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
const SHAPE_POLY_SET& front = footprint->GetPolyCourtyard( F_CrtYd );
const SHAPE_POLY_SET& back = footprint->GetPolyCourtyard( B_CrtYd );
if( pad->HasHole() )
{
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
const SHAPE_POLY_SET& front = fp->GetPolyCourtyard( F_CrtYd );
const SHAPE_POLY_SET& back = fp->GetPolyCourtyard( B_CrtYd );
if( front.OutlineCount() > 0 && front.Collide( hole, 0 ) )
if( front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
drce->SetItems( pad, footprint );
drce->SetItems( pad, fp );
reportViolation( drce, pad->GetPosition(), F_CrtYd );
}
else if( back.OutlineCount() > 0 && back.Collide( hole, 0 ) )
else if( back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( errorCode );
drce->SetItems( pad, footprint );
drce->SetItems( pad, fp );
reportViolation( drce, pad->GetPosition(), B_CrtYd );
}
}
};
if( ( frontA.OutlineCount() > 0 && frontA.BBoxFromCaches().Intersects( fpBBBox ) )

View File

@ -86,11 +86,12 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::testAgainstEdge( BOARD_ITEM* item, SHAPE*
DRC_CONSTRAINT_T aConstraintType,
PCB_DRC_CODE aErrorCode )
{
const std::shared_ptr<SHAPE>& shape = edge->GetEffectiveShape( Edge_Cuts );
const SHAPE* edgeShape = shape.get();
std::shared_ptr<SHAPE> shape;
if( edge->Type() == PCB_PAD_T )
edgeShape = static_cast<PAD*>( edge )->GetEffectiveHoleShape();
shape = edge->GetEffectiveHoleShape();
else
shape = edge->GetEffectiveShape( Edge_Cuts );
auto constraint = m_drcEngine->EvalRules( aConstraintType, edge, item, UNDEFINED_LAYER );
int minClearance = constraint.GetValue().Min();
@ -99,7 +100,7 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::testAgainstEdge( BOARD_ITEM* item, SHAPE*
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && minClearance >= 0 )
{
if( itemShape->Collide( edgeShape, minClearance, &actual, &pos ) )
if( itemShape->Collide( shape.get(), minClearance, &actual, &pos ) )
{
if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
{

View File

@ -593,40 +593,28 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
if( testHoles )
{
std::unique_ptr<SHAPE_SEGMENT> itemHoleShape;
std::unique_ptr<SHAPE_SEGMENT> otherHoleShape;
std::shared_ptr<SHAPE_SEGMENT> itemHoleShape;
std::shared_ptr<SHAPE_SEGMENT> otherHoleShape;
clearance = 0;
if( item->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
pos = via->GetPosition();
if( via->GetLayerSet().Contains( layer ) )
itemHoleShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
if( item->GetLayerSet().Contains( layer ) )
itemHoleShape = item->GetEffectiveHoleShape();
}
else if( item->Type() == PCB_PAD_T )
else if( item->HasHole() )
{
PAD* pad = static_cast<PAD*>( item );
if( pad->GetDrillSize().x )
itemHoleShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
itemHoleShape = item->GetEffectiveHoleShape();
}
if( other->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( other );
pos = via->GetPosition();
if( via->GetLayerSet().Contains( layer ) )
otherHoleShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
if( other->GetLayerSet().Contains( layer ) )
otherHoleShape = other->GetEffectiveHoleShape();
}
else if( other->Type() == PCB_PAD_T )
else if( other->HasHole() )
{
PAD* pad = static_cast<PAD*>( other );
if( pad->GetDrillSize().x )
otherHoleShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
otherHoleShape = other->GetEffectiveHoleShape();
}
if( itemHoleShape || otherHoleShape )
@ -722,7 +710,7 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
continue;
const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
int size = hole->GetWidth();
// Note: drill size represents finish size, which means the actual hole
@ -763,24 +751,18 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
}
}
if( testHoles && ( aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_PAD_T ) )
if( testHoles )
{
std::unique_ptr<SHAPE_SEGMENT> holeShape;
std::shared_ptr<SHAPE_SEGMENT> holeShape;
if( aItem->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
pos = via->GetPosition();
if( via->GetLayerSet().Contains( aLayer ) )
holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
if( aItem->GetLayerSet().Contains( aLayer ) )
holeShape = aItem->GetEffectiveHoleShape();
}
else if( aItem->Type() == PCB_PAD_T )
else if( aItem->HasHole() )
{
PAD* pad = static_cast<PAD*>( aItem );
if( pad->GetDrillSize().x )
holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
holeShape = aItem->GetEffectiveHoleShape();
}
if( holeShape )

View File

@ -184,7 +184,7 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
BOARD_ITEM* testItem = aTestItemShape->parent;
const SHAPE* testShape = aTestItemShape->shape;
std::unique_ptr<SHAPE> hole;
std::shared_ptr<SHAPE> hole;
if( m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_SILK ) )
return false;
@ -194,28 +194,13 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
if( testItem->IsTented() )
{
switch( testItem->Type() )
if( testItem->HasHole() )
{
case PCB_VIA_T:
{
PCB_VIA* via = static_cast<PCB_VIA*>( testItem );
hole.reset( via->GetEffectiveShape( UNDEFINED_LAYER,
FLASHING::NEVER_FLASHED )->Clone() );
hole = testItem->GetEffectiveHoleShape();
testShape = hole.get();
break;
}
case PCB_PAD_T:
else
{
PAD* pad = static_cast<PAD*>( testItem );
if( pad->GetDrillSize().x || pad->GetDrillSize().y )
return true;
hole.reset( pad->GetEffectiveHoleShape()->Clone() );
testShape = hole.get();
break;
}
default:
return true;
}
}

View File

@ -2322,24 +2322,20 @@ void FOOTPRINT::CheckPads( const std::function<void( const PAD*, int,
pad->TransformShapeWithClearanceToPolygon( padOutline, layer, 0, ARC_HIGH_DEF,
ERROR_LOC::ERROR_INSIDE );
const SHAPE_SEGMENT* drillShape = pad->GetEffectiveHoleShape();
const SEG drillSeg = drillShape->GetSeg();
SHAPE_POLY_SET drillOutline;
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
SHAPE_POLY_SET holeOutline;
TransformOvalToPolygon( drillOutline, drillSeg.A, drillSeg.B,
drillShape->GetWidth(), ARC_HIGH_DEF,
ERROR_LOC::ERROR_INSIDE );
TransformOvalToPolygon( holeOutline, hole->GetSeg().A, hole->GetSeg().B,
hole->GetWidth(), ARC_HIGH_DEF, ERROR_LOC::ERROR_INSIDE );
padOutline.BooleanSubtract( drillOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
padOutline.BooleanSubtract( holeOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
if( padOutline.IsEmpty() )
{
(aErrorHandler)( pad, DRCE_PADSTACK, _( "(PTH pad's hole leaves no copper)" ) );
}
}
}
}
}
void FOOTPRINT::CheckOverlappingPads( const std::function<void( const PAD*, const PAD*,

View File

@ -329,16 +329,8 @@ std::shared_ptr<SHAPE> PAD::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFl
|| ( aLayer != UNDEFINED_LAYER && !FlashLayer( aLayer ) ) )
{
if( GetAttribute() == PAD_ATTRIB::PTH )
{
BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
// Note: drill size represents finish size, which means the actual holes size is the
// plating thickness larger.
auto hole = static_cast<SHAPE_SEGMENT*>( GetEffectiveHoleShape()->Clone() );
hole->SetWidth( hole->GetWidth() + bds.GetHolePlatingThickness() );
return std::make_shared<SHAPE_SEGMENT>( *hole );
}
return GetEffectiveHoleShape();
else
return std::make_shared<SHAPE_NULL>();
}
@ -349,12 +341,12 @@ std::shared_ptr<SHAPE> PAD::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFl
}
const SHAPE_SEGMENT* PAD::GetEffectiveHoleShape() const
std::shared_ptr<SHAPE_SEGMENT> PAD::GetEffectiveHoleShape() const
{
if( m_shapesDirty )
BuildEffectiveShapes( UNDEFINED_LAYER );
return m_effectiveHoleShape.get();
return m_effectiveHoleShape;
}
@ -1537,10 +1529,10 @@ bool PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, in
if( !drillsize.x || !drillsize.y )
return false;
const SHAPE_SEGMENT* seg = GetEffectiveHoleShape();
std::shared_ptr<SHAPE_SEGMENT> slot = GetEffectiveHoleShape();
TransformOvalToPolygon( aCornerBuffer, seg->GetSeg().A, seg->GetSeg().B,
seg->GetWidth() + aInflateValue * 2, aError, aErrorLoc );
TransformOvalToPolygon( aCornerBuffer, slot->GetSeg().A, slot->GetSeg().B,
slot->GetWidth() + aInflateValue * 2, aError, aErrorLoc );
return true;
}

View File

@ -437,9 +437,9 @@ public:
const std::shared_ptr<SHAPE_POLY_SET>& GetEffectivePolygon() const;
/**
* Return a SHAPE object representing the pad's hole.
* Return a SHAPE_SEGMENT object representing the pad's hole.
*/
const SHAPE_SEGMENT* GetEffectiveHoleShape() const;
std::shared_ptr<SHAPE_SEGMENT> GetEffectiveHoleShape() const override;
/**
* Return the radius of a minimum sized circle which fully encloses this pad.

View File

@ -467,22 +467,17 @@ bool calcIsInsideArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CO
{
if( aItem->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( aItem );
const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape();
return areaOutline.Collide( holeShape );
return areaOutline.Collide( aItem->GetEffectiveHoleShape().get() );
}
else if( aItem->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() );
LSET overlap = via->GetLayerSet() & aArea->GetLayerSet();
LSET overlap = aItem->GetLayerSet() & aArea->GetLayerSet();
/// Avoid buried vias that don't overlap the zone's layers
if( overlap.count() > 0 )
if( overlap.any() )
{
if( aCtx->GetLayer() == UNDEFINED_LAYER || overlap.Contains( aCtx->GetLayer() ) )
return areaOutline.Collide( &holeShape );
return areaOutline.Collide( aItem->GetEffectiveHoleShape().get() );
}
}

View File

@ -1086,13 +1086,13 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
m_gal->SetLineWidth( m_holePlatingThickness );
m_gal->SetStrokeColor( color );
const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
int holeSize = seg->GetWidth() + m_holePlatingThickness;
std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
int holeSize = slot->GetWidth() + m_holePlatingThickness;
if( seg->GetSeg().A == seg->GetSeg().B ) // Circular hole
m_gal->DrawCircle( seg->GetSeg().A, holeSize / 2 );
if( slot->GetSeg().A == slot->GetSeg().B ) // Circular hole
m_gal->DrawCircle( slot->GetSeg().A, KiROUND( holeSize / 2.0 ) );
else
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, holeSize );
m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B, holeSize );
return;
}
@ -1122,12 +1122,12 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
if( aLayer == LAYER_PAD_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
{
const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
if( seg->GetSeg().A == seg->GetSeg().B ) // Circular hole
m_gal->DrawCircle( seg->GetSeg().A, getDrillSize( aPad ).x / 2 );
if( slot->GetSeg().A == slot->GetSeg().B ) // Circular hole
m_gal->DrawCircle( slot->GetSeg().A, slot->GetWidth() / 2 );
else
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() );
m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B, slot->GetWidth() );
}
else
{
@ -1400,9 +1400,9 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
{
clearance += m_holePlatingThickness;
const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B,
seg->GetWidth() + 2 * clearance );
std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B,
slot->GetWidth() + 2 * clearance );
}
}
}

View File

@ -366,6 +366,12 @@ SEARCH_RESULT PCB_TRACK::Visit( INSPECTOR inspector, void* testData, const KICAD
}
std::shared_ptr<SHAPE_SEGMENT> PCB_VIA::GetEffectiveHoleShape() const
{
return std::make_shared<SHAPE_SEGMENT>( SEG( m_Start, m_Start ), KiROUND( m_drill / 2.0 ) );
}
bool PCB_VIA::IsTented() const
{
const BOARD* board = GetBoard();

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -38,6 +38,7 @@
#include <board_connected_item.h>
#include <convert_to_biu.h>
#include <geometry/shape_segment.h>
class PCB_TRACK;
@ -358,6 +359,8 @@ public:
return true;
}
std::shared_ptr<SHAPE_SEGMENT> GetEffectiveHoleShape() const override;
bool IsTented() const override;
int GetSolderMaskExpansion() const;

View File

@ -702,23 +702,22 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
{
for( PAD* pad : footprint->Pads() )
{
VECTOR2I hole = pad->GetDrillSize();
if( hole.x == 0 || hole.y == 0 )
continue;
if( hole.x == hole.y )
if( pad->HasHole() )
{
hole.x = std::min( smallDrill, hole.x );
aPlotter->Circle( pad->GetPosition(), hole.x, FILL_T::NO_FILL );
std::shared_ptr<SHAPE_SEGMENT> slot = pad->GetEffectiveHoleShape();
if( slot->GetSeg().A == slot->GetSeg().B ) // circular hole
{
int drill = std::min( smallDrill, slot->GetWidth() );
aPlotter->Circle( pad->GetPosition(), drill, FILL_T::NO_FILL );
}
else
{
// Note: small drill marks have no significance when applied to slots
const SHAPE_SEGMENT* seg = pad->GetEffectiveHoleShape();
aPlotter->ThickSegment( seg->GetSeg().A,
seg->GetSeg().B,
seg->GetWidth(), SKETCH, nullptr );
aPlotter->ThickSegment( slot->GetSeg().A,
slot->GetSeg().B,
slot->GetWidth(), SKETCH, nullptr );
}
}
}
}

View File

@ -1297,7 +1297,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
if( pad->GetProperty() == PAD_PROP::CASTELLATED )
{
std::unique_ptr<SHAPE> hole;
hole.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
hole.reset( pad->GetEffectiveHoleShape()->Clone() );
aWorld->AddEdgeExclusion( std::move( hole ) );
}
}

View File

@ -2816,24 +2816,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
}
}
std::unique_ptr<SHAPE_SEGMENT> holeShape;
if( aOther->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aOther );
VECTOR2I pos = via->GetPosition();
holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
}
else if( aOther->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( aOther );
if( pad->GetDrillSize().x )
holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
}
if( holeShape )
if( aOther->HasHole() )
{
constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
UNDEFINED_LAYER );
@ -2843,10 +2826,13 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
{
std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
if( viaShape->Collide( holeShape.get(), clearance - m_drcEpsilon ) )
if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(),
clearance - m_drcEpsilon ) )
{
return true;
}
}
}
return false;
}

View File

@ -187,21 +187,15 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE::testCourtyardClearances()
auto testPadAgainstCourtyards =
[&]( const PAD* pad, FOOTPRINT* footprint ) -> bool
{
// Skip pads with no hole
if( pad->GetAttribute() != PAD_ATTRIB::PTH
&& pad->GetAttribute() != PAD_ATTRIB::NPTH )
return false;
const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
if( pad->HasHole() )
{
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
const SHAPE_POLY_SET& front = footprint->GetPolyCourtyard( F_CrtYd );
const SHAPE_POLY_SET& back = footprint->GetPolyCourtyard( B_CrtYd );
if( front.OutlineCount() > 0 && front.Collide( hole, 0 ) )
{
if( front.OutlineCount() > 0 && front.Collide( hole.get(), 0 ) )
return true;
}
else if( back.OutlineCount() > 0 && back.Collide( hole, 0 ) )
{
else if( back.OutlineCount() > 0 && back.Collide( hole.get(), 0 ) )
return true;
}

View File

@ -1208,35 +1208,28 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
return 0;
}
if( fpFrame && !fpFrame->GetModel() )
return 0;
if( selection.Empty() )
{
if( fpFrame )
{
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( fpFrame->GetModel() );
FOOTPRINT* fp = static_cast<FOOTPRINT*>( fpFrame->GetModel() );
size_t padCount = fp->GetPadCount( DO_NOT_INCLUDE_NPTH );
if( !footprint )
return 0;
msgItems.emplace_back( _( "Library" ), fp->GetFPID().GetLibNickname().wx_str() );
wxString msg;
msgItems.emplace_back( _( "Footprint Name" ), fp->GetFPID().GetLibItemName().wx_str() );
msg = footprint->GetFPID().GetLibNickname().wx_str();
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Library" ), msg ) );
msgItems.emplace_back( _( "Pads" ), wxString::Format( wxT( "%zu" ), padCount ) );
msg = footprint->GetFPID().GetLibItemName().wx_str();
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Footprint Name" ), msg ) );
msg.Printf( wxT( "%zu" ), (size_t) footprint->GetPadCount( DO_NOT_INCLUDE_NPTH ) );
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Pads" ), msg ) );
wxString doc, keyword;
doc.Printf( _( "Doc: %s" ), footprint->GetDescription() );
keyword.Printf( _( "Keywords: %s" ), footprint->GetKeywords() );
msgItems.emplace_back( MSG_PANEL_ITEM( doc, keyword ) );
msgItems.emplace_back( wxString::Format( _( "Doc: %s" ), fp->GetDescription() ),
wxString::Format( _( "Keywords: %s" ), fp->GetKeywords() ) );
}
else
{
m_frame->SetMsgPanel( m_frame->GetBoard() );
return 0;
}
}
else if( selection.GetSize() == 1 )
@ -1245,15 +1238,10 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
item->GetMsgPanelInfo( m_frame, msgItems );
}
// Pair selection broken into multiple, optional data, starting with the selection item names
if( pcbFrame && selection.GetSize() == 2 )
else if( pcbFrame && selection.GetSize() == 2 )
{
auto clearanceString =
[&]( const DRC_CONSTRAINT& curr_constraint )
{
return StringFromValue( units, curr_constraint.m_Value.Min(), true );
};
// Pair selection broken into multiple, optional data, starting with the selected item
// names
BOARD_ITEM* a = static_cast<BOARD_ITEM*>( selection[0] );
BOARD_ITEM* b = static_cast<BOARD_ITEM*>( selection[1] );
@ -1281,14 +1269,13 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
int actual_clearance = a_shape->GetClearance( b_shape.get() );
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Resolved clearance" ),
clearanceString( constraint ) ) );
msgItems.emplace_back( _( "Resolved clearance" ),
MessageTextFromValue( units, constraint.m_Value.Min() ) );
if( actual_clearance > -1 && actual_clearance < std::numeric_limits<int>::max() )
{
msgItems.emplace_back(
MSG_PANEL_ITEM( _( "Actual clearance" ),
StringFromValue( units, actual_clearance, true ) ) );
msgItems.emplace_back( _( "Actual clearance" ),
MessageTextFromValue( units, actual_clearance ) );
}
}
}
@ -1309,9 +1296,33 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
if( layer >= 0 )
{
int actual = std::numeric_limits<int>::max();
if( a->HasHole() )
{
std::shared_ptr<SHAPE_SEGMENT> hole = a->GetEffectiveHoleShape();
std::shared_ptr<SHAPE> other( b->GetEffectiveShape( layer ) );
actual = std::min( actual, hole->GetClearance( other.get() ) );
}
if( b->HasHole() )
{
std::shared_ptr<SHAPE_SEGMENT> hole = b->GetEffectiveHoleShape();
std::shared_ptr<SHAPE> other( a->GetEffectiveShape( layer ) );
actual = std::min( actual, hole->GetClearance( other.get() ) );
}
constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer );
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Resolved hole clearance" ),
clearanceString( constraint ) ) );
msgItems.emplace_back( _( "Resolved hole clearance" ),
MessageTextFromValue( units, constraint.m_Value.Min() ) );
if( actual > -1 && actual < std::numeric_limits<int>::max() )
{
msgItems.emplace_back( _( "Actual hole clearance" ),
MessageTextFromValue( units, actual ) );
}
}
}
@ -1341,13 +1352,13 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
if( edgeLayer == Edge_Cuts )
{
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Resolved edge clearance" ),
clearanceString( constraint ) ) );
msgItems.emplace_back( _( "Resolved edge clearance" ),
MessageTextFromValue( units, constraint.m_Value.Min() ) );
}
else
{
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Resolved margin clearance" ),
clearanceString( constraint ) ) );
msgItems.emplace_back( _( "Resolved margin clearance" ),
MessageTextFromValue( units, constraint.m_Value.Min() ) );
}
}
}
@ -1355,8 +1366,8 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
if( msgItems.empty() )
{
wxString msg = wxString::Format( wxT( "%d" ), selection.GetSize() );
msgItems.emplace_back( MSG_PANEL_ITEM( _( "Selected Items" ), msg ) );
msgItems.emplace_back( _( "Selected Items" ),
wxString::Format( wxT( "%d" ), selection.GetSize() ) );
}
m_frame->SetMsgPanel( msgItems );

View File

@ -522,7 +522,7 @@ void ZONE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>&
AccumulateDescription( msg, _( "No footprints" ) );
if( !msg.IsEmpty() )
aList.emplace_back( MSG_PANEL_ITEM( _( "Restrictions" ), msg ) );
aList.emplace_back( _( "Restrictions" ), msg );
}
else if( IsOnCopperLayer() )
{
@ -586,23 +586,14 @@ void ZONE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>&
source ) );
}
// Useful for statistics, especially when zones are complex the number of hatches
// and filled polygons can explain the display and DRC calculation time:
msg.Printf( wxT( "%d" ), (int) m_borderHatchLines.size() );
aList.emplace_back( MSG_PANEL_ITEM( _( "HatchBorder Lines" ), msg ) );
if( !m_FilledPolysList.empty() )
{
count = 0;
for( auto item: m_FilledPolysList )
{
const std::shared_ptr<SHAPE_POLY_SET>& polyset = item.second;
count += polyset->TotalVertices();
}
for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& ii: m_FilledPolysList )
count += ii.second->TotalVertices();
msg.Printf( wxT( "%d" ), count );
aList.emplace_back( MSG_PANEL_ITEM( _( "Corner Count" ), msg ) );
aList.emplace_back( _( "Corner Count" ), wxString::Format( wxT( "%d" ), count ) );
}
}