Added more sophisticated checking for thermal spoke connections.
And, again, some more performance optimizations to make up for it.
This commit is contained in:
parent
49610085ea
commit
3784950603
|
@ -390,7 +390,9 @@ bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy ) const
|
|||
}
|
||||
}
|
||||
|
||||
return inside && !PointOnEdge( aPt, aAccuracy );
|
||||
// If aAccuracy is > 0 then by definition we don't care whether or not the point is
|
||||
// *exactly* on the edge -- which saves us considerable processing time
|
||||
return inside && ( aAccuracy > 0 || !PointOnEdge( aPt ) );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1391,19 +1391,20 @@ bool SHAPE_POLY_SET::CollideEdge( const VECTOR2I& aPoint,
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles ) const
|
||||
bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles,
|
||||
bool aIgnoreEdges ) const
|
||||
{
|
||||
if( m_polys.size() == 0 ) // empty set?
|
||||
return false;
|
||||
|
||||
// If there is a polygon specified, check the condition against that polygon
|
||||
if( aSubpolyIndex >= 0 )
|
||||
return containsSingle( aP, aSubpolyIndex, aIgnoreHoles );
|
||||
return containsSingle( aP, aSubpolyIndex, aIgnoreHoles, aIgnoreEdges );
|
||||
|
||||
// In any other case, check it against all polygons in the set
|
||||
for( int polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ )
|
||||
{
|
||||
if( containsSingle( aP, polygonIdx, aIgnoreHoles ) )
|
||||
if( containsSingle( aP, polygonIdx, aIgnoreHoles, aIgnoreEdges ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1429,10 +1430,11 @@ void SHAPE_POLY_SET::RemoveVertex( VERTEX_INDEX aIndex )
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles ) const
|
||||
bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles,
|
||||
bool aIgnoreEdges ) const
|
||||
{
|
||||
// Check that the point is inside the outline
|
||||
if( pointInPolygon( aP, m_polys[aSubpolyIndex][0] ) )
|
||||
if( pointInPolygon( aP, m_polys[aSubpolyIndex][0], aIgnoreEdges ) )
|
||||
{
|
||||
if( !aIgnoreHoles )
|
||||
{
|
||||
|
@ -1443,7 +1445,7 @@ bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool
|
|||
|
||||
// If the point is inside a hole (and not on its edge),
|
||||
// it is outside of the polygon
|
||||
if( pointInPolygon( aP, hole ) )
|
||||
if( pointInPolygon( aP, hole, aIgnoreEdges ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1455,9 +1457,10 @@ bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const
|
||||
bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath,
|
||||
bool aIgnoreEdges ) const
|
||||
{
|
||||
return aPath.PointInside( aP );
|
||||
return aPath.PointInside( aP, aIgnoreEdges ? 1 : 0 );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1466,9 +1469,7 @@ void SHAPE_POLY_SET::Move( const VECTOR2I& aVector )
|
|||
for( POLYGON& poly : m_polys )
|
||||
{
|
||||
for( SHAPE_LINE_CHAIN& path : poly )
|
||||
{
|
||||
path.Move( aVector );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1478,9 +1479,7 @@ void SHAPE_POLY_SET::Rotate( double aAngle, const VECTOR2I& aCenter )
|
|||
for( POLYGON& poly : m_polys )
|
||||
{
|
||||
for( SHAPE_LINE_CHAIN& path : poly )
|
||||
{
|
||||
path.Rotate( aAngle, aCenter );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1492,9 +1491,7 @@ int SHAPE_POLY_SET::TotalVertices() const
|
|||
for( const POLYGON& poly : m_polys )
|
||||
{
|
||||
for( const SHAPE_LINE_CHAIN& path : poly )
|
||||
{
|
||||
c += path.PointCount();
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
|
|
|
@ -977,9 +977,12 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
* @param aP is the point to check
|
||||
* @param aSubpolyIndex is the subpolygon to check, or -1 to check all
|
||||
* @param aIgnoreHoles controls whether or not internal holes are considered
|
||||
* @param aIgnoreEdges controls whether or not a check for the point lying exactly on
|
||||
* the polygon edge is made
|
||||
* @return true if the polygon contains the point
|
||||
*/
|
||||
bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1, bool aIgnoreHoles = false ) const;
|
||||
bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1, bool aIgnoreHoles = false,
|
||||
bool aIgnoreEdges = false ) const;
|
||||
|
||||
///> Returns true if the set is empty (no polygons at all)
|
||||
bool IsEmpty() const
|
||||
|
@ -1136,14 +1139,14 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
* if aFastMode is PM_STRICTLY_SIMPLE (default) the result is (theorically) a strictly
|
||||
* simple polygon, but calculations can be really significantly time consuming
|
||||
*/
|
||||
void booleanOp( ClipperLib::ClipType aType,
|
||||
void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape,
|
||||
POLYGON_MODE aFastMode );
|
||||
|
||||
void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aShape,
|
||||
const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode );
|
||||
|
||||
void booleanOp( ClipperLib::ClipType aType,
|
||||
const SHAPE_POLY_SET& aShape,
|
||||
const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode );
|
||||
|
||||
bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const;
|
||||
bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath,
|
||||
bool aIgnoreEdges ) const;
|
||||
|
||||
/**
|
||||
* containsSingle function
|
||||
|
@ -1154,10 +1157,13 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
* @param aSubpolyIndex is an integer specifying which polygon in the set has to be
|
||||
* checked.
|
||||
* @param aIgnoreHoles can be set to true to ignore internal holes in the polygon
|
||||
* @param aIgnoreEdges can be set to true to skip checking whether or not the point
|
||||
* lies directly on the edge
|
||||
* @return bool - true if aP is inside aSubpolyIndex-th polygon; false in any other
|
||||
* case.
|
||||
*/
|
||||
bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles = false ) const;
|
||||
bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles = false,
|
||||
bool aIgnoreEdges = false ) const;
|
||||
|
||||
/**
|
||||
* Operations ChamferPolygon and FilletPolygon are computed under the private chamferFillet
|
||||
|
|
|
@ -492,8 +492,7 @@ void ZONE_FILLER::knockoutThermals( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET&
|
|||
* Removes clearance from the shape for copper items which share the zone's layer but are
|
||||
* not connected to it.
|
||||
*/
|
||||
void ZONE_FILLER::knockoutCopperItems( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFill,
|
||||
std::deque<SHAPE_LINE_CHAIN>& aSpokes)
|
||||
void ZONE_FILLER::knockoutCopperItems( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFill )
|
||||
{
|
||||
SHAPE_POLY_SET holes;
|
||||
|
||||
|
@ -705,7 +704,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
dumper->BeginGroup( "clipper-zone" );
|
||||
|
||||
SHAPE_POLY_SET solidAreas = aSmoothedOutline;
|
||||
std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
|
||||
std::deque<THERMAL_SPOKE> thermalSpokes;
|
||||
|
||||
int numSegs = std::max( GetArcToSegmentCount( outline_half_thickness, m_high_def, 360.0 ), 6 );
|
||||
|
||||
|
@ -719,7 +718,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
|
||||
buildThermalSpokes( aZone, thermalSpokes );
|
||||
|
||||
knockoutCopperItems( aZone, solidAreas, thermalSpokes );
|
||||
knockoutCopperItems( aZone, solidAreas );
|
||||
|
||||
if( s_DumpZonesWhenFilling )
|
||||
dumper->Write( &solidAreas, "solid-areas-minus-clearances" );
|
||||
|
@ -728,11 +727,14 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
{
|
||||
SHAPE_POLY_SET amalgamatedSpokes;
|
||||
|
||||
for( SHAPE_LINE_CHAIN& spoke : thermalSpokes )
|
||||
for( THERMAL_SPOKE& spoke : thermalSpokes )
|
||||
{
|
||||
// Add together all spokes whose endpoints lie within the zone's filled area
|
||||
if( solidAreas.Contains( spoke.Point(2) ) && solidAreas.Contains( spoke.Point(3) ) )
|
||||
amalgamatedSpokes.AddOutline( spoke );
|
||||
if( solidAreas.Contains( spoke.m_TestPtA, -1, false, true )
|
||||
&& solidAreas.Contains( spoke.m_TestPtB, -1, false, true ) )
|
||||
{
|
||||
amalgamatedSpokes.AddOutline( spoke.m_Outline );
|
||||
}
|
||||
}
|
||||
|
||||
amalgamatedSpokes.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
|
@ -851,7 +853,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPol
|
|||
* Function buildThermalSpokes
|
||||
*/
|
||||
void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
|
||||
std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
|
||||
std::deque<THERMAL_SPOKE>& aSpokesList )
|
||||
{
|
||||
auto zoneBB = aZone->GetBoundingBox();
|
||||
int zone_clearance = aZone->GetZoneClearance();
|
||||
|
@ -860,17 +862,17 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
|
|||
zoneBB.Inflate( biggest_clearance );
|
||||
|
||||
// half size of the pen used to draw/plot zones outlines
|
||||
int pen_radius = aZone->GetMinThickness() / 2;
|
||||
int pen_w = aZone->GetMinThickness() / 2;
|
||||
|
||||
// Is a point on the boundary of the polygon inside or outside? This small correction
|
||||
// lets us avoid the question.
|
||||
int boundaryCorrection = KiROUND( IU_PER_MM * 0.04 );
|
||||
// Is a point on the boundary of the polygon inside or outside? This small epsilon lets
|
||||
// us avoid the question.
|
||||
int epsilon = KiROUND( IU_PER_MM * 0.04 );
|
||||
|
||||
// We'd normally add in an arcCorrection for circles (since a finite number of segments
|
||||
// is only an approximation of the circle radius). However, boundaryCorrection is already
|
||||
// twice even our ARC_LOW_DEF error tolerance, so there's little benefit to it (and a small
|
||||
// but existant performance penalty).
|
||||
//int numSegs = std::max( GetArcToSegmentCount( pen_raidus, m_high_def, 360.0 ), 6 );
|
||||
// is only an approximation of the circle radius). However, epsilon is already twice even
|
||||
// our LOW resolution error tolerance, so there's little benefit to it (and a small but
|
||||
// existant performance penalty).
|
||||
//int numSegs = std::max( GetArcToSegmentCount( pen_w, m_high_def, 360.0 ), 6 );
|
||||
//double arcCorrection = GetCircletoPolyCorrectionFactor( numSegs );
|
||||
|
||||
for( auto module : m_board->Modules() )
|
||||
|
@ -883,17 +885,16 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
|
|||
int thermalReliefGap = aZone->GetThermalReliefGap( pad );
|
||||
|
||||
// Calculate thermal bridge half width
|
||||
int spokeThickness = aZone->GetThermalReliefCopperBridge( pad )
|
||||
- aZone->GetMinThickness();
|
||||
int spoke_w = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness();
|
||||
|
||||
if( spokeThickness <= 0 )
|
||||
if( spoke_w <= 0 )
|
||||
continue;
|
||||
|
||||
spokeThickness = spokeThickness / 2;
|
||||
spoke_w = spoke_w / 2;
|
||||
|
||||
// Quick test here to possibly save us some work
|
||||
BOX2I itemBB = pad->GetBoundingBox();
|
||||
itemBB.Inflate( thermalReliefGap + pen_radius + boundaryCorrection );
|
||||
itemBB.Inflate( thermalReliefGap + pen_w + epsilon );
|
||||
|
||||
if( !( itemBB.Intersects( zoneBB ) ) )
|
||||
continue;
|
||||
|
@ -907,7 +908,7 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
|
|||
pad->SetOrientation( 0.0 );
|
||||
pad->SetPosition( - pad->GetOffset() );
|
||||
BOX2I reliefBB = pad->GetBoundingBox();
|
||||
reliefBB.Inflate( thermalReliefGap + pen_radius + boundaryCorrection );
|
||||
reliefBB.Inflate( thermalReliefGap + pen_w + epsilon );
|
||||
|
||||
// For circle pads, the thermal stubs orientation is 45 deg
|
||||
if( pad->GetShape() == PAD_SHAPE_CIRCLE )
|
||||
|
@ -915,47 +916,60 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
|
|||
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
SHAPE_LINE_CHAIN spoke;
|
||||
THERMAL_SPOKE spoke;
|
||||
// polygons are rectangles with width of copper bridge value
|
||||
switch( i )
|
||||
{
|
||||
case 0: // lower stub
|
||||
spoke.Append( +spokeThickness, 0 );
|
||||
spoke.Append( -spokeThickness, 0 );
|
||||
spoke.Append( -spokeThickness, reliefBB.GetBottom() );
|
||||
spoke.Append( +spokeThickness, reliefBB.GetBottom() );
|
||||
spoke.m_Outline.Append( +spoke_w, 0 );
|
||||
spoke.m_Outline.Append( -spoke_w, 0 );
|
||||
spoke.m_Outline.Append( -spoke_w, reliefBB.GetBottom() );
|
||||
spoke.m_Outline.Append( +spoke_w, reliefBB.GetBottom() );
|
||||
spoke.m_TestPtA = { epsilon - spoke_w, reliefBB.GetBottom() };
|
||||
spoke.m_TestPtB = { spoke_w - epsilon, reliefBB.GetBottom() };
|
||||
break;
|
||||
|
||||
case 1: // upper stub
|
||||
spoke.Append( +spokeThickness, 0 );
|
||||
spoke.Append( -spokeThickness, 0 );
|
||||
spoke.Append( -spokeThickness, reliefBB.GetTop() );
|
||||
spoke.Append( +spokeThickness, reliefBB.GetTop() );
|
||||
spoke.m_Outline.Append( +spoke_w, 0 );
|
||||
spoke.m_Outline.Append( -spoke_w, 0 );
|
||||
spoke.m_Outline.Append( -spoke_w, reliefBB.GetTop() );
|
||||
spoke.m_Outline.Append( +spoke_w, reliefBB.GetTop() );
|
||||
spoke.m_TestPtA = { epsilon - spoke_w, reliefBB.GetTop() };
|
||||
spoke.m_TestPtB = { spoke_w - epsilon, reliefBB.GetTop() };
|
||||
break;
|
||||
|
||||
case 2: // right stub
|
||||
spoke.Append( 0, spokeThickness );
|
||||
spoke.Append( 0, -spokeThickness );
|
||||
spoke.Append( reliefBB.GetRight(), -spokeThickness );
|
||||
spoke.Append( reliefBB.GetRight(), spokeThickness );
|
||||
spoke.m_Outline.Append( 0, spoke_w );
|
||||
spoke.m_Outline.Append( 0, -spoke_w );
|
||||
spoke.m_Outline.Append( reliefBB.GetRight(), -spoke_w );
|
||||
spoke.m_Outline.Append( reliefBB.GetRight(), spoke_w );
|
||||
spoke.m_TestPtA = { reliefBB.GetRight(), epsilon - spoke_w };
|
||||
spoke.m_TestPtB = { reliefBB.GetRight(), spoke_w - epsilon };
|
||||
break;
|
||||
|
||||
case 3: // left stub
|
||||
spoke.Append( 0, spokeThickness );
|
||||
spoke.Append( 0, -spokeThickness );
|
||||
spoke.Append( reliefBB.GetLeft(), -spokeThickness );
|
||||
spoke.Append( reliefBB.GetLeft(), spokeThickness );
|
||||
spoke.m_Outline.Append( 0, spoke_w );
|
||||
spoke.m_Outline.Append( 0, -spoke_w );
|
||||
spoke.m_Outline.Append( reliefBB.GetLeft(), -spoke_w );
|
||||
spoke.m_Outline.Append( reliefBB.GetLeft(), spoke_w );
|
||||
spoke.m_TestPtA = { reliefBB.GetLeft(), epsilon - spoke_w };
|
||||
spoke.m_TestPtB = { reliefBB.GetLeft(), spoke_w - epsilon };
|
||||
break;
|
||||
}
|
||||
|
||||
for( int ic = 0; ic < spoke.PointCount(); ic++ )
|
||||
for( int j = 0; j < spoke.m_Outline.PointCount(); j++ )
|
||||
{
|
||||
RotatePoint( spoke.Point( ic ), spokeAngle );
|
||||
spoke.Point( ic ) += padPos + pad->GetOffset();
|
||||
RotatePoint( spoke.m_Outline.Point( j ), spokeAngle );
|
||||
spoke.m_Outline.Point( j ) += padPos + pad->GetOffset();
|
||||
}
|
||||
|
||||
spoke.SetClosed( true );
|
||||
aSpokesList.push_back( spoke );
|
||||
RotatePoint( spoke.m_TestPtA, spokeAngle );
|
||||
spoke.m_TestPtA += padPos + pad->GetOffset();
|
||||
RotatePoint( spoke.m_TestPtB, spokeAngle );
|
||||
spoke.m_TestPtB += padPos + pad->GetOffset();
|
||||
|
||||
spoke.m_Outline.SetClosed( true );
|
||||
aSpokesList.push_back( std::move( spoke ) );
|
||||
}
|
||||
|
||||
pad->SetPosition( padPos );
|
||||
|
|
|
@ -35,6 +35,20 @@ class COMMIT;
|
|||
class SHAPE_POLY_SET;
|
||||
class SHAPE_LINE_CHAIN;
|
||||
|
||||
struct THERMAL_SPOKE
|
||||
{
|
||||
SHAPE_LINE_CHAIN m_Outline;
|
||||
VECTOR2I m_TestPtA;
|
||||
VECTOR2I m_TestPtB;
|
||||
|
||||
THERMAL_SPOKE()
|
||||
{
|
||||
m_TestPtA = { 0, 0 };
|
||||
m_TestPtB = { 0, 0 };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ZONE_FILLER
|
||||
{
|
||||
public:
|
||||
|
@ -52,8 +66,7 @@ private:
|
|||
|
||||
void knockoutThermals( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFill );
|
||||
|
||||
void knockoutCopperItems( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFill,
|
||||
std::deque<SHAPE_LINE_CHAIN>& aSpokes );
|
||||
void knockoutCopperItems( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFill );
|
||||
|
||||
/**
|
||||
* Function computeRawFilledArea
|
||||
|
@ -73,7 +86,7 @@ private:
|
|||
* Function buildThermalSpokes
|
||||
* Constructs a list of all thermal spokes for the given zone.
|
||||
*/
|
||||
void buildThermalSpokes( const ZONE_CONTAINER* aZone, std::deque<SHAPE_LINE_CHAIN>& aSpokes );
|
||||
void buildThermalSpokes( const ZONE_CONTAINER* aZone, std::deque<THERMAL_SPOKE>& aSpokes );
|
||||
|
||||
/**
|
||||
* Build the filled solid areas polygons from zone outlines (stored in m_Poly)
|
||||
|
|
Loading…
Reference in New Issue