ADDED: custom-shaped pad spoke templates.
This commit is contained in:
parent
31c88d1bcb
commit
d6b75c64e1
|
@ -47,7 +47,7 @@ EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
|
|||
m_rectangleWidth( 0 ),
|
||||
m_segmentLength( 0 ),
|
||||
m_editState( 0 ),
|
||||
m_annotationProxy( false )
|
||||
m_proxyItem( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -59,18 +59,27 @@ EDA_SHAPE::~EDA_SHAPE()
|
|||
|
||||
wxString EDA_SHAPE::ShowShape() const
|
||||
{
|
||||
if( IsAnnotationProxy() )
|
||||
return _( "Number Box" );
|
||||
|
||||
switch( m_shape )
|
||||
if( IsProxyItem() )
|
||||
{
|
||||
case SHAPE_T::SEGMENT: return _( "Line" );
|
||||
case SHAPE_T::RECTANGLE: return _( "Rect" );
|
||||
case SHAPE_T::ARC: return _( "Arc" );
|
||||
case SHAPE_T::CIRCLE: return _( "Circle" );
|
||||
case SHAPE_T::BEZIER: return _( "Bezier Curve" );
|
||||
case SHAPE_T::POLY: return _( "Polygon" );
|
||||
default: return wxT( "??" );
|
||||
switch( m_shape )
|
||||
{
|
||||
case SHAPE_T::SEGMENT: return _( "Thermal Spoke" );
|
||||
case SHAPE_T::RECTANGLE: return _( "Number Box" );
|
||||
default: return wxT( "??" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( m_shape )
|
||||
{
|
||||
case SHAPE_T::SEGMENT: return _( "Line" );
|
||||
case SHAPE_T::RECTANGLE: return _( "Rect" );
|
||||
case SHAPE_T::ARC: return _( "Arc" );
|
||||
case SHAPE_T::CIRCLE: return _( "Circle" );
|
||||
case SHAPE_T::BEZIER: return _( "Bezier Curve" );
|
||||
case SHAPE_T::POLY: return _( "Polygon" );
|
||||
default: return wxT( "??" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -674,15 +683,27 @@ void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeA
|
|||
|
||||
wxString EDA_SHAPE::GetFriendlyName() const
|
||||
{
|
||||
switch( m_shape )
|
||||
if( IsProxyItem() )
|
||||
{
|
||||
case SHAPE_T::CIRCLE: return _( "Circle" );
|
||||
case SHAPE_T::ARC: return _( "Arc" );
|
||||
case SHAPE_T::BEZIER: return _( "Curve" );
|
||||
case SHAPE_T::POLY: return _( "Polygon" );
|
||||
case SHAPE_T::RECTANGLE: return IsAnnotationProxy() ? _( "Pad Number Box" ) : _( "Rectangle" );
|
||||
case SHAPE_T::SEGMENT: return _( "Segment" );
|
||||
default: return _( "Unrecognized" );
|
||||
switch( m_shape )
|
||||
{
|
||||
case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
|
||||
case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
|
||||
default: return _( "Unrecognized" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( m_shape )
|
||||
{
|
||||
case SHAPE_T::CIRCLE: return _( "Circle" );
|
||||
case SHAPE_T::ARC: return _( "Arc" );
|
||||
case SHAPE_T::BEZIER: return _( "Curve" );
|
||||
case SHAPE_T::POLY: return _( "Polygon" );
|
||||
case SHAPE_T::RECTANGLE: return _( "Rectangle" );
|
||||
case SHAPE_T::SEGMENT: return _( "Segment" );
|
||||
default: return _( "Unrecognized" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -880,7 +901,7 @@ bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
|
|||
return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
|
||||
|
||||
case SHAPE_T::RECTANGLE:
|
||||
if( IsAnnotationProxy() || IsFilled() ) // Filled rect hit-test
|
||||
if( IsProxyItem() || IsFilled() ) // Filled rect hit-test
|
||||
{
|
||||
SHAPE_POLY_SET poly;
|
||||
poly.NewOutline();
|
||||
|
@ -1175,7 +1196,7 @@ std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineCh
|
|||
{
|
||||
std::vector<VECTOR2I> pts = GetRectCorners();
|
||||
|
||||
if( ( IsFilled() || IsAnnotationProxy() ) && !aEdgeOnly )
|
||||
if( ( IsFilled() || IsProxyItem() ) && !aEdgeOnly )
|
||||
effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
|
||||
|
||||
if( width > 0 || !IsFilled() || aEdgeOnly )
|
||||
|
@ -1598,7 +1619,7 @@ void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance
|
|||
{
|
||||
std::vector<VECTOR2I> pts = GetRectCorners();
|
||||
|
||||
if( IsFilled() || IsAnnotationProxy() )
|
||||
if( IsFilled() || IsProxyItem() )
|
||||
{
|
||||
aBuffer.NewOutline();
|
||||
|
||||
|
|
|
@ -85,8 +85,8 @@ public:
|
|||
|
||||
wxString SHAPE_T_asString() const;
|
||||
|
||||
virtual bool IsAnnotationProxy() const { return m_annotationProxy; }
|
||||
virtual void SetIsAnnotationProxy( bool aIsProxy = true ) { m_annotationProxy = aIsProxy; }
|
||||
virtual bool IsProxyItem() const { return m_proxyItem; }
|
||||
virtual void SetIsProxyItem( bool aIsProxy = true ) { m_proxyItem = aIsProxy; }
|
||||
|
||||
bool IsFilled() const
|
||||
{
|
||||
|
@ -399,7 +399,8 @@ protected:
|
|||
SHAPE_POLY_SET m_poly; // Stores the S_POLYGON shape
|
||||
|
||||
int m_editState;
|
||||
bool m_annotationProxy; // A shape storing the position of an annotation
|
||||
bool m_proxyItem; // A shape storing proxy information (ie: a pad
|
||||
// number box, thermal spoke template, etc.)
|
||||
};
|
||||
|
||||
#ifndef SWIG
|
||||
|
|
|
@ -566,7 +566,7 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
|
|||
{
|
||||
for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
|
||||
{
|
||||
if( !primitive->IsAnnotationProxy() )
|
||||
if( !primitive->IsProxyItem() )
|
||||
{
|
||||
for( SHAPE* shape : primitive->MakeEffectiveShapes() )
|
||||
{
|
||||
|
|
|
@ -124,7 +124,7 @@ void PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError,
|
|||
|
||||
for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
|
||||
{
|
||||
if( !primitive->IsAnnotationProxy() )
|
||||
if( !primitive->IsProxyItem() )
|
||||
primitive->TransformShapeToPolygon( polyset, UNDEFINED_LAYER, 0, aError, aErrorLoc );
|
||||
}
|
||||
|
||||
|
|
|
@ -1171,7 +1171,7 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
|||
{
|
||||
const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
|
||||
|
||||
if( shape->IsAnnotationProxy() )
|
||||
if( shape->IsProxyItem() && shape->GetShape() == SHAPE_T::RECTANGLE )
|
||||
{
|
||||
position = shape->GetCenter();
|
||||
padsize = shape->GetBotRight() - shape->GetTopLeft();
|
||||
|
@ -1190,7 +1190,7 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
|||
// See if we have a number box
|
||||
for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives() )
|
||||
{
|
||||
if( primitive->IsAnnotationProxy() )
|
||||
if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::RECTANGLE )
|
||||
{
|
||||
position = aPad->GetPosition() + primitive->GetCenter();
|
||||
padsize.x = abs( primitive->GetBotRight().x - primitive->GetTopLeft().x );
|
||||
|
@ -1734,7 +1734,14 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
|
|||
switch( aShape->GetShape() )
|
||||
{
|
||||
case SHAPE_T::SEGMENT:
|
||||
if( outline_mode )
|
||||
if( aShape->IsProxyItem() )
|
||||
{
|
||||
m_gal->SetIsFill( false );
|
||||
m_gal->SetIsStroke( true );
|
||||
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
|
||||
m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
|
||||
}
|
||||
else if( outline_mode )
|
||||
{
|
||||
m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
|
||||
}
|
||||
|
@ -1752,7 +1759,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
|
|||
{
|
||||
std::vector<VECTOR2I> pts = aShape->GetRectCorners();
|
||||
|
||||
if( aShape->IsAnnotationProxy() )
|
||||
if( aShape->IsProxyItem() )
|
||||
{
|
||||
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
|
||||
m_gal->DrawLine( pts[0], pts[1] );
|
||||
|
|
|
@ -354,14 +354,45 @@ void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
|
|||
}
|
||||
|
||||
|
||||
void PCB_SHAPE::SetIsAnnotationProxy( bool aIsProxy )
|
||||
void PCB_SHAPE::SetIsProxyItem( bool aIsProxy )
|
||||
{
|
||||
if( aIsProxy && !m_annotationProxy )
|
||||
SetStroke( STROKE_PARAMS( 1 ) );
|
||||
else if( m_annotationProxy && !aIsProxy )
|
||||
SetStroke( STROKE_PARAMS( pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ) ) );
|
||||
PAD* parentPad = nullptr;
|
||||
|
||||
m_annotationProxy = aIsProxy;
|
||||
if( GetBoard() && GetBoard()->IsFootprintHolder() )
|
||||
{
|
||||
for( FOOTPRINT* fp : GetBoard()->Footprints() )
|
||||
{
|
||||
for( PAD* pad : fp->Pads() )
|
||||
{
|
||||
if( pad->IsEntered() )
|
||||
{
|
||||
parentPad = pad;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( aIsProxy && !m_proxyItem )
|
||||
{
|
||||
if( GetShape() == SHAPE_T::SEGMENT )
|
||||
{
|
||||
if( parentPad && parentPad->GetThermalSpokeWidth() )
|
||||
SetWidth( parentPad->GetThermalSpokeWidth() );
|
||||
else
|
||||
SetWidth( pcbIUScale.mmToIU( ZONE_THERMAL_RELIEF_COPPER_WIDTH_MM ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWidth( 1 );
|
||||
}
|
||||
}
|
||||
else if( m_proxyItem && !aIsProxy )
|
||||
{
|
||||
SetWidth( pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ) );
|
||||
}
|
||||
|
||||
m_proxyItem = aIsProxy;
|
||||
}
|
||||
|
||||
|
||||
|
@ -591,21 +622,42 @@ static struct PCB_SHAPE_DESC
|
|||
_HKI( "Net" ), isCopper );
|
||||
|
||||
auto isPadEditMode =
|
||||
[]( INSPECTABLE* aItem ) -> bool
|
||||
[]( BOARD* aBoard ) -> bool
|
||||
{
|
||||
if( aBoard && aBoard->IsFootprintHolder() )
|
||||
{
|
||||
for( FOOTPRINT* fp : aBoard->Footprints() )
|
||||
{
|
||||
for( PAD* pad : fp->Pads() )
|
||||
{
|
||||
if( pad->IsEntered() )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
auto showNumberBoxProperty =
|
||||
[&]( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
|
||||
{
|
||||
if( shape->GetBoard() && shape->GetBoard()->IsFootprintHolder() )
|
||||
{
|
||||
for( FOOTPRINT* fp : shape->GetBoard()->Footprints() )
|
||||
{
|
||||
for( PAD* pad : fp->Pads() )
|
||||
{
|
||||
if( pad->IsEntered() )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( shape->GetShape() == SHAPE_T::RECTANGLE )
|
||||
return isPadEditMode( shape->GetBoard() );
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
auto showSpokeTemplateProperty =
|
||||
[&]( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
|
||||
{
|
||||
if( shape->GetShape() == SHAPE_T::SEGMENT )
|
||||
return isPadEditMode( shape->GetBoard() );
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -614,8 +666,15 @@ static struct PCB_SHAPE_DESC
|
|||
const wxString groupPadPrimitives = _HKI( "Pad Primitives" );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_SHAPE, bool>( _HKI( "Number Box" ),
|
||||
&PCB_SHAPE::SetIsAnnotationProxy, &PCB_SHAPE::IsAnnotationProxy ),
|
||||
&PCB_SHAPE::SetIsProxyItem,
|
||||
&PCB_SHAPE::IsProxyItem ),
|
||||
groupPadPrimitives )
|
||||
.SetAvailableFunc( isPadEditMode );
|
||||
.SetAvailableFunc( showNumberBoxProperty );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_SHAPE, bool>( _HKI( "Thermal Spoke Template" ),
|
||||
&PCB_SHAPE::SetIsProxyItem,
|
||||
&PCB_SHAPE::IsProxyItem ),
|
||||
groupPadPrimitives )
|
||||
.SetAvailableFunc( showSpokeTemplateProperty );
|
||||
}
|
||||
} _PCB_SHAPE_DESC;
|
||||
|
|
|
@ -102,8 +102,8 @@ public:
|
|||
std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
|
||||
FLASHING aFlash = FLASHING::DEFAULT ) const override;
|
||||
|
||||
bool IsAnnotationProxy() const override { return m_annotationProxy; }
|
||||
void SetIsAnnotationProxy( bool aIsProxy = true ) override;
|
||||
bool IsProxyItem() const override { return m_proxyItem; }
|
||||
void SetIsProxyItem( bool aIsProxy = true ) override;
|
||||
|
||||
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
|
||||
|
||||
|
|
|
@ -4585,7 +4585,7 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
|
|||
case T_gr_bbox:
|
||||
{
|
||||
PCB_SHAPE* numberBox = parsePCB_SHAPE( nullptr );
|
||||
numberBox->SetIsAnnotationProxy();
|
||||
numberBox->SetIsProxyItem();
|
||||
pad->AddPrimitive( numberBox );
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1704,7 +1704,7 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
|
|||
break;
|
||||
|
||||
case SHAPE_T::RECTANGLE:
|
||||
if( primitive->IsAnnotationProxy() )
|
||||
if( primitive->IsProxyItem() )
|
||||
{
|
||||
m_out->Print( nested_level, "(gr_bbox (start %s) (end %s)",
|
||||
formatInternalUnits( primitive->GetStart() ).c_str(),
|
||||
|
|
|
@ -766,48 +766,21 @@ PCB_LAYER_ID PAD_TOOL::explodePad( PAD* aPad )
|
|||
|
||||
for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives() )
|
||||
{
|
||||
PCB_SHAPE* shape = new PCB_SHAPE( board()->GetFirstFootprint() );
|
||||
|
||||
shape->SetShape( primitive->GetShape() );
|
||||
shape->SetIsAnnotationProxy( primitive->IsAnnotationProxy());
|
||||
shape->SetFilled( primitive->IsFilled() );
|
||||
shape->SetStroke( primitive->GetStroke() );
|
||||
|
||||
switch( shape->GetShape() )
|
||||
{
|
||||
case SHAPE_T::SEGMENT:
|
||||
case SHAPE_T::RECTANGLE:
|
||||
case SHAPE_T::CIRCLE:
|
||||
shape->SetStart( primitive->GetStart() );
|
||||
shape->SetEnd( primitive->GetEnd() );
|
||||
break;
|
||||
|
||||
case SHAPE_T::ARC:
|
||||
shape->SetStart( primitive->GetStart() );
|
||||
shape->SetEnd( primitive->GetEnd() );
|
||||
shape->SetCenter( primitive->GetCenter() );
|
||||
break;
|
||||
|
||||
case SHAPE_T::BEZIER:
|
||||
shape->SetStart( primitive->GetStart() );
|
||||
shape->SetEnd( primitive->GetEnd() );
|
||||
shape->SetBezierC1( primitive->GetBezierC1() );
|
||||
shape->SetBezierC2( primitive->GetBezierC2() );
|
||||
break;
|
||||
|
||||
case SHAPE_T::POLY:
|
||||
shape->SetPolyShape( primitive->GetPolyShape() );
|
||||
break;
|
||||
|
||||
default:
|
||||
UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
|
||||
}
|
||||
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( primitive->Duplicate() );
|
||||
|
||||
shape->SetParent( board()->GetFirstFootprint() );
|
||||
shape->Rotate( VECTOR2I( 0, 0 ), aPad->GetOrientation() );
|
||||
shape->Move( aPad->ShapePos() );
|
||||
|
||||
shape->SetLayer( layer );
|
||||
|
||||
if( shape->IsProxyItem() && shape->GetShape() == SHAPE_T::SEGMENT )
|
||||
{
|
||||
if( aPad->GetThermalSpokeWidth() )
|
||||
shape->SetWidth( aPad->GetThermalSpokeWidth() );
|
||||
else
|
||||
shape->SetWidth( pcbIUScale.mmToIU( ZONE_THERMAL_RELIEF_COPPER_WIDTH_MM ) );
|
||||
}
|
||||
|
||||
commit.Add( shape );
|
||||
}
|
||||
|
||||
|
@ -851,7 +824,7 @@ std::vector<PCB_SHAPE*> PAD_TOOL::RecombinePad( PAD* aPad, bool aIsDryRun, BOARD
|
|||
if( shape->GetLayer() != aLayer )
|
||||
continue;
|
||||
|
||||
if( shape->IsAnnotationProxy() ) // Pad number (and net name) box
|
||||
if( shape->IsProxyItem() ) // Pad number (and net name) box
|
||||
return shape;
|
||||
|
||||
SHAPE_POLY_SET drawPoly;
|
||||
|
@ -938,46 +911,12 @@ std::vector<PCB_SHAPE*> PAD_TOOL::RecombinePad( PAD* aPad, bool aIsDryRun, BOARD
|
|||
|
||||
if( !aIsDryRun )
|
||||
{
|
||||
PCB_SHAPE* primitive = new PCB_SHAPE;
|
||||
|
||||
primitive->SetShape( fpShape->GetShape() );
|
||||
primitive->SetFilled( fpShape->IsFilled() );
|
||||
primitive->SetStroke( fpShape->GetStroke() );
|
||||
|
||||
switch( primitive->GetShape() )
|
||||
{
|
||||
case SHAPE_T::SEGMENT:
|
||||
case SHAPE_T::RECTANGLE:
|
||||
case SHAPE_T::CIRCLE:
|
||||
primitive->SetStart( fpShape->GetStart() );
|
||||
primitive->SetEnd( fpShape->GetEnd() );
|
||||
break;
|
||||
|
||||
case SHAPE_T::ARC:
|
||||
primitive->SetStart( fpShape->GetStart() );
|
||||
primitive->SetEnd( fpShape->GetEnd() );
|
||||
primitive->SetCenter( fpShape->GetCenter() );
|
||||
break;
|
||||
|
||||
case SHAPE_T::BEZIER:
|
||||
primitive->SetStart( fpShape->GetStart() );
|
||||
primitive->SetEnd( fpShape->GetEnd() );
|
||||
primitive->SetBezierC1( fpShape->GetBezierC1() );
|
||||
primitive->SetBezierC2( fpShape->GetBezierC2() );
|
||||
break;
|
||||
|
||||
case SHAPE_T::POLY:
|
||||
primitive->SetPolyShape( fpShape->GetPolyShape() );
|
||||
break;
|
||||
|
||||
default:
|
||||
UNIMPLEMENTED_FOR( primitive->SHAPE_T_asString() );
|
||||
}
|
||||
PCB_SHAPE* primitive = static_cast<PCB_SHAPE*>( fpShape->Duplicate() );
|
||||
|
||||
primitive->SetParent( nullptr );
|
||||
primitive->Move( - aPad->ShapePos() );
|
||||
primitive->Rotate( VECTOR2I( 0, 0 ), - aPad->GetOrientation() );
|
||||
|
||||
primitive->SetIsAnnotationProxy( fpShape->IsAnnotationProxy() );
|
||||
aPad->AddPrimitive( primitive );
|
||||
|
||||
aCommit.Remove( fpShape );
|
||||
|
|
|
@ -1303,7 +1303,7 @@ void PCB_POINT_EDITOR::updateItem() const
|
|||
break;
|
||||
}
|
||||
|
||||
if( shape->IsAnnotationProxy() )
|
||||
if( shape->IsProxyItem() )
|
||||
{
|
||||
for( PAD* pad : shape->GetParentFootprint()->Pads() )
|
||||
{
|
||||
|
|
|
@ -1774,6 +1774,20 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer,
|
|||
if( !( itemBB.Intersects( zoneBB ) ) )
|
||||
continue;
|
||||
|
||||
bool customSpokes = false;
|
||||
|
||||
if( pad->GetShape() == PAD_SHAPE::CUSTOM )
|
||||
{
|
||||
for( const std::shared_ptr<PCB_SHAPE>& primitive : pad->GetPrimitives() )
|
||||
{
|
||||
if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
|
||||
{
|
||||
customSpokes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Thermal spokes consist of square-ended segments from the pad center to points just
|
||||
// outside the thermal relief. The outside end has an extra center point (which must be
|
||||
// at idx 3) which is used for testing whether or not the spoke connects to copper in the
|
||||
|
@ -1826,9 +1840,51 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer,
|
|||
}
|
||||
};
|
||||
|
||||
if( customSpokes )
|
||||
{
|
||||
SHAPE_POLY_SET thermalPoly;
|
||||
SHAPE_LINE_CHAIN thermalOutline;
|
||||
|
||||
pad->TransformShapeToPolygon( thermalPoly, aLayer, thermalReliefGap + epsilon,
|
||||
m_maxError, ERROR_OUTSIDE );
|
||||
|
||||
if( thermalPoly.OutlineCount() )
|
||||
thermalOutline = thermalPoly.Outline( 0 );
|
||||
|
||||
for( const std::shared_ptr<PCB_SHAPE>& primitive : pad->GetPrimitives() )
|
||||
{
|
||||
if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
|
||||
{
|
||||
SEG seg( primitive->GetStart(), primitive->GetEnd() );
|
||||
SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
|
||||
|
||||
// Make sure seg.A is the origin
|
||||
if( !pad->GetEffectivePolygon()->Contains( seg.A ) )
|
||||
seg.Reverse();
|
||||
|
||||
// Trim seg.B to the thermal outline
|
||||
if( thermalOutline.Intersect( seg, intersections ) )
|
||||
{
|
||||
seg.B = intersections.front().p;
|
||||
|
||||
VECTOR2I offset = ( seg.B - seg.A ).Perpendicular().Resize( spoke_half_w );
|
||||
SHAPE_LINE_CHAIN spoke;
|
||||
|
||||
spoke.Append( seg.A + offset );
|
||||
spoke.Append( seg.A - offset );
|
||||
spoke.Append( seg.B - offset );
|
||||
spoke.Append( seg.B ); // test pt
|
||||
spoke.Append( seg.B + offset );
|
||||
|
||||
spoke.SetClosed( true );
|
||||
aSpokesList.push_back( std::move( spoke ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the spokes are at a cardinal angle then we can generate them from a bounding box
|
||||
// without trig.
|
||||
if( ( pad->GetOrientation() + pad->GetThermalSpokeAngle() ).IsCardinal() )
|
||||
else if( ( pad->GetOrientation() + pad->GetThermalSpokeAngle() ).IsCardinal() )
|
||||
{
|
||||
BOX2I spokesBox = pad->GetBoundingBox();
|
||||
spokesBox.Inflate( thermalReliefGap + epsilon );
|
||||
|
|
Loading…
Reference in New Issue