Fix a couple of DRC bugs where the bbox wasn't inflated for largestClearance.
Also removes a case of double-testing a pad with a non-plated hole.
This commit is contained in:
parent
909358e643
commit
1a672aba56
|
@ -298,7 +298,12 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
|
|||
return;
|
||||
}
|
||||
|
||||
if( !aItem->GetBoundingBox().Intersects( aZone->GetCachedBoundingBox() ) )
|
||||
EDA_RECT itemBBox = aItem->GetBoundingBox();
|
||||
EDA_RECT worstCaseBBox = itemBBox;
|
||||
|
||||
worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
|
||||
|
||||
if( !worstCaseBBox.Intersects( aZone->GetCachedBoundingBox() ) )
|
||||
return;
|
||||
|
||||
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
|
||||
|
@ -307,55 +312,38 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
|
|||
if( !testClearance && !testHoles )
|
||||
return;
|
||||
|
||||
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
|
||||
EDA_RECT itemBBox = aItem->GetBoundingBox();
|
||||
DRC_CONSTRAINT constraint;
|
||||
DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
|
||||
int clearance = -1;
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
bool unflashedPad = false;
|
||||
bool platedHole = false;
|
||||
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
|
||||
|
||||
if( !zoneTree )
|
||||
return;
|
||||
|
||||
DRC_CONSTRAINT constraint;
|
||||
int clearance = -1;
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
if( aItem->Type() == PCB_PAD_T )
|
||||
{
|
||||
unflashedPad = !static_cast<PAD*>( aItem )->FlashLayer( aLayer );
|
||||
PAD* pad = static_cast<PAD*>( aItem );
|
||||
bool flashedPad = pad->FlashLayer( aLayer );
|
||||
bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
|
||||
|
||||
if( unflashedPad && !aItem->HasHole() )
|
||||
return;
|
||||
|
||||
platedHole = static_cast<PAD*>( aItem )->GetAttribute() == PAD_ATTRIB::PTH;
|
||||
if( !flashedPad && !platedHole )
|
||||
testClearance = false;
|
||||
}
|
||||
|
||||
if( zoneTree && testClearance )
|
||||
if( testClearance )
|
||||
{
|
||||
if( unflashedPad && !platedHole )
|
||||
constraintType = HOLE_CLEARANCE_CONSTRAINT;
|
||||
|
||||
constraint = m_drcEngine->EvalRules( constraintType, aItem, aZone, aLayer );
|
||||
constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
|
||||
clearance = constraint.GetValue().Min();
|
||||
}
|
||||
|
||||
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
|
||||
{
|
||||
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
|
||||
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
|
||||
|
||||
if( unflashedPad )
|
||||
{
|
||||
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
|
||||
// 2x the plating thickness larger.
|
||||
if( platedHole )
|
||||
size += 2 * m_board->GetDesignSettings().GetHolePlatingThickness();
|
||||
|
||||
itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
|
||||
}
|
||||
|
||||
if( zoneTree && zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
|
||||
std::max( 0, clearance - m_drcEpsilon ),
|
||||
&actual, &pos ) )
|
||||
if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
|
||||
std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
|
||||
wxString msg;
|
||||
|
@ -373,7 +361,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
|
|||
}
|
||||
}
|
||||
|
||||
if( zoneTree && testHoles && aItem->HasHole() )
|
||||
if( testHoles && aItem->HasHole() )
|
||||
{
|
||||
std::shared_ptr<SHAPE_SEGMENT> holeShape;
|
||||
|
||||
|
@ -433,7 +421,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
|
|||
if( !reportProgress( ii++, m_board->Tracks().size(), progressDelta ) )
|
||||
break;
|
||||
|
||||
for( PCB_LAYER_ID layer : track->GetLayerSet().Seq() )
|
||||
for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & LSET::AllCuMask() ).Seq() )
|
||||
{
|
||||
std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
|
||||
|
||||
|
|
|
@ -177,27 +177,46 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
|
|||
continue;
|
||||
}
|
||||
|
||||
BOX2I frontBBox = frontA.BBoxFromCaches();
|
||||
BOX2I backBBox = backA.BBoxFromCaches();
|
||||
BOX2I frontA_worstCaseBBox = frontA.BBoxFromCaches();
|
||||
BOX2I backA_worstCaseBBox = backA.BBoxFromCaches();
|
||||
|
||||
frontBBox.Inflate( m_largestCourtyardClearance );
|
||||
backBBox.Inflate( m_largestCourtyardClearance );
|
||||
frontA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
|
||||
backA_worstCaseBBox.Inflate( m_largestCourtyardClearance );
|
||||
|
||||
EDA_RECT fpABBox = fpA->GetBoundingBox();
|
||||
EDA_RECT fpA_bbox = fpA->GetBoundingBox();
|
||||
|
||||
for( auto itB = itA + 1; itB != m_board->Footprints().end(); itB++ )
|
||||
{
|
||||
FOOTPRINT* fpB = *itB;
|
||||
EDA_RECT fpBBBox = fpB->GetBoundingBox();
|
||||
const SHAPE_POLY_SET& frontB = fpB->GetCourtyard( F_CrtYd );
|
||||
const SHAPE_POLY_SET& backB = fpB->GetCourtyard( B_CrtYd );
|
||||
DRC_CONSTRAINT constraint;
|
||||
int clearance;
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
if( frontB.OutlineCount() == 0 && backB.OutlineCount() == 0
|
||||
&& m_drcEngine->IsErrorLimitExceeded( DRCE_PTH_IN_COURTYARD )
|
||||
&& m_drcEngine->IsErrorLimitExceeded( DRCE_NPTH_IN_COURTYARD ) )
|
||||
{
|
||||
// No courtyards defined and no hole testing against other footprint's courtyards
|
||||
continue;
|
||||
}
|
||||
|
||||
BOX2I frontB_worstCaseBBox = frontB.BBoxFromCaches();
|
||||
BOX2I backB_worstCaseBBox = backB.BBoxFromCaches();
|
||||
|
||||
frontB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
|
||||
backB_worstCaseBBox.Inflate( m_largestCourtyardClearance );
|
||||
|
||||
EDA_RECT fpB_bbox = fpB->GetBoundingBox();
|
||||
DRC_CONSTRAINT constraint;
|
||||
int clearance;
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
//
|
||||
// Check courtyard-to-courtyard collisions on front of board.
|
||||
//
|
||||
|
||||
if( frontA.OutlineCount() > 0 && frontB.OutlineCount() > 0
|
||||
&& frontBBox.Intersects( frontB.BBoxFromCaches() ) )
|
||||
&& frontA_worstCaseBBox.Intersects( frontB.BBoxFromCaches() ) )
|
||||
{
|
||||
constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, F_Cu );
|
||||
clearance = constraint.GetValue().Min();
|
||||
|
@ -226,8 +245,12 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Check courtyard-to-courtyard collisions on back of board.
|
||||
//
|
||||
|
||||
if( backA.OutlineCount() > 0 && backB.OutlineCount() > 0
|
||||
&& backBBox.Intersects( backB.BBoxFromCaches() ) )
|
||||
&& backA_worstCaseBBox.Intersects( backB.BBoxFromCaches() ) )
|
||||
{
|
||||
constraint = m_drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, fpA, fpB, B_Cu );
|
||||
clearance = constraint.GetValue().Min();
|
||||
|
@ -256,6 +279,13 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Check pad-hole-to-courtyard collisions on front and back of board.
|
||||
//
|
||||
// NB: via holes are not checked. There is a presumption that a physical object goes
|
||||
// through a pad hole, which is not the case for via holes.
|
||||
//
|
||||
|
||||
auto testPadAgainstCourtyards =
|
||||
[&]( const PAD* pad, const FOOTPRINT* fp )
|
||||
{
|
||||
|
@ -292,15 +322,15 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
|
|||
}
|
||||
};
|
||||
|
||||
if( ( frontA.OutlineCount() > 0 && frontA.BBoxFromCaches().Intersects( fpBBBox ) )
|
||||
|| ( backA.OutlineCount() > 0 && backA.BBoxFromCaches().Intersects( fpBBBox ) ) )
|
||||
if( ( frontA.OutlineCount() > 0 && frontA_worstCaseBBox.Intersects( fpB_bbox ) )
|
||||
|| ( backA.OutlineCount() > 0 && backA_worstCaseBBox.Intersects( fpB_bbox ) ) )
|
||||
{
|
||||
for( const PAD* padB : fpB->Pads() )
|
||||
testPadAgainstCourtyards( padB, fpA );
|
||||
}
|
||||
|
||||
if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpABBox ) )
|
||||
|| ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpABBox ) ) )
|
||||
if( ( frontB.OutlineCount() > 0 && frontB.BBoxFromCaches().Intersects( fpA_bbox ) )
|
||||
|| ( backB.OutlineCount() > 0 && backB.BBoxFromCaches().Intersects( fpA_bbox ) ) )
|
||||
{
|
||||
for( const PAD* padA : fpA->Pads() )
|
||||
testPadAgainstCourtyards( padA, fpB );
|
||||
|
|
|
@ -71,7 +71,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
bool testItemAgainstItem( BOARD_ITEM* item, SHAPE* itemShape, PCB_LAYER_ID layer,
|
||||
bool testItemAgainstItem( BOARD_ITEM* aItem, SHAPE* aItemShape, PCB_LAYER_ID aLayer,
|
||||
BOARD_ITEM* other );
|
||||
|
||||
void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
|
||||
|
@ -119,6 +119,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
|
|||
|
||||
static const LSET courtyards( 2, F_CrtYd, B_CrtYd );
|
||||
|
||||
//
|
||||
// Generate a count for use in progress reporting.
|
||||
//
|
||||
|
||||
forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
|
@ -126,6 +130,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
|
|||
return true;
|
||||
} );
|
||||
|
||||
//
|
||||
// Generate a BOARD_ITEM RTree.
|
||||
//
|
||||
|
||||
forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
|
@ -158,6 +166,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
|
|||
progressDelta = 100;
|
||||
ii = 0;
|
||||
|
||||
//
|
||||
// Run clearance checks -between- items.
|
||||
//
|
||||
|
||||
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE )
|
||||
|| !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
|
||||
{
|
||||
|
@ -222,6 +234,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
|
|||
count = 0;
|
||||
ii = 0;
|
||||
|
||||
//
|
||||
// Generate a count for progress reporting.
|
||||
//
|
||||
|
||||
forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
|
||||
LSET::AllCuMask(),
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
|
@ -236,6 +252,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
|
|||
return true;
|
||||
} );
|
||||
|
||||
//
|
||||
// Run clearance checks -within- polygonal items.
|
||||
//
|
||||
|
||||
forEachGeometryItem( { PCB_ZONE_T, PCB_FP_ZONE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T },
|
||||
LSET::AllCuMask(),
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
|
@ -507,7 +527,9 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER
|
|||
{
|
||||
SHAPE_LINE_CHAIN* firstOutline = &fill.Outline( outlineIdx );
|
||||
|
||||
//
|
||||
// Step one: outline to outline clearance violations
|
||||
//
|
||||
|
||||
for( int ii = outlineIdx + 1; ii < fill.OutlineCount(); ++ii )
|
||||
{
|
||||
|
@ -541,7 +563,9 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER
|
|||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Step two: interior hole clearance violations
|
||||
//
|
||||
|
||||
for( int holeIdx = 0; holeIdx < fill.HoleCount( outlineIdx ); ++holeIdx )
|
||||
{
|
||||
|
@ -554,9 +578,9 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER
|
|||
}
|
||||
|
||||
|
||||
bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item,
|
||||
SHAPE* itemShape,
|
||||
PCB_LAYER_ID layer,
|
||||
bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* aItem,
|
||||
SHAPE* aItemShape,
|
||||
PCB_LAYER_ID aLayer,
|
||||
BOARD_ITEM* other )
|
||||
{
|
||||
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
|
||||
|
@ -566,17 +590,17 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
|
|||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( layer );
|
||||
std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
|
||||
|
||||
if( testClearance )
|
||||
{
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, item, other, layer );
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, other, aLayer );
|
||||
clearance = constraint.GetValue().Min();
|
||||
}
|
||||
|
||||
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
|
||||
{
|
||||
if( itemShape->Collide( otherShape.get(), clearance, &actual, &pos ) )
|
||||
if( aItemShape->Collide( otherShape.get(), clearance, &actual, &pos ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
|
||||
wxString msg;
|
||||
|
@ -587,10 +611,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
|
|||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
|
||||
drce->SetItems( item, other );
|
||||
drce->SetItems( aItem, other );
|
||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drce, pos, layer );
|
||||
reportViolation( drce, pos, aLayer );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,19 +624,19 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
|
|||
std::shared_ptr<SHAPE_SEGMENT> otherHoleShape;
|
||||
clearance = 0;
|
||||
|
||||
if( item->Type() == PCB_VIA_T )
|
||||
if( aItem->Type() == PCB_VIA_T )
|
||||
{
|
||||
if( item->GetLayerSet().Contains( layer ) )
|
||||
itemHoleShape = item->GetEffectiveHoleShape();
|
||||
if( aItem->GetLayerSet().Contains( aLayer ) )
|
||||
itemHoleShape = aItem->GetEffectiveHoleShape();
|
||||
}
|
||||
else if( item->HasHole() )
|
||||
else if( aItem->HasHole() )
|
||||
{
|
||||
itemHoleShape = item->GetEffectiveHoleShape();
|
||||
itemHoleShape = aItem->GetEffectiveHoleShape();
|
||||
}
|
||||
|
||||
if( other->Type() == PCB_VIA_T )
|
||||
{
|
||||
if( other->GetLayerSet().Contains( layer ) )
|
||||
if( other->GetLayerSet().Contains( aLayer ) )
|
||||
otherHoleShape = other->GetEffectiveHoleShape();
|
||||
}
|
||||
else if( other->HasHole() )
|
||||
|
@ -622,8 +646,8 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
|
|||
|
||||
if( itemHoleShape || otherHoleShape )
|
||||
{
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, other, item,
|
||||
layer );
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, other, aItem,
|
||||
aLayer );
|
||||
clearance = constraint.GetValue().Min();
|
||||
}
|
||||
|
||||
|
@ -640,13 +664,13 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
|
|||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
|
||||
drce->SetItems( item, other );
|
||||
drce->SetItems( aItem, other );
|
||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drce, pos, layer );
|
||||
reportViolation( drce, pos, aLayer );
|
||||
}
|
||||
|
||||
if( otherHoleShape && otherHoleShape->Collide( itemShape, clearance, &actual, &pos ) )
|
||||
if( otherHoleShape && otherHoleShape->Collide( aItemShape, clearance, &actual, &pos ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
|
||||
wxString msg;
|
||||
|
@ -657,10 +681,10 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
|
|||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
|
||||
drce->SetItems( item, other );
|
||||
drce->SetItems( aItem, other );
|
||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drce, pos, layer );
|
||||
reportViolation( drce, pos, aLayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -677,68 +701,113 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
|
|||
if( !zone->GetLayerSet().test( aLayer ) )
|
||||
continue;
|
||||
|
||||
if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) )
|
||||
EDA_RECT itemBBox = aItem->GetBoundingBox();
|
||||
EDA_RECT worstCaseBBox = itemBBox;
|
||||
|
||||
worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
|
||||
|
||||
if( !worstCaseBBox.Intersects( zone->GetCachedBoundingBox() ) )
|
||||
continue;
|
||||
|
||||
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
|
||||
bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
|
||||
|
||||
if( !testClearance && !testHoles )
|
||||
return;
|
||||
|
||||
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
|
||||
DRC_CONSTRAINT constraint;
|
||||
bool colliding;
|
||||
int clearance = -1;
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
if( testClearance )
|
||||
{
|
||||
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
|
||||
bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
|
||||
aLayer );
|
||||
clearance = constraint.GetValue().Min();
|
||||
}
|
||||
|
||||
if( !testClearance && !testHoles )
|
||||
return;
|
||||
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
|
||||
{
|
||||
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
|
||||
|
||||
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
|
||||
EDA_RECT itemBBox = aItem->GetBoundingBox();
|
||||
DRC_CONSTRAINT constraint;
|
||||
bool colliding;
|
||||
int clearance = -1;
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
if( testClearance )
|
||||
if( aItem->Type() == PCB_PAD_T )
|
||||
{
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, aItem, zone,
|
||||
aLayer );
|
||||
clearance = constraint.GetValue().Min();
|
||||
PAD* pad = static_cast<PAD*>( aItem );
|
||||
|
||||
if( !pad->FlashLayer( aLayer ) )
|
||||
{
|
||||
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
|
||||
continue;
|
||||
|
||||
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
|
||||
int size = hole->GetWidth();
|
||||
|
||||
// Note: drill size represents finish size, which means the actual hole
|
||||
// size is the plating thickness larger.
|
||||
if( pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
size += m_board->GetDesignSettings().GetHolePlatingThickness();
|
||||
|
||||
itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
|
||||
}
|
||||
}
|
||||
|
||||
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
|
||||
if( zoneTree )
|
||||
{
|
||||
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
|
||||
colliding = zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, clearance,
|
||||
&actual, &pos );
|
||||
}
|
||||
else
|
||||
{
|
||||
colliding = zone->Outline()->Collide( itemShape.get(), clearance, &actual, &pos );
|
||||
}
|
||||
|
||||
if( aItem->Type() == PCB_PAD_T )
|
||||
if( colliding )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
|
||||
wxString msg;
|
||||
|
||||
msg.Printf( _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), clearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
|
||||
drce->SetItems( aItem, zone );
|
||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drce, pos, aLayer );
|
||||
}
|
||||
}
|
||||
|
||||
if( testHoles )
|
||||
{
|
||||
std::shared_ptr<SHAPE_SEGMENT> holeShape;
|
||||
|
||||
if( aItem->Type() == PCB_VIA_T )
|
||||
{
|
||||
if( aItem->GetLayerSet().Contains( aLayer ) )
|
||||
holeShape = aItem->GetEffectiveHoleShape();
|
||||
}
|
||||
else if( aItem->HasHole() )
|
||||
{
|
||||
holeShape = aItem->GetEffectiveHoleShape();
|
||||
}
|
||||
|
||||
if( holeShape )
|
||||
{
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aItem,
|
||||
zone, aLayer );
|
||||
clearance = constraint.GetValue().Min();
|
||||
|
||||
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
|
||||
&& clearance > 0
|
||||
&& zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer, clearance,
|
||||
&actual, &pos ) )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( aItem );
|
||||
|
||||
if( !pad->FlashLayer( aLayer ) )
|
||||
{
|
||||
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
|
||||
continue;
|
||||
|
||||
std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
|
||||
int size = hole->GetWidth();
|
||||
|
||||
// Note: drill size represents finish size, which means the actual hole
|
||||
// size is the plating thickness larger.
|
||||
if( pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
size += m_board->GetDesignSettings().GetHolePlatingThickness();
|
||||
|
||||
itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
|
||||
}
|
||||
}
|
||||
|
||||
if( zoneTree )
|
||||
{
|
||||
colliding = zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
|
||||
clearance, &actual, &pos );
|
||||
}
|
||||
else
|
||||
{
|
||||
colliding = zone->Outline()->Collide( itemShape.get(), clearance, &actual,
|
||||
&pos );
|
||||
}
|
||||
|
||||
if( colliding )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
|
||||
wxString msg;
|
||||
|
||||
msg.Printf( _( "(%s clearance %s; actual %s)" ),
|
||||
|
@ -753,48 +822,6 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
|
|||
reportViolation( drce, pos, aLayer );
|
||||
}
|
||||
}
|
||||
|
||||
if( testHoles )
|
||||
{
|
||||
std::shared_ptr<SHAPE_SEGMENT> holeShape;
|
||||
|
||||
if( aItem->Type() == PCB_VIA_T )
|
||||
{
|
||||
if( aItem->GetLayerSet().Contains( aLayer ) )
|
||||
holeShape = aItem->GetEffectiveHoleShape();
|
||||
}
|
||||
else if( aItem->HasHole() )
|
||||
{
|
||||
holeShape = aItem->GetEffectiveHoleShape();
|
||||
}
|
||||
|
||||
if( holeShape )
|
||||
{
|
||||
constraint = m_drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, aItem,
|
||||
zone, aLayer );
|
||||
clearance = constraint.GetValue().Min();
|
||||
|
||||
if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE
|
||||
&& clearance > 0
|
||||
&& zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
|
||||
clearance, &actual, &pos ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
|
||||
wxString msg;
|
||||
|
||||
msg.Printf( _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), clearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
|
||||
drce->SetItems( aItem, zone );
|
||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drce, pos, aLayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( m_drcEngine->IsCancelled() )
|
||||
|
|
|
@ -73,7 +73,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
void addItemToRTrees( BOARD_ITEM* item );
|
||||
void addItemToRTrees( BOARD_ITEM* aItem );
|
||||
void buildRTrees();
|
||||
|
||||
void testSilkToMaskClearance();
|
||||
|
@ -97,7 +97,7 @@ private:
|
|||
int m_maxError;
|
||||
int m_largestClearance;
|
||||
|
||||
std::unique_ptr<DRC_RTREE> m_tesselatedTree;
|
||||
std::unique_ptr<DRC_RTREE> m_fullSolderMaskRTree;
|
||||
std::unique_ptr<DRC_RTREE> m_itemTree;
|
||||
|
||||
std::unordered_map<PTR_PTR_CACHE_KEY, LSET> m_checkedPairs;
|
||||
|
@ -109,13 +109,13 @@ private:
|
|||
};
|
||||
|
||||
|
||||
void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
|
||||
void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* aItem )
|
||||
{
|
||||
ZONE* solderMask = m_board->m_SolderMask;
|
||||
|
||||
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
|
||||
if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T )
|
||||
{
|
||||
ZONE* zone = static_cast<ZONE*>( item );
|
||||
ZONE* zone = static_cast<ZONE*>( aItem );
|
||||
|
||||
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
|
||||
{
|
||||
|
@ -126,35 +126,35 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
|
|||
}
|
||||
}
|
||||
}
|
||||
else if( item->Type() == PCB_PAD_T )
|
||||
else if( aItem->Type() == PCB_PAD_T )
|
||||
{
|
||||
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
|
||||
{
|
||||
if( item->IsOnLayer( layer ) )
|
||||
if( aItem->IsOnLayer( layer ) )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( item );
|
||||
PAD* pad = static_cast<PAD*>( aItem );
|
||||
int clearance = ( m_webWidth / 2 ) + pad->GetSolderMaskExpansion();
|
||||
|
||||
item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
|
||||
clearance, m_maxError, ERROR_OUTSIDE );
|
||||
aItem->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
|
||||
clearance, m_maxError, ERROR_OUTSIDE );
|
||||
|
||||
m_itemTree->Insert( item, layer, m_largestClearance );
|
||||
m_itemTree->Insert( aItem, layer, m_largestClearance );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( item->Type() == PCB_VIA_T )
|
||||
else if( aItem->Type() == PCB_VIA_T )
|
||||
{
|
||||
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
|
||||
{
|
||||
if( item->IsOnLayer( layer ) )
|
||||
if( aItem->IsOnLayer( layer ) )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
|
||||
int clearance = ( m_webWidth / 2 ) + via->GetSolderMaskExpansion();
|
||||
|
||||
via->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ), layer,
|
||||
clearance, m_maxError, ERROR_OUTSIDE );
|
||||
|
||||
m_itemTree->Insert( item, layer, m_largestClearance );
|
||||
m_itemTree->Insert( aItem, layer, m_largestClearance );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,13 +162,13 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
|
|||
{
|
||||
for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
|
||||
{
|
||||
if( item->IsOnLayer( layer ) )
|
||||
if( aItem->IsOnLayer( layer ) )
|
||||
{
|
||||
item->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ),
|
||||
layer, m_webWidth / 2, m_maxError,
|
||||
ERROR_OUTSIDE );
|
||||
aItem->TransformShapeWithClearanceToPolygon( *solderMask->GetFill( layer ),
|
||||
layer, m_webWidth / 2, m_maxError,
|
||||
ERROR_OUTSIDE );
|
||||
|
||||
m_itemTree->Insert( item, layer, m_largestClearance );
|
||||
m_itemTree->Insert( aItem, layer, m_largestClearance );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
|
|||
solderMask->GetFill( F_Mask )->RemoveAllContours();
|
||||
solderMask->GetFill( B_Mask )->RemoveAllContours();
|
||||
|
||||
m_tesselatedTree = std::make_unique<DRC_RTREE>();
|
||||
m_fullSolderMaskRTree = std::make_unique<DRC_RTREE>();
|
||||
m_itemTree = std::make_unique<DRC_RTREE>();
|
||||
|
||||
forEachGeometryItem( s_allBasicItems, layers,
|
||||
|
@ -221,8 +221,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
|
|||
|
||||
solderMask->CacheTriangulation();
|
||||
|
||||
m_tesselatedTree->Insert( solderMask, F_Mask );
|
||||
m_tesselatedTree->Insert( solderMask, B_Mask );
|
||||
m_fullSolderMaskRTree->Insert( solderMask, F_Mask );
|
||||
m_fullSolderMaskRTree->Insert( solderMask, B_Mask );
|
||||
|
||||
m_checkedPairs.clear();
|
||||
}
|
||||
|
@ -272,8 +272,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testSilkToMaskClearance()
|
|||
|
||||
std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
|
||||
|
||||
if( m_tesselatedTree->QueryColliding( itemBBox, itemShape.get(), layer,
|
||||
clearance, &actual, &pos ) )
|
||||
if( m_fullSolderMaskRTree->QueryColliding( itemBBox, itemShape.get(), layer,
|
||||
clearance, &actual, &pos ) )
|
||||
{
|
||||
auto drce = DRC_ITEM::Create( DRCE_SILK_CLEARANCE );
|
||||
wxString msg;
|
||||
|
@ -577,64 +577,59 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem,
|
|||
continue;
|
||||
}
|
||||
|
||||
if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) )
|
||||
EDA_RECT inflatedBBox( aItemBBox );
|
||||
int clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance;
|
||||
|
||||
if( aItem->Type() == PCB_PAD_T )
|
||||
clearance += static_cast<PAD*>( aItem )->GetSolderMaskExpansion();
|
||||
else if( aItem->Type() == PCB_VIA_T )
|
||||
clearance += static_cast<PCB_VIA*>( aItem )->GetSolderMaskExpansion();
|
||||
|
||||
inflatedBBox.Inflate( clearance );
|
||||
|
||||
if( !inflatedBBox.Intersects( zone->GetCachedBoundingBox() ) )
|
||||
continue;
|
||||
|
||||
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aMaskLayer );
|
||||
|
||||
if( zoneTree && zoneTree->QueryColliding( aItemBBox, itemShape.get(), aTargetLayer,
|
||||
clearance, &actual, &pos ) )
|
||||
{
|
||||
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
|
||||
int clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance;
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
wxString msg;
|
||||
BOARD_ITEM* colliding = nullptr;
|
||||
|
||||
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aMaskLayer );
|
||||
if( aMaskLayer == F_Mask )
|
||||
msg = _( "Front solder mask aperture bridges items with different nets" );
|
||||
else
|
||||
msg = _( "Rear solder mask aperture bridges items with different nets" );
|
||||
|
||||
if( aItem->Type() == PCB_PAD_T )
|
||||
// Simple mask apertures aren't associated with copper items, so they only constitute
|
||||
// a bridge when they expose other copper items having at least two distinct nets.
|
||||
if( isMaskAperture( aItem ) && zoneNet >= 0 )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( aItem );
|
||||
|
||||
clearance += pad->GetSolderMaskExpansion();
|
||||
}
|
||||
else if( aItem->Type() == PCB_VIA_T )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
|
||||
|
||||
clearance += via->GetSolderMaskExpansion();
|
||||
}
|
||||
|
||||
if( zoneTree && zoneTree->QueryColliding( aItemBBox, itemShape.get(), aTargetLayer,
|
||||
clearance, &actual, &pos ) )
|
||||
{
|
||||
wxString msg;
|
||||
BOARD_ITEM* colliding = nullptr;
|
||||
|
||||
if( aMaskLayer == F_Mask )
|
||||
msg = _( "Front solder mask aperture bridges items with different nets" );
|
||||
else
|
||||
msg = _( "Rear solder mask aperture bridges items with different nets" );
|
||||
|
||||
// Simple mask apertures aren't associated with copper items, so they only
|
||||
// constitute a bridge when they expose other copper items having at least
|
||||
// two distinct nets.
|
||||
if( isMaskAperture( aItem ) && zoneNet >= 0 )
|
||||
{
|
||||
if( checkMaskAperture( aItem, zone, aTargetLayer, zoneNet, &colliding ) )
|
||||
{
|
||||
auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
|
||||
|
||||
drce->SetErrorMessage( msg );
|
||||
drce->SetItems( aItem, colliding, zone );
|
||||
drce->SetViolatingRule( &m_bridgeRule );
|
||||
reportViolation( drce, pos, aTargetLayer );
|
||||
}
|
||||
}
|
||||
else
|
||||
if( checkMaskAperture( aItem, zone, aTargetLayer, zoneNet, &colliding ) )
|
||||
{
|
||||
auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
|
||||
|
||||
drce->SetErrorMessage( msg );
|
||||
drce->SetItems( aItem, zone );
|
||||
drce->SetItems( aItem, colliding, zone );
|
||||
drce->SetViolatingRule( &m_bridgeRule );
|
||||
reportViolation( drce, pos, aTargetLayer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
|
||||
|
||||
drce->SetErrorMessage( msg );
|
||||
drce->SetItems( aItem, zone );
|
||||
drce->SetViolatingRule( &m_bridgeRule );
|
||||
reportViolation( drce, pos, aTargetLayer );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_drcEngine->IsCancelled() )
|
||||
|
@ -722,8 +717,8 @@ bool DRC_TEST_PROVIDER_SOLDER_MASK::Run()
|
|||
m_largestClearance = std::max( m_largestClearance, pad->GetSolderMaskExpansion() );
|
||||
}
|
||||
|
||||
// Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is maxed
|
||||
// with the various SILK_CLEARANCE_CONSTRAINTS.
|
||||
// Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is
|
||||
// maxed with the various SILK_CLEARANCE_CONSTRAINTS.
|
||||
m_largestClearance += m_largestClearance + m_webWidth;
|
||||
|
||||
DRC_CONSTRAINT worstClearanceConstraint;
|
||||
|
|
|
@ -92,8 +92,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
|
|||
if( m_drcEngine->IsCancelled() )
|
||||
return;
|
||||
|
||||
//
|
||||
// Quick tests for "connected":
|
||||
//
|
||||
|
||||
if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
|
||||
continue;
|
||||
|
||||
|
@ -105,8 +107,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
|
|||
if( !pad->FlashLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
//
|
||||
// If those passed, do a thorough test:
|
||||
//
|
||||
|
||||
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer );
|
||||
ZONE_CONNECTION conn = constraint.m_ZoneConnection;
|
||||
|
||||
|
@ -139,8 +143,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
|
|||
if( spokes >= minCount ) // We already have enough
|
||||
continue;
|
||||
|
||||
//
|
||||
// See if there are any other manual spokes added:
|
||||
//
|
||||
|
||||
for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
|
||||
{
|
||||
if( padOutline.PointInside( track->GetStart() ) )
|
||||
|
@ -155,8 +161,10 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// And finally report it if there aren't enough:
|
||||
//
|
||||
|
||||
if( spokes < minCount )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );
|
||||
|
|
Loading…
Reference in New Issue