Overhaul of remove-unconnected's zone filling and drawing strategies.

1) The highest priority zone that a via/pad collides with "owns" its
connectivity state.  Once set, lower priority zones cannot change it --
and in fact, if they would have connected to it are forced not to.

2) The connectivity state goes with the zone fill state, and therefore
must be saved in the file.

3) Display of remove-unconnected's pads is no longer done in GetViewLOD()
(which isn't called for selected items), and is instead done in PCB_PAINTER.
This allows us to draw the full pad in outline mode when a via/pad is
selected which would otherwise only show the hole.

4) Note that in some cases this will still generate DRC errors -- in
particular when a via nearly collides with a higher priority zone it
won't get "owned" by that zone and may therefore have insufficient
clearance if said zone concludes it's unconnected and a subsequent
(lower priority) zone connects to it (causing it to now become flashed).

Fixes https://gitlab.com/kicad/code/kicad/issues/11299
This commit is contained in:
Jeff Young 2022-10-18 13:00:37 +01:00
parent ebd8ebb756
commit 437d2c4589
16 changed files with 267 additions and 58 deletions

View File

@ -338,5 +338,6 @@ zone
zone_45_only
zone_clearance
zone_connect
zone_layer_connections
zone_type
zones

View File

@ -41,6 +41,14 @@ class SHAPE;
class PCB_GROUP;
enum ZONE_LAYER_CONNECTION
{
ZLC_UNRESOLVED,
ZLC_CONNECTED,
ZLC_UNCONNECTED
};
/**
* A base class for any item which can be embedded within the #BOARD container class, and
* therefore instances of derived classes should only be found in Pcbnew or other programs

View File

@ -392,8 +392,7 @@ void CONNECTIVITY_DATA::PropagateNets( BOARD_COMMIT* aCommit, PROPAGATE_MODE aMo
bool CONNECTIVITY_DATA::IsConnectedOnLayer( const BOARD_CONNECTED_ITEM *aItem, int aLayer,
const std::initializer_list<KICAD_T>& aTypes,
bool aCheckOptionalFlashing ) const
const std::initializer_list<KICAD_T>& aTypes ) const
{
CN_CONNECTIVITY_ALGO::ITEM_MAP_ENTRY &entry = m_connAlgo->ItemEntry( aItem );

View File

@ -200,8 +200,7 @@ public:
unsigned int GetUnconnectedCount( bool aVisibileOnly ) const;
bool IsConnectedOnLayer( const BOARD_CONNECTED_ITEM* aItem, int aLayer,
const std::initializer_list<KICAD_T>& aTypes = {},
bool aCheckOptionalFlashing = false ) const;
const std::initializer_list<KICAD_T>& aTypes = {} ) const;
unsigned int GetNodeCount( int aNet = -1 ) const;

View File

@ -1575,6 +1575,9 @@ unsigned int DIALOG_NET_INSPECTOR::calculateViaLength( const PCB_TRACK* aTrack )
BOARD_DESIGN_SETTINGS& bds = m_brd->GetDesignSettings();
// Must be static to keep from raising its ugly head in performance profiles
static std::initializer_list<KICAD_T> traceAndPadTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T };
// calculate the via length individually from the board stackup and via's start and end layer.
if( bds.m_HasStackup )
{
@ -1583,8 +1586,7 @@ unsigned int DIALOG_NET_INSPECTOR::calculateViaLength( const PCB_TRACK* aTrack )
for( int layer = via->TopLayer(); layer <= via->BottomLayer(); ++layer )
{
if( m_brd->GetConnectivity()->IsConnectedOnLayer( via, layer,
{ PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T }, true ) )
if( m_brd->GetConnectivity()->IsConnectedOnLayer( via, layer, traceAndPadTypes ) )
{
if( top_layer == UNDEFINED_LAYER )
top_layer = PCB_LAYER_ID( layer );

View File

@ -102,6 +102,9 @@ PAD::PAD( FOOTPRINT* parent ) :
m_effectiveBoundingRadius = 0;
m_removeUnconnectedLayer = false;
m_keepTopBottomLayer = true;
for( size_t ii = 0; ii < arrayDim( m_zoneLayerConnections ); ++ii )
m_zoneLayerConnections[ ii ] = ZLC_UNCONNECTED;
}
@ -292,11 +295,16 @@ bool PAD::FlashLayer( int aLayer ) const
static std::initializer_list<KICAD_T> types = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
PCB_PAD_T };
// Do not check zones. Doing so results in race conditions when the via collides with
// two different zones of different priorities.
// Only the highest priority zone that a via interacts with on any given layer gets
// to determine if it is connected or not. This keeps us from deciding it's not
// flashed when filling the first zone, and then later having another zone connect to
// it, causing it to become flashed, resulting in the first zone having insufficient
// clearance.
// See https://gitlab.com/kicad/code/kicad/-/issues/11299.
if( m_zoneLayerConnections[ aLayer ] == ZLC_CONNECTED )
return true;
return board->GetConnectivity()->IsConnectedOnLayer( this, aLayer, types, true );
return board->GetConnectivity()->IsConnectedOnLayer( this, aLayer, types );
}
}
@ -1384,25 +1392,9 @@ double PAD::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
if( IsBackLayer( (PCB_LAYER_ID) aLayer ) && !aView->IsLayerVisible( LAYER_PAD_BK ) )
return HIDE;
LSET visible = LSET::AllLayersMask();
LSET visible = board->GetVisibleLayers() & board->GetEnabledLayers();
// Handle board visibility
if( board )
visible &= board->GetEnabledLayers();
// Handle view visibility
for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
{
if( !aView->IsLayerVisible( layer ) )
visible.set( layer, false );
}
if( aLayer == LAYER_PADS_TH )
{
if( !FlashLayer( visible ) )
return HIDE;
}
else if( IsHoleLayer( aLayer ) )
if( IsHoleLayer( aLayer ) )
{
if( !( visible & LSET::PhysicalLayersMask() ).any() )
return HIDE;
@ -1425,8 +1417,8 @@ double PAD::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
// Netnames will be shown only if zoom is appropriate
int divisor = std::min( GetBoundingBox().GetWidth(), GetBoundingBox().GetHeight() );
// Pad sizes can be zero briefly when someone is typing a number like "0.5"
// in the pad properties dialog
// Pad sizes can be zero briefly when someone is typing a number like "0.5" in the pad
// properties dialog
if( divisor == 0 )
return HIDE;
@ -1440,8 +1432,7 @@ double PAD::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
const BOX2I PAD::ViewBBox() const
{
// Bounding box includes soldermask too. Remember mask and/or paste
// margins can be < 0
// Bounding box includes soldermask too. Remember mask and/or paste margins can be < 0
int solderMaskMargin = std::max( GetSolderMaskExpansion(), 0 );
VECTOR2I solderPasteMargin = VECTOR2D( GetSolderPasteMargin() );
BOX2I bbox = GetBoundingBox();

View File

@ -32,6 +32,7 @@
#include <geometry/shape_compound.h>
#include <pad_shapes.h>
#include <geometry/eda_angle.h>
#include <core/arraydim.h>
class PCB_SHAPE;
class PARAM_CFG;
@ -718,6 +719,17 @@ public:
virtual void SwapData( BOARD_ITEM* aImage ) override;
void ClearZoneConnectionCache()
{
for( size_t ii = 0; ii < arrayDim( m_zoneLayerConnections ); ++ii )
m_zoneLayerConnections[ ii ] = ZLC_UNRESOLVED;
}
ZONE_LAYER_CONNECTION& ZoneConnectionCache( PCB_LAYER_ID aLayer ) const
{
return m_zoneLayerConnections[ aLayer ];
}
#if defined(DEBUG)
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif
@ -833,6 +845,8 @@ private:
EDA_ANGLE m_thermalSpokeAngle; // Rotation of the spokes. 45° will produce an X,
// while 90° will produce a +.
int m_thermalGap;
mutable ZONE_LAYER_CONNECTION m_zoneLayerConnections[B_Cu + 1];
};
#endif // PAD_H

View File

@ -1075,7 +1075,12 @@ void PCB_BASE_FRAME::SetDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions, boo
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
{
return via->GetViaType() == VIATYPE::BLIND_BURIED
|| via->GetViaType() == VIATYPE::MICROVIA;
|| via->GetViaType() == VIATYPE::MICROVIA
|| via->GetRemoveUnconnected();
}
else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
{
return pad->GetRemoveUnconnected();
}
return false;

View File

@ -803,8 +803,9 @@ void PCB_PAINTER::draw( const PCB_ARC* aArc, int aLayer )
void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
{
COLOR4D color = m_pcbSettings.GetColor( aVia, aLayer );
VECTOR2D center( aVia->GetStart() );
const BOARD* board = aVia->GetBoard();
COLOR4D color = m_pcbSettings.GetColor( aVia, aLayer );
VECTOR2D center( aVia->GetStart() );
if( color == COLOR4D::CLEAR )
return;
@ -901,10 +902,21 @@ void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
{
int annular_width = ( aVia->GetWidth() - getDrillSize( aVia ) ) / 2.0;
double radius = aVia->GetWidth() / 2.0;
bool draw = aLayer == LAYER_VIA_THROUGH;
bool draw = false;
if( m_pcbSettings.IsPrinting() )
{
draw = aVia->FlashLayer( m_pcbSettings.GetPrintLayers() );
}
else if( aVia->FlashLayer( board->GetVisibleLayers() & board->GetEnabledLayers() ) )
{
draw = true;
}
else if( aVia->IsSelected() )
{
draw = true;
outline_mode = true;
}
if( !outline_mode )
{
@ -983,7 +995,8 @@ void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
{
COLOR4D color = m_pcbSettings.GetColor( aPad, aLayer );
const BOARD* board = aPad->GetBoard();
COLOR4D color = m_pcbSettings.GetColor( aPad, aLayer );
if( IsNetnameLayer( aLayer ) )
{
@ -1196,6 +1209,8 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
m_gal->SetFillColor( color );
}
bool drawShape = false;
if( aLayer == LAYER_PAD_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
{
std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
@ -1205,7 +1220,21 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
else
m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B, slot->GetWidth() );
}
else
else if( m_pcbSettings.IsPrinting() )
{
drawShape = aPad->FlashLayer( m_pcbSettings.GetPrintLayers() );
}
else if( aPad->FlashLayer( board->GetVisibleLayers() & board->GetEnabledLayers() ) )
{
drawShape = true;
}
else if( aPad->IsSelected() )
{
drawShape = true;
outline_mode = true;
}
if( drawShape )
{
VECTOR2I pad_size = aPad->GetSize();
VECTOR2I margin;

View File

@ -83,8 +83,13 @@ PCB_VIA::PCB_VIA( BOARD_ITEM* aParent ) :
SetViaType( VIATYPE::THROUGH );
m_bottomLayer = B_Cu;
SetDrillDefault();
m_removeUnconnectedLayer = false;
m_keepTopBottomLayer = true;
for( size_t ii = 0; ii < arrayDim( m_zoneLayerConnections ); ++ii )
m_zoneLayerConnections[ ii ] = ZLC_UNCONNECTED;
m_isFree = false;
}
@ -613,13 +618,17 @@ bool PCB_VIA::FlashLayer( int aLayer ) const
// Must be static to keep from raising its ugly head in performance profiles
static std::initializer_list<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
PCB_PAD_T, PCB_ZONE_T, PCB_FP_ZONE_T };
PCB_PAD_T };
// Do not check zones. Doing so results in race conditions when the via collides with
// two different zones of different priorities.
// Only the highest priority zone that a via interacts with on any given layer gets to
// determine if it is connected or not. This keeps us from deciding it's not flashed when
// filling the first zone, and then later having another zone connect to it, causing it to
// become flashed, resulting in the first zone having insufficient clearance.
// See https://gitlab.com/kicad/code/kicad/-/issues/11299.
if( m_zoneLayerConnections[ aLayer ] == ZLC_CONNECTED )
return true;
return board->GetConnectivity()->IsConnectedOnLayer( this, aLayer, connectedTypes, true );
return board->GetConnectivity()->IsConnectedOnLayer( this, aLayer, connectedTypes );
}
@ -762,12 +771,7 @@ double PCB_VIA::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
return HIDE;
}
if( IsViaPadLayer( aLayer ) )
{
if( !FlashLayer( visible ) )
return HIDE;
}
else if( IsHoleLayer( aLayer ) )
if( IsHoleLayer( aLayer ) )
{
if( m_viaType == VIATYPE::BLIND_BURIED || m_viaType == VIATYPE::MICROVIA )
{

View File

@ -39,7 +39,8 @@
#include <board_connected_item.h>
#include <base_units.h>
#include <geometry/shape_segment.h>
#include "core/minoptmax.h"
#include <core/minoptmax.h>
#include <core/arraydim.h>
class PCB_TRACK;
class PCB_VIA;
@ -504,6 +505,17 @@ public:
std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
FLASHING aFlash = FLASHING::DEFAULT ) const override;
void ClearZoneConnectionCache()
{
for( size_t ii = 0; ii < arrayDim( m_zoneLayerConnections ); ++ii )
m_zoneLayerConnections[ ii ] = ZLC_UNRESOLVED;
}
ZONE_LAYER_CONNECTION& ZoneConnectionCache( PCB_LAYER_ID aLayer ) const
{
return m_zoneLayerConnections[ aLayer ];
}
protected:
wxString layerMaskDescribe() const override;
@ -518,6 +530,8 @@ private:
bool m_removeUnconnectedLayer; ///< Remove unconnected copper on a via
bool m_keepTopBottomLayer; ///< Keep the top and bottom annular rings
bool m_isFree; ///< "Free" vias don't get their nets auto-updated
mutable ZONE_LAYER_CONNECTION m_zoneLayerConnections[B_Cu + 1];
};

View File

@ -4701,7 +4701,7 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
case T_drill:
{
bool haveWidth = false;
bool haveWidth = false;
VECTOR2I drillSize = pad->GetDrillSize();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
@ -4869,7 +4869,7 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
case T_chamfer:
{
int chamfers = 0;
int chamfers = 0;
bool end_list = false;
while( !end_list )
@ -4912,7 +4912,6 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
}
case T_property:
{
while( token != T_RIGHT )
{
token = NextTok();
@ -4938,7 +4937,6 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
}
break;
}
case T_options:
parsePAD_option( pad.get() );
@ -5020,6 +5018,19 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
NeedRIGHT();
break;
case T_zone_layer_connections:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
PCB_LAYER_ID layer = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
if( layer < F_Cu || layer > B_Cu )
Expecting( "copper layer name" );
pad->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
}
break;
// Continue to process "(locked)" format which was output during 5.99 development
case T_locked:
// Pad locking is now a session preference
@ -5431,6 +5442,19 @@ PCB_VIA* PCB_PARSER::parsePCB_VIA()
NeedRIGHT();
break;
case T_zone_layer_connections:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
PCB_LAYER_ID layer = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
if( layer < F_Cu || layer > B_Cu )
Expecting( "copper layer name" );
via->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
}
break;
case T_tstamp:
NextTok();
const_cast<KIID&>( via->m_Uuid ) = CurStrToKIID();

View File

@ -1496,7 +1496,8 @@ void PCB_PLUGIN::formatLayers( LSET aLayerMask, int aNestLevel ) const
void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
{
const char* shape;
const BOARD* board = aPad->GetBoard();
const char* shape;
switch( aPad->GetShape() )
{
@ -1600,6 +1601,16 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
if( aPad->GetKeepTopBottom() )
m_out->Print( 0, " (keep_end_layers)" );
m_out->Print( 0, " (zone_layer_connections" );
for( LSEQ cu = board->GetEnabledLayers().CuStack(); cu; ++cu )
{
if( aPad->ZoneConnectionCache( *cu ) == ZLC_CONNECTED )
m_out->Print( 0, " %s", m_out->Quotew( LSET::Name( *cu ) ).c_str() );
}
m_out->Print( 0, ")" );
}
}
@ -2075,7 +2086,7 @@ void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
PCB_LAYER_ID layer1, layer2;
const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
BOARD* board = (BOARD*) via->GetParent();
const BOARD* board = via->GetBoard();
wxCHECK_RET( board != nullptr, wxT( "Via has no parent." ) );
@ -2137,6 +2148,19 @@ void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
if( via->GetIsFree() )
m_out->Print( 0, " (free)" );
if( via->GetRemoveUnconnected() )
{
m_out->Print( 0, " (zone_layer_connections" );
for( LSEQ cu = board->GetEnabledLayers().CuStack(); cu; ++cu )
{
if( via->ZoneConnectionCache( *cu ) == ZLC_CONNECTED )
m_out->Print( 0, " %s", m_out->Quotew( LSET::Name( *cu ) ).c_str() );
}
m_out->Print( 0, ")" );
}
}
else if( aTrack->Type() == PCB_ARC_T )
{

View File

@ -127,7 +127,8 @@ class SHAPE_LINE_CHAIN;
//#define SEXPR_BOARD_FILE_VERSION 20220621 // Add Image support
//#define SEXPR_BOARD_FILE_VERSION 20220815 // Add allow-soldermask-bridges-in-FPs flag
//#define SEXPR_BOARD_FILE_VERSION 20220818 // First-class storage for net-ties
#define SEXPR_BOARD_FILE_VERSION 20220914 // Number boxes for custom-shape pads
//#define SEXPR_BOARD_FILE_VERSION 20220914 // Number boxes for custom-shape pads
#define SEXPR_BOARD_FILE_VERSION 20221018 // Via & pad zone-layer-connections
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting

View File

@ -23,6 +23,8 @@
#include <bitmaps.h>
#include <board.h>
#include <board_design_settings.h>
#include <pad.h>
#include <pcb_track.h>
#include <eda_list_dialog.h>
#include <string_utils.h>
#include <footprint_edit_frame.h>
@ -1203,16 +1205,31 @@ void APPEARANCE_CONTROLS::SetObjectVisible( GAL_LAYER_ID aLayer, bool isVisible
void APPEARANCE_CONTROLS::setVisibleLayers( LSET aLayers )
{
KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
if( m_isFpEditor )
{
KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
view->SetLayerVisible( layer, aLayers.Contains( layer ) );
}
else
{
m_frame->GetBoard()->SetVisibleLayers( aLayers );
view->UpdateAllItemsConditionally( KIGFX::REPAINT,
[]( KIGFX::VIEW_ITEM* aItem ) -> bool
{
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
{
return via->GetRemoveUnconnected();
}
else if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
{
return pad->GetRemoveUnconnected();
}
return false;
} );
}
}

View File

@ -31,7 +31,6 @@
#include <zone.h>
#include <footprint.h>
#include <pad.h>
#include <pcb_shape.h>
#include <pcb_target.h>
#include <pcb_track.h>
#include <pcb_text.h>
@ -136,6 +135,8 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
pad->BuildEffectivePolygon();
}
pad->ClearZoneConnectionCache();
m_worstClearance = std::max( m_worstClearance, pad->GetLocalClearance() );
}
@ -149,6 +150,12 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
footprint->BuildCourtyardCaches();
}
for( PCB_TRACK* track : m_board->Tracks() )
{
if( track->Type() == PCB_VIA_T )
static_cast<PCB_VIA*>( track )->ClearZoneConnectionCache();
}
// Sort by priority to reduce deferrals waiting on higher priority zones.
//
std::sort( aZones.begin(), aZones.end(),
@ -775,6 +782,14 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
int gap = evalRulesForItems( PHYSICAL_CLEARANCE_CONSTRAINT,
aZone, aTrack, aLayer );
if( aTrack->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
if( via->ZoneConnectionCache( aLayer ) == ZLC_UNCONNECTED )
sameNet = false;
}
if( !sameNet )
{
gap = std::max( gap, evalRulesForItems( CLEARANCE_CONSTRAINT,
@ -1401,7 +1416,69 @@ bool ZONE_FILLER::fillSingleZone( ZONE* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_S
if( aZone->IsOnCopperLayer() )
{
if( fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
{
aZone->SetNeedRefill( false );
BOX2I zone_boundingbox = aZone->GetBoundingBox();
// Check all conditionally-flashed vias and pads which aren't owned yet to see if
// we own them. If so, set their connection caches. See FlashLayer() for additional
// background.
for( PCB_TRACK* track : m_board->Tracks() )
{
if( track->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( track );
if( !via->IsOnLayer( aLayer ) || !via->GetRemoveUnconnected() )
continue;
if( via->ZoneConnectionCache( aLayer ) == ZLC_UNRESOLVED
&& via->GetBoundingBox().Intersects( zone_boundingbox ) )
{
auto viaShape = via->GetEffectiveShape( aLayer, FLASHING::ALWAYS_FLASHED );
// If the via collides with the zone's outline then we "own" the via.
// If it collides with the fill then it's connected; otherwise not.
if( aZone->Outline()->Collide( viaShape.get() ) )
{
if( aFillPolys.Collide( viaShape.get() ) )
via->ZoneConnectionCache( aLayer ) = ZLC_CONNECTED;
else
via->ZoneConnectionCache( aLayer ) = ZLC_UNCONNECTED;
}
}
}
}
for( FOOTPRINT* footprint : m_board->Footprints() )
{
for( PAD* pad : footprint->Pads() )
{
if( !pad->IsOnLayer( aLayer ) || !pad->GetRemoveUnconnected() )
continue;
if( pad->ZoneConnectionCache( aLayer ) == ZLC_UNRESOLVED
&& pad->GetBoundingBox().Intersects( zone_boundingbox ) )
{
auto padShape = pad->GetEffectiveShape( aLayer, FLASHING::ALWAYS_FLASHED );
// If the pad collides with the zone's outline then we "own" the pad.
// If it collides with the fill then it's connected; otherwise not.
if( aZone->Outline()->Collide( padShape.get() ) )
{
if( aFillPolys.Collide( padShape.get() ) )
pad->ZoneConnectionCache( aLayer ) = ZLC_CONNECTED;
else
pad->ZoneConnectionCache( aLayer ) = ZLC_UNCONNECTED;
}
}
}
}
}
}
else
{