Move board object polygon code into the object source files.
Having the board object polygon code all defined in a separate file made finding the polygon code for an object difficult to find.
This commit is contained in:
parent
a79a221257
commit
1f7fd436a2
|
@ -511,7 +511,6 @@ set( PCB_COMMON_SRCS
|
|||
${CMAKE_SOURCE_DIR}/pcbnew/board_commit.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/board_connected_item.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/board_design_settings.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/board_items_to_polygon_shape_transform.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/board.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/board_item.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/pcb_dimension.cpp
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
||||
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
|
||||
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
|
||||
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <bezier_curves.h>
|
||||
#include <base_units.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <eda_draw_frame.h>
|
||||
#include <geometry/shape_simple.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include <eda_shape.h>
|
||||
#include <plotters/plotter.h>
|
||||
|
||||
|
||||
EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
|
||||
m_endsSwapped( false ),
|
||||
m_shape( aType ),
|
||||
|
@ -1002,10 +1004,21 @@ void EDA_SHAPE::computeArcBBox( EDA_RECT& aBBox ) const
|
|||
{
|
||||
switch( quarter )
|
||||
{
|
||||
case 0: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y + radius ) ); break; // down
|
||||
case 1: aBBox.Merge( wxPoint( m_arcCenter.x - radius, m_arcCenter.y ) ); break; // left
|
||||
case 2: aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y - radius ) ); break; // up
|
||||
case 3: aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y ) ); break; // right
|
||||
case 0:
|
||||
aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y + radius ) );
|
||||
break; // down
|
||||
|
||||
case 1:
|
||||
aBBox.Merge( wxPoint( m_arcCenter.x - radius, m_arcCenter.y ) );
|
||||
break; // left
|
||||
|
||||
case 2:
|
||||
aBBox.Merge( wxPoint( m_arcCenter.x, m_arcCenter.y - radius ) );
|
||||
break; // up
|
||||
|
||||
case 3:
|
||||
aBBox.Merge( wxPoint( m_arcCenter.x + radius, m_arcCenter.y ) );
|
||||
break; // right
|
||||
}
|
||||
|
||||
++quarter %= 4;
|
||||
|
@ -1429,6 +1442,130 @@ int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
|
|||
}
|
||||
|
||||
|
||||
void EDA_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
int width = ignoreLineWidth ? 0 : m_width;
|
||||
|
||||
width += 2 * aClearanceValue;
|
||||
|
||||
switch( m_shape )
|
||||
{
|
||||
case SHAPE_T::CIRCLE:
|
||||
if( IsFilled() )
|
||||
{
|
||||
TransformCircleToPolygon( aCornerBuffer, getCenter(), GetRadius() + width / 2, aError,
|
||||
aErrorLoc );
|
||||
}
|
||||
else
|
||||
{
|
||||
TransformRingToPolygon( aCornerBuffer, getCenter(), GetRadius(), width, aError,
|
||||
aErrorLoc );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SHAPE_T::RECT:
|
||||
{
|
||||
std::vector<wxPoint> pts = GetRectCorners();
|
||||
|
||||
if( IsFilled() )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( const wxPoint& pt : pts )
|
||||
aCornerBuffer.Append( pt );
|
||||
}
|
||||
|
||||
if( width > 0 || !IsFilled() )
|
||||
{
|
||||
// Add in segments
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[0], pts[1], width, aError, aErrorLoc );
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[1], pts[2], width, aError, aErrorLoc );
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[2], pts[3], width, aError, aErrorLoc );
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[3], pts[0], width, aError, aErrorLoc );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SHAPE_T::ARC:
|
||||
TransformArcToPolygon( aCornerBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError,
|
||||
aErrorLoc );
|
||||
break;
|
||||
|
||||
case SHAPE_T::SEGMENT:
|
||||
TransformOvalToPolygon( aCornerBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
|
||||
break;
|
||||
|
||||
case SHAPE_T::POLY:
|
||||
{
|
||||
if( !IsPolyShapeValid() )
|
||||
break;
|
||||
|
||||
// The polygon is expected to be a simple polygon; not self intersecting, no hole.
|
||||
double orientation = getParentOrientation();
|
||||
wxPoint offset = getParentPosition();
|
||||
|
||||
// Build the polygon with the actual position and orientation:
|
||||
std::vector<wxPoint> poly;
|
||||
DupPolyPointsList( poly );
|
||||
|
||||
for( wxPoint& point : poly )
|
||||
{
|
||||
RotatePoint( &point, orientation );
|
||||
point += offset;
|
||||
}
|
||||
|
||||
if( IsFilled() )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( const wxPoint& point : poly )
|
||||
aCornerBuffer.Append( point.x, point.y );
|
||||
}
|
||||
|
||||
if( width > 0 || !IsFilled() )
|
||||
{
|
||||
wxPoint pt1( poly[ poly.size() - 1] );
|
||||
|
||||
for( const wxPoint& pt2 : poly )
|
||||
{
|
||||
if( pt2 != pt1 )
|
||||
TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError, aErrorLoc );
|
||||
|
||||
pt1 = pt2;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SHAPE_T::BEZIER:
|
||||
{
|
||||
std::vector<wxPoint> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
|
||||
BEZIER_POLY converter( ctrlPts );
|
||||
std::vector< wxPoint> poly;
|
||||
converter.GetPoly( poly, m_width );
|
||||
|
||||
for( unsigned ii = 1; ii < poly.size(); ii++ )
|
||||
{
|
||||
TransformOvalToPolygon( aCornerBuffer, poly[ii - 1], poly[ii], width, aError,
|
||||
aErrorLoc );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct EDA_SHAPE_DESC
|
||||
{
|
||||
EDA_SHAPE_DESC()
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <base_units.h>
|
||||
#include <basic_gal.h> // for BASIC_GAL, basic_gal
|
||||
#include <convert_to_biu.h> // for Mils2iu
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <eda_rect.h> // for EDA_RECT
|
||||
#include <eda_text.h> // for EDA_TEXT, TEXT_EFFECTS, GR_TEXT_VJUSTIF...
|
||||
#include <gal/color4d.h> // for COLOR4D, COLOR4D::BLACK
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include <i18n_utility.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
#include <geometry/shape_compound.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
|
||||
#include <wx/debug.h> // for wxASSERT
|
||||
|
@ -60,6 +62,14 @@ class OUTPUTFORMATTER;
|
|||
class wxFindReplaceData;
|
||||
|
||||
|
||||
void addTextSegmToPoly( int x0, int y0, int xf, int yf, void* aData )
|
||||
{
|
||||
TSEGM_2_POLY_PRMS* prm = static_cast<TSEGM_2_POLY_PRMS*>( aData );
|
||||
TransformOvalToPolygon( *prm->m_cornerBuffer, wxPoint( x0, y0 ), wxPoint( xf, yf ),
|
||||
prm->m_textWidth, prm->m_error, ERROR_INSIDE );
|
||||
}
|
||||
|
||||
|
||||
EDA_TEXT_HJUSTIFY_T EDA_TEXT::MapHorizJustify( int aHorizJustify )
|
||||
{
|
||||
wxASSERT( aHorizJustify >= GR_TEXT_HJUSTIFY_LEFT && aHorizJustify <= GR_TEXT_HJUSTIFY_RIGHT );
|
||||
|
@ -668,6 +678,52 @@ int EDA_TEXT::Compare( const EDA_TEXT* aOther ) const
|
|||
}
|
||||
|
||||
|
||||
void EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( SHAPE_POLY_SET* aCornerBuffer,
|
||||
int aClearanceValue ) const
|
||||
{
|
||||
if( GetText().Length() == 0 )
|
||||
return;
|
||||
|
||||
wxPoint corners[4]; // Buffer of polygon corners
|
||||
|
||||
EDA_RECT rect = GetTextBox();
|
||||
|
||||
// This ugly hack is because this code used to be defined in the board polygon code
|
||||
// file rather than in the EDA_TEXT source file where it belonged. Using the board
|
||||
// default text width was dubious so this recreates the same code with the exception
|
||||
// if for some reason a different default text width is require for some other object.
|
||||
#if !defined( DEFAULT_TEXT_WIDTH )
|
||||
#define LOCAL_DEFAULT_TEXT_WIDTH
|
||||
#define DEFAULT_TEXT_WIDTH 0.15
|
||||
#endif
|
||||
|
||||
rect.Inflate( aClearanceValue + Millimeter2iu( DEFAULT_TEXT_WIDTH ) );
|
||||
|
||||
#if defined( LOCAL_DEFAULT_TEXT_WIDTH )
|
||||
#undef DEFAULT_TEXT_WIDTH
|
||||
#undef LOCAL_DEFAULT_TEXT_WIDTH
|
||||
#endif
|
||||
|
||||
corners[0].x = rect.GetOrigin().x;
|
||||
corners[0].y = rect.GetOrigin().y;
|
||||
corners[1].y = corners[0].y;
|
||||
corners[1].x = rect.GetRight();
|
||||
corners[2].x = corners[1].x;
|
||||
corners[2].y = rect.GetBottom();
|
||||
corners[3].y = corners[2].y;
|
||||
corners[3].x = corners[0].x;
|
||||
|
||||
aCornerBuffer->NewOutline();
|
||||
|
||||
for( wxPoint& corner : corners )
|
||||
{
|
||||
// Rotate polygon
|
||||
RotatePoint( &corner.x, &corner.y, GetTextPos().x, GetTextPos().y, GetTextAngle() );
|
||||
aCornerBuffer->Append( corner.x, corner.y );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct EDA_TEXT_DESC
|
||||
{
|
||||
EDA_TEXT_DESC()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013 Jean-Pierre Charras, jpe.charras at wanadoo.fr
|
||||
* Copyright (C) 2004-2021 KiCad Developers, see change_log.txt for contributors.
|
||||
* Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 2004-2021 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
|
||||
|
@ -36,6 +36,27 @@ class SHAPE_COMPOUND;
|
|||
class SHAPE_POLY_SET;
|
||||
class wxFindReplaceData;
|
||||
|
||||
|
||||
/**
|
||||
* A helper for the text to polygon callback function.
|
||||
*
|
||||
* These variables are parameters used in #addTextSegmToPoly but #addTextSegmToPoly is a
|
||||
* callback function so the cannot be sent as arguments.
|
||||
*/
|
||||
struct TSEGM_2_POLY_PRMS
|
||||
{
|
||||
int m_textWidth;
|
||||
int m_error;
|
||||
SHAPE_POLY_SET* m_cornerBuffer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback function used to convert text segments to polygons.
|
||||
*/
|
||||
extern void addTextSegmToPoly( int x0, int y0, int xf, int yf, void* aData );
|
||||
|
||||
|
||||
namespace KIGFX
|
||||
{
|
||||
class RENDER_SETTINGS;
|
||||
|
|
|
@ -2171,3 +2171,75 @@ bool BOARD::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
|
|||
}
|
||||
|
||||
|
||||
void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aOutlines ) const
|
||||
{
|
||||
int maxError = GetDesignSettings().m_MaxError;
|
||||
|
||||
// convert tracks and vias:
|
||||
for( const PCB_TRACK* track : m_tracks )
|
||||
{
|
||||
if( !track->IsOnLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
track->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
|
||||
// convert pads and other copper items in footprints
|
||||
for( const FOOTPRINT* footprint : m_footprints )
|
||||
{
|
||||
footprint->TransformPadsWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
|
||||
// Micro-wave footprints may have items on copper layers
|
||||
footprint->TransformFPShapesWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE,
|
||||
true, /* include text */
|
||||
true /* include shapes */ );
|
||||
|
||||
for( const ZONE* zone : footprint->Zones() )
|
||||
{
|
||||
if( zone->GetLayerSet().test( aLayer ) )
|
||||
zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
|
||||
}
|
||||
}
|
||||
|
||||
// convert copper zones
|
||||
for( const ZONE* zone : Zones() )
|
||||
{
|
||||
if( zone->GetLayerSet().test( aLayer ) )
|
||||
zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
|
||||
}
|
||||
|
||||
// convert graphic items on copper layers (texts)
|
||||
for( const BOARD_ITEM* item : m_drawings )
|
||||
{
|
||||
if( !item->IsOnLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_SHAPE_T:
|
||||
{
|
||||
const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
|
||||
shape->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_TEXT_T:
|
||||
{
|
||||
const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
|
||||
text->TransformTextShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,761 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2009-2021 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 1992-2021 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
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <macros.h>
|
||||
#include <bezier_curves.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <trigo.h>
|
||||
#include <board.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_dimension.h>
|
||||
#include <pcb_track.h>
|
||||
#include <pcb_shape.h>
|
||||
#include <pcb_text.h>
|
||||
#include <zone.h>
|
||||
#include <footprint.h>
|
||||
#include <fp_shape.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <geometry/geometry_utils.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
#include <geometry/shape_circle.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
|
||||
// A helper struct for the callback function
|
||||
// These variables are parameters used in addTextSegmToPoly.
|
||||
// But addTextSegmToPoly is a call-back function,
|
||||
// so we cannot send them as arguments.
|
||||
struct TSEGM_2_POLY_PRMS
|
||||
{
|
||||
int m_textWidth;
|
||||
int m_error;
|
||||
SHAPE_POLY_SET* m_cornerBuffer;
|
||||
};
|
||||
|
||||
|
||||
TSEGM_2_POLY_PRMS prms;
|
||||
|
||||
|
||||
// This is a call back function, used by GRText to draw the 3D text shape:
|
||||
static void addTextSegmToPoly( int x0, int y0, int xf, int yf, void* aData )
|
||||
{
|
||||
TSEGM_2_POLY_PRMS* prm = static_cast<TSEGM_2_POLY_PRMS*>( aData );
|
||||
TransformOvalToPolygon( *prm->m_cornerBuffer, wxPoint( x0, y0 ), wxPoint( xf, yf ),
|
||||
prm->m_textWidth, prm->m_error, ERROR_INSIDE );
|
||||
}
|
||||
|
||||
|
||||
void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aOutlines ) const
|
||||
{
|
||||
int maxError = GetDesignSettings().m_MaxError;
|
||||
|
||||
// convert tracks and vias:
|
||||
for( const PCB_TRACK* track : m_tracks )
|
||||
{
|
||||
if( !track->IsOnLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
track->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
|
||||
// convert pads and other copper items in footprints
|
||||
for( const FOOTPRINT* footprint : m_footprints )
|
||||
{
|
||||
footprint->TransformPadsWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
|
||||
// Micro-wave footprints may have items on copper layers
|
||||
footprint->TransformFPShapesWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE,
|
||||
true, /* include text */
|
||||
true /* include shapes */ );
|
||||
|
||||
for( const ZONE* zone : footprint->Zones() )
|
||||
{
|
||||
if( zone->GetLayerSet().test( aLayer ) )
|
||||
zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
|
||||
}
|
||||
}
|
||||
|
||||
// convert copper zones
|
||||
for( const ZONE* zone : Zones() )
|
||||
{
|
||||
if( zone->GetLayerSet().test( aLayer ) )
|
||||
zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
|
||||
}
|
||||
|
||||
// convert graphic items on copper layers (texts)
|
||||
for( const BOARD_ITEM* item : m_drawings )
|
||||
{
|
||||
if( !item->IsOnLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_SHAPE_T:
|
||||
{
|
||||
const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
|
||||
shape->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_TEXT_T:
|
||||
{
|
||||
const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
|
||||
text->TransformTextShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FOOTPRINT::TransformPadsWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aMaxError, ERROR_LOC aErrorLoc,
|
||||
bool aSkipNPTHPadsWihNoCopper,
|
||||
bool aSkipPlatedPads,
|
||||
bool aSkipNonPlatedPads ) const
|
||||
{
|
||||
for( const PAD* pad : m_pads )
|
||||
{
|
||||
if( aLayer != UNDEFINED_LAYER && !pad->IsOnLayer(aLayer) )
|
||||
continue;
|
||||
|
||||
if( !pad->FlashLayer( aLayer ) && IsCopperLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
// NPTH pads are not drawn on layers if the shape size and pos is the same
|
||||
// as their hole:
|
||||
if( aSkipNPTHPadsWihNoCopper && pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
||||
{
|
||||
if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
|
||||
{
|
||||
switch( pad->GetShape() )
|
||||
{
|
||||
case PAD_SHAPE::CIRCLE:
|
||||
if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
case PAD_SHAPE::OVAL:
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool isPlated = ( ( aLayer == F_Cu ) && pad->FlashLayer( F_Mask ) ) ||
|
||||
( ( aLayer == B_Cu ) && pad->FlashLayer( B_Mask ) );
|
||||
|
||||
if( aSkipPlatedPads && isPlated )
|
||||
continue;
|
||||
|
||||
if( aSkipNonPlatedPads && !isPlated )
|
||||
continue;
|
||||
|
||||
wxSize clearance( aClearance, aClearance );
|
||||
|
||||
switch( aLayer )
|
||||
{
|
||||
case F_Mask:
|
||||
case B_Mask:
|
||||
clearance.x += pad->GetSolderMaskMargin();
|
||||
clearance.y += pad->GetSolderMaskMargin();
|
||||
break;
|
||||
|
||||
case F_Paste:
|
||||
case B_Paste:
|
||||
clearance += pad->GetSolderPasteMargin();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Our standard TransformShapeWithClearanceToPolygon() routines can't handle differing
|
||||
// x:y clearance values (which get generated when a relative paste margin is used with
|
||||
// an oblong pad). So we apply this huge hack and fake a larger pad to run the transform
|
||||
// on.
|
||||
// Of course being a hack it falls down when dealing with custom shape pads (where the
|
||||
// size is only the size of the anchor), so for those we punt and just use clearance.x.
|
||||
|
||||
if( ( clearance.x < 0 || clearance.x != clearance.y )
|
||||
&& pad->GetShape() != PAD_SHAPE::CUSTOM )
|
||||
{
|
||||
PAD dummy( *pad );
|
||||
dummy.SetSize( pad->GetSize() + clearance + clearance );
|
||||
dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
||||
aMaxError, aErrorLoc );
|
||||
}
|
||||
else
|
||||
{
|
||||
pad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, clearance.x,
|
||||
aMaxError, aErrorLoc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate shapes of graphic items (outlines) as polygons added to a buffer.
|
||||
* @aCornerBuffer = the buffer to store polygons
|
||||
* @aInflateValue = a value to inflate shapes
|
||||
* @aError = the maximum error to allow when approximating curves with segments
|
||||
* @aIncludeText = indicates footprint text items (reference, value, etc.) should be included
|
||||
* in the outline
|
||||
*/
|
||||
void FOOTPRINT::TransformFPShapesWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIncludeText,
|
||||
bool aIncludeShapes ) const
|
||||
{
|
||||
std::vector<FP_TEXT*> texts; // List of FP_TEXT to convert
|
||||
|
||||
for( BOARD_ITEM* item : GraphicalItems() )
|
||||
{
|
||||
if( item->Type() == PCB_FP_TEXT_T && aIncludeText )
|
||||
{
|
||||
FP_TEXT* text = static_cast<FP_TEXT*>( item );
|
||||
|
||||
if( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer && text->IsVisible() )
|
||||
texts.push_back( text );
|
||||
}
|
||||
|
||||
if( item->Type() == PCB_FP_SHAPE_T && aIncludeShapes )
|
||||
{
|
||||
const FP_SHAPE* outline = static_cast<FP_SHAPE*>( item );
|
||||
|
||||
if( aLayer != UNDEFINED_LAYER && outline->GetLayer() == aLayer )
|
||||
{
|
||||
outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( aIncludeText )
|
||||
{
|
||||
if( Reference().GetLayer() == aLayer && Reference().IsVisible() )
|
||||
texts.push_back( &Reference() );
|
||||
|
||||
if( Value().GetLayer() == aLayer && Value().IsVisible() )
|
||||
texts.push_back( &Value() );
|
||||
}
|
||||
|
||||
for( const FP_TEXT* text : texts )
|
||||
{
|
||||
text->TransformTextShapeWithClearanceToPolygon( aCornerBuffer, aLayer, aClearance,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FP_TEXT::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc ) const
|
||||
{
|
||||
prms.m_cornerBuffer = &aCornerBuffer;
|
||||
prms.m_textWidth = GetEffectiveTextPenWidth() + ( 2 * aClearance );
|
||||
prms.m_error = aError;
|
||||
wxSize size = GetTextSize();
|
||||
int penWidth = GetEffectiveTextPenWidth();
|
||||
|
||||
if( IsMirrored() )
|
||||
size.x = -size.x;
|
||||
|
||||
GRText( nullptr, GetTextPos(), BLACK, GetShownText(), GetDrawRotation(), size,
|
||||
GetHorizJustify(), GetVertJustify(), penWidth, IsItalic(), IsBold(),
|
||||
addTextSegmToPoly, &prms );
|
||||
}
|
||||
|
||||
|
||||
void FP_TEXT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIgnoreLineWidth ) const
|
||||
{
|
||||
SHAPE_POLY_SET buffer;
|
||||
EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( &buffer, aClearance );
|
||||
|
||||
const FOOTPRINT* parentFootprint = static_cast<const FOOTPRINT*>( m_parent );
|
||||
|
||||
if( parentFootprint )
|
||||
buffer.Rotate( DECIDEG2RAD( GetDrawRotation() ), GetTextPos() );
|
||||
|
||||
aCornerBuffer.Append( buffer );
|
||||
}
|
||||
|
||||
|
||||
void ZONE::TransformSolidAreasShapesToPolygon( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aError ) const
|
||||
{
|
||||
if( !m_FilledPolysList.count( aLayer ) || m_FilledPolysList.at( aLayer ).IsEmpty() )
|
||||
return;
|
||||
|
||||
// Just add filled areas if filled polygons outlines have no thickness
|
||||
if( !GetFilledPolysUseThickness() || GetMinThickness() == 0 )
|
||||
{
|
||||
const SHAPE_POLY_SET& polys = m_FilledPolysList.at( aLayer );
|
||||
aCornerBuffer.Append( polys );
|
||||
return;
|
||||
}
|
||||
|
||||
// Filled areas have polygons with outline thickness.
|
||||
// we must create the polygons and add inflated polys
|
||||
SHAPE_POLY_SET polys = m_FilledPolysList.at( aLayer );
|
||||
|
||||
auto board = GetBoard();
|
||||
int maxError = ARC_HIGH_DEF;
|
||||
|
||||
if( board )
|
||||
maxError = board->GetDesignSettings().m_MaxError;
|
||||
|
||||
int numSegs = GetArcToSegmentCount( GetMinThickness(), maxError, 360.0 );
|
||||
|
||||
polys.InflateWithLinkedHoles( GetMinThickness()/2, numSegs, SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
aCornerBuffer.Append( polys );
|
||||
}
|
||||
|
||||
|
||||
void EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( SHAPE_POLY_SET* aCornerBuffer,
|
||||
int aClearanceValue ) const
|
||||
{
|
||||
if( GetText().Length() == 0 )
|
||||
return;
|
||||
|
||||
wxPoint corners[4]; // Buffer of polygon corners
|
||||
|
||||
EDA_RECT rect = GetTextBox();
|
||||
rect.Inflate( aClearanceValue + Millimeter2iu( DEFAULT_TEXT_WIDTH ) );
|
||||
corners[0].x = rect.GetOrigin().x;
|
||||
corners[0].y = rect.GetOrigin().y;
|
||||
corners[1].y = corners[0].y;
|
||||
corners[1].x = rect.GetRight();
|
||||
corners[2].x = corners[1].x;
|
||||
corners[2].y = rect.GetBottom();
|
||||
corners[3].y = corners[2].y;
|
||||
corners[3].x = corners[0].x;
|
||||
|
||||
aCornerBuffer->NewOutline();
|
||||
|
||||
for( wxPoint& corner : corners )
|
||||
{
|
||||
// Rotate polygon
|
||||
RotatePoint( &corner.x, &corner.y, GetTextPos().x, GetTextPos().y, GetTextAngle() );
|
||||
aCornerBuffer->Append( corner.x, corner.y );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCB_TEXT::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc ) const
|
||||
{
|
||||
wxSize size = GetTextSize();
|
||||
|
||||
if( IsMirrored() )
|
||||
size.x = -size.x;
|
||||
|
||||
int penWidth = GetEffectiveTextPenWidth();
|
||||
|
||||
prms.m_cornerBuffer = &aCornerBuffer;
|
||||
prms.m_textWidth = GetEffectiveTextPenWidth() + ( 2 * aClearanceValue );
|
||||
prms.m_error = aError;
|
||||
COLOR4D color; // not actually used, but needed by GRText
|
||||
|
||||
GRText( nullptr, GetTextPos(), color, GetShownText(), GetTextAngle(), size, GetHorizJustify(),
|
||||
GetVertJustify(), penWidth, IsItalic(), IsBold(), addTextSegmToPoly, &prms );
|
||||
}
|
||||
|
||||
|
||||
void PCB_TEXT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIgnoreLineWidth ) const
|
||||
{
|
||||
EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( &aCornerBuffer, aClearance );
|
||||
}
|
||||
|
||||
|
||||
void EDA_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
int width = ignoreLineWidth ? 0 : m_width;
|
||||
|
||||
width += 2 * aClearanceValue;
|
||||
|
||||
switch( m_shape )
|
||||
{
|
||||
case SHAPE_T::CIRCLE:
|
||||
if( IsFilled() )
|
||||
{
|
||||
TransformCircleToPolygon( aCornerBuffer, getCenter(), GetRadius() + width / 2, aError,
|
||||
aErrorLoc );
|
||||
}
|
||||
else
|
||||
{
|
||||
TransformRingToPolygon( aCornerBuffer, getCenter(), GetRadius(), width, aError,
|
||||
aErrorLoc );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SHAPE_T::RECT:
|
||||
{
|
||||
std::vector<wxPoint> pts = GetRectCorners();
|
||||
|
||||
if( IsFilled() )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( const wxPoint& pt : pts )
|
||||
aCornerBuffer.Append( pt );
|
||||
}
|
||||
|
||||
if( width > 0 || !IsFilled() )
|
||||
{
|
||||
// Add in segments
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[0], pts[1], width, aError, aErrorLoc );
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[1], pts[2], width, aError, aErrorLoc );
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[2], pts[3], width, aError, aErrorLoc );
|
||||
TransformOvalToPolygon( aCornerBuffer, pts[3], pts[0], width, aError, aErrorLoc );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SHAPE_T::ARC:
|
||||
TransformArcToPolygon( aCornerBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError,
|
||||
aErrorLoc );
|
||||
break;
|
||||
|
||||
case SHAPE_T::SEGMENT:
|
||||
TransformOvalToPolygon( aCornerBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
|
||||
break;
|
||||
|
||||
case SHAPE_T::POLY:
|
||||
{
|
||||
if( !IsPolyShapeValid() )
|
||||
break;
|
||||
|
||||
// The polygon is expected to be a simple polygon; not self intersecting, no hole.
|
||||
double orientation = getParentOrientation();
|
||||
wxPoint offset = getParentPosition();
|
||||
|
||||
// Build the polygon with the actual position and orientation:
|
||||
std::vector<wxPoint> poly;
|
||||
DupPolyPointsList( poly );
|
||||
|
||||
for( wxPoint& point : poly )
|
||||
{
|
||||
RotatePoint( &point, orientation );
|
||||
point += offset;
|
||||
}
|
||||
|
||||
if( IsFilled() )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( const wxPoint& point : poly )
|
||||
aCornerBuffer.Append( point.x, point.y );
|
||||
}
|
||||
|
||||
if( width > 0 || !IsFilled() )
|
||||
{
|
||||
wxPoint pt1( poly[ poly.size() - 1] );
|
||||
|
||||
for( const wxPoint& pt2 : poly )
|
||||
{
|
||||
if( pt2 != pt1 )
|
||||
TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError, aErrorLoc );
|
||||
|
||||
pt1 = pt2;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SHAPE_T::BEZIER:
|
||||
{
|
||||
std::vector<wxPoint> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
|
||||
BEZIER_POLY converter( ctrlPts );
|
||||
std::vector< wxPoint> poly;
|
||||
converter.GetPoly( poly, m_width );
|
||||
|
||||
for( unsigned ii = 1; ii < poly.size(); ii++ )
|
||||
{
|
||||
TransformOvalToPolygon( aCornerBuffer, poly[ii - 1], poly[ii], width, aError,
|
||||
aErrorLoc );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
EDA_SHAPE::TransformShapeWithClearanceToPolygon( aCornerBuffer, aClearanceValue, aError,
|
||||
aErrorLoc, ignoreLineWidth );
|
||||
}
|
||||
|
||||
|
||||
void PCB_TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for tracks." );
|
||||
|
||||
|
||||
switch( Type() )
|
||||
{
|
||||
case PCB_VIA_T:
|
||||
{
|
||||
int radius = ( m_Width / 2 ) + aClearanceValue;
|
||||
TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aError, aErrorLoc );
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_ARC_T:
|
||||
{
|
||||
const PCB_ARC* arc = static_cast<const PCB_ARC*>( this );
|
||||
int width = m_Width + ( 2 * aClearanceValue );
|
||||
|
||||
TransformArcToPolygon( aCornerBuffer, arc->GetStart(), arc->GetMid(),
|
||||
arc->GetEnd(), width, aError, aErrorLoc );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
int width = m_Width + ( 2 * aClearanceValue );
|
||||
|
||||
TransformOvalToPolygon( aCornerBuffer, m_Start, m_End, width, aError, aErrorLoc );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for pads." );
|
||||
|
||||
// minimal segment count to approximate a circle to create the polygonal pad shape
|
||||
// This minimal value is mainly for very small pads, like SM0402.
|
||||
// Most of time pads are using the segment count given by aError value.
|
||||
const int pad_min_seg_per_circle_count = 16;
|
||||
double angle = m_orient;
|
||||
int dx = m_size.x / 2;
|
||||
int dy = m_size.y / 2;
|
||||
|
||||
wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset,
|
||||
// the pad position is NOT the shape position
|
||||
|
||||
switch( GetShape() )
|
||||
{
|
||||
case PAD_SHAPE::CIRCLE:
|
||||
case PAD_SHAPE::OVAL:
|
||||
// Note: dx == dy is not guaranteed for circle pads in legacy boards
|
||||
if( dx == dy || ( GetShape() == PAD_SHAPE::CIRCLE ) )
|
||||
{
|
||||
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx + aClearanceValue, aError,
|
||||
aErrorLoc, pad_min_seg_per_circle_count );
|
||||
}
|
||||
else
|
||||
{
|
||||
int half_width = std::min( dx, dy );
|
||||
wxPoint delta( dx - half_width, dy - half_width );
|
||||
|
||||
RotatePoint( &delta, angle );
|
||||
|
||||
TransformOvalToPolygon( aCornerBuffer, padShapePos - delta, padShapePos + delta,
|
||||
( half_width + aClearanceValue ) * 2, aError, aErrorLoc,
|
||||
pad_min_seg_per_circle_count );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PAD_SHAPE::TRAPEZOID:
|
||||
case PAD_SHAPE::RECT:
|
||||
{
|
||||
int ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.x / 2 : 0;
|
||||
int ddy = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.y / 2 : 0;
|
||||
|
||||
SHAPE_POLY_SET outline;
|
||||
TransformTrapezoidToPolygon( outline, padShapePos, m_size, angle, ddx, ddy,
|
||||
aClearanceValue, aError, aErrorLoc );
|
||||
aCornerBuffer.Append( outline );
|
||||
break;
|
||||
}
|
||||
|
||||
case PAD_SHAPE::CHAMFERED_RECT:
|
||||
case PAD_SHAPE::ROUNDRECT:
|
||||
{
|
||||
bool doChamfer = GetShape() == PAD_SHAPE::CHAMFERED_RECT;
|
||||
|
||||
SHAPE_POLY_SET outline;
|
||||
TransformRoundChamferedRectToPolygon( outline, padShapePos, m_size, angle,
|
||||
GetRoundRectCornerRadius(),
|
||||
doChamfer ? GetChamferRectRatio() : 0,
|
||||
doChamfer ? GetChamferPositions() : 0,
|
||||
aClearanceValue, aError, aErrorLoc );
|
||||
aCornerBuffer.Append( outline );
|
||||
break;
|
||||
}
|
||||
|
||||
case PAD_SHAPE::CUSTOM:
|
||||
{
|
||||
SHAPE_POLY_SET outline;
|
||||
MergePrimitivesAsPolygon( &outline, aErrorLoc );
|
||||
outline.Rotate( -DECIDEG2RAD( m_orient ) );
|
||||
outline.Move( VECTOR2I( m_pos ) );
|
||||
|
||||
if( aClearanceValue )
|
||||
{
|
||||
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
|
||||
pad_min_seg_per_circle_count );
|
||||
int clearance = aClearanceValue;
|
||||
|
||||
if( aErrorLoc == ERROR_OUTSIDE )
|
||||
{
|
||||
int actual_error = CircleToEndSegmentDeltaRadius( clearance, numSegs );
|
||||
clearance += GetCircleToPolyCorrection( actual_error );
|
||||
}
|
||||
|
||||
outline.Inflate( clearance, numSegs );
|
||||
outline.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
outline.Fracture( SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
|
||||
aCornerBuffer.Append( outline );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( "PAD::TransformShapeWithClearanceToPolygon no implementation for "
|
||||
+ PAD_SHAPE_T_asString( GetShape() ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
|
||||
int aError, ERROR_LOC aErrorLoc ) const
|
||||
{
|
||||
wxSize drillsize = GetDrillSize();
|
||||
|
||||
if( !drillsize.x || !drillsize.y )
|
||||
return false;
|
||||
|
||||
const SHAPE_SEGMENT* seg = GetEffectiveHoleShape();
|
||||
|
||||
TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
|
||||
seg->GetWidth() + aInflateValue * 2, aError, aErrorLoc );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ZONE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance, int aError,
|
||||
ERROR_LOC aErrorLoc, bool aIgnoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !aIgnoreLineWidth, "IgnoreLineWidth has no meaning for zones." );
|
||||
|
||||
if( !m_FilledPolysList.count( aLayer ) )
|
||||
return;
|
||||
|
||||
aCornerBuffer = m_FilledPolysList.at( aLayer );
|
||||
|
||||
int numSegs = GetArcToSegmentCount( aClearance, aError, 360.0 );
|
||||
aCornerBuffer.Inflate( aClearance, numSegs );
|
||||
aCornerBuffer.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
}
|
||||
|
||||
|
||||
void PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIgnoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !aIgnoreLineWidth, "IgnoreLineWidth has no meaning for dimensions." );
|
||||
|
||||
for( const std::shared_ptr<SHAPE>& shape : m_shapes )
|
||||
{
|
||||
const SHAPE_CIRCLE* circle = dynamic_cast<const SHAPE_CIRCLE*>( shape.get() );
|
||||
const SHAPE_SEGMENT* seg = dynamic_cast<const SHAPE_SEGMENT*>( shape.get() );
|
||||
|
||||
if( circle )
|
||||
{
|
||||
TransformCircleToPolygon( aCornerBuffer, (wxPoint) circle->GetCenter(),
|
||||
circle->GetRadius() + m_lineThickness / 2 + aClearance,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
else if( seg )
|
||||
{
|
||||
TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A,
|
||||
(wxPoint) seg->GetSeg().B, m_lineThickness + 2 * aClearance,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon unexpected "
|
||||
"shape type." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2187,6 +2187,146 @@ bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) co
|
|||
}
|
||||
|
||||
|
||||
void FOOTPRINT::TransformPadsWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aMaxError, ERROR_LOC aErrorLoc,
|
||||
bool aSkipNPTHPadsWihNoCopper,
|
||||
bool aSkipPlatedPads,
|
||||
bool aSkipNonPlatedPads ) const
|
||||
{
|
||||
for( const PAD* pad : m_pads )
|
||||
{
|
||||
if( aLayer != UNDEFINED_LAYER && !pad->IsOnLayer(aLayer) )
|
||||
continue;
|
||||
|
||||
if( !pad->FlashLayer( aLayer ) && IsCopperLayer( aLayer ) )
|
||||
continue;
|
||||
|
||||
// NPTH pads are not drawn on layers if the shape size and pos is the same
|
||||
// as their hole:
|
||||
if( aSkipNPTHPadsWihNoCopper && pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
||||
{
|
||||
if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
|
||||
{
|
||||
switch( pad->GetShape() )
|
||||
{
|
||||
case PAD_SHAPE::CIRCLE:
|
||||
if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
case PAD_SHAPE::OVAL:
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool isPlated = ( ( aLayer == F_Cu ) && pad->FlashLayer( F_Mask ) ) ||
|
||||
( ( aLayer == B_Cu ) && pad->FlashLayer( B_Mask ) );
|
||||
|
||||
if( aSkipPlatedPads && isPlated )
|
||||
continue;
|
||||
|
||||
if( aSkipNonPlatedPads && !isPlated )
|
||||
continue;
|
||||
|
||||
wxSize clearance( aClearance, aClearance );
|
||||
|
||||
switch( aLayer )
|
||||
{
|
||||
case F_Mask:
|
||||
case B_Mask:
|
||||
clearance.x += pad->GetSolderMaskMargin();
|
||||
clearance.y += pad->GetSolderMaskMargin();
|
||||
break;
|
||||
|
||||
case F_Paste:
|
||||
case B_Paste:
|
||||
clearance += pad->GetSolderPasteMargin();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Our standard TransformShapeWithClearanceToPolygon() routines can't handle differing
|
||||
// x:y clearance values (which get generated when a relative paste margin is used with
|
||||
// an oblong pad). So we apply this huge hack and fake a larger pad to run the transform
|
||||
// on.
|
||||
// Of course being a hack it falls down when dealing with custom shape pads (where the
|
||||
// size is only the size of the anchor), so for those we punt and just use clearance.x.
|
||||
|
||||
if( ( clearance.x < 0 || clearance.x != clearance.y )
|
||||
&& pad->GetShape() != PAD_SHAPE::CUSTOM )
|
||||
{
|
||||
PAD dummy( *pad );
|
||||
dummy.SetSize( pad->GetSize() + clearance + clearance );
|
||||
dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
||||
aMaxError, aErrorLoc );
|
||||
}
|
||||
else
|
||||
{
|
||||
pad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, clearance.x,
|
||||
aMaxError, aErrorLoc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FOOTPRINT::TransformFPShapesWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIncludeText,
|
||||
bool aIncludeShapes ) const
|
||||
{
|
||||
std::vector<FP_TEXT*> texts; // List of FP_TEXT to convert
|
||||
|
||||
for( BOARD_ITEM* item : GraphicalItems() )
|
||||
{
|
||||
if( item->Type() == PCB_FP_TEXT_T && aIncludeText )
|
||||
{
|
||||
FP_TEXT* text = static_cast<FP_TEXT*>( item );
|
||||
|
||||
if( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer && text->IsVisible() )
|
||||
texts.push_back( text );
|
||||
}
|
||||
|
||||
if( item->Type() == PCB_FP_SHAPE_T && aIncludeShapes )
|
||||
{
|
||||
const FP_SHAPE* outline = static_cast<FP_SHAPE*>( item );
|
||||
|
||||
if( aLayer != UNDEFINED_LAYER && outline->GetLayer() == aLayer )
|
||||
{
|
||||
outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( aIncludeText )
|
||||
{
|
||||
if( Reference().GetLayer() == aLayer && Reference().IsVisible() )
|
||||
texts.push_back( &Reference() );
|
||||
|
||||
if( Value().GetLayer() == aLayer && Value().IsVisible() )
|
||||
texts.push_back( &Value() );
|
||||
}
|
||||
|
||||
for( const FP_TEXT* text : texts )
|
||||
{
|
||||
text->TransformTextShapeWithClearanceToPolygon( aCornerBuffer, aLayer, aClearance,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct FOOTPRINT_DESC
|
||||
{
|
||||
FOOTPRINT_DESC()
|
||||
|
|
|
@ -449,6 +449,44 @@ std::shared_ptr<SHAPE> FP_TEXT::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
|||
}
|
||||
|
||||
|
||||
void FP_TEXT::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc ) const
|
||||
{
|
||||
struct TSEGM_2_POLY_PRMS prms;
|
||||
|
||||
prms.m_cornerBuffer = &aCornerBuffer;
|
||||
prms.m_textWidth = GetEffectiveTextPenWidth() + ( 2 * aClearance );
|
||||
prms.m_error = aError;
|
||||
wxSize size = GetTextSize();
|
||||
int penWidth = GetEffectiveTextPenWidth();
|
||||
|
||||
if( IsMirrored() )
|
||||
size.x = -size.x;
|
||||
|
||||
GRText( nullptr, GetTextPos(), BLACK, GetShownText(), GetDrawRotation(), size,
|
||||
GetHorizJustify(), GetVertJustify(), penWidth, IsItalic(), IsBold(),
|
||||
addTextSegmToPoly, &prms );
|
||||
}
|
||||
|
||||
|
||||
void FP_TEXT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIgnoreLineWidth ) const
|
||||
{
|
||||
SHAPE_POLY_SET buffer;
|
||||
EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( &buffer, aClearance );
|
||||
|
||||
const FOOTPRINT* parentFootprint = static_cast<const FOOTPRINT*>( m_parent );
|
||||
|
||||
if( parentFootprint )
|
||||
buffer.Rotate( DECIDEG2RAD( GetDrawRotation() ), GetTextPos() );
|
||||
|
||||
aCornerBuffer.Append( buffer );
|
||||
}
|
||||
|
||||
|
||||
static struct FP_TEXT_DESC
|
||||
{
|
||||
FP_TEXT_DESC()
|
||||
|
|
123
pcbnew/pad.cpp
123
pcbnew/pad.cpp
|
@ -1447,6 +1447,129 @@ void PAD::SwapData( BOARD_ITEM* aImage )
|
|||
}
|
||||
|
||||
|
||||
bool PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
|
||||
int aError, ERROR_LOC aErrorLoc ) const
|
||||
{
|
||||
wxSize drillsize = GetDrillSize();
|
||||
|
||||
if( !drillsize.x || !drillsize.y )
|
||||
return false;
|
||||
|
||||
const SHAPE_SEGMENT* seg = GetEffectiveHoleShape();
|
||||
|
||||
TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
|
||||
seg->GetWidth() + aInflateValue * 2, aError, aErrorLoc );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for pads." );
|
||||
|
||||
// minimal segment count to approximate a circle to create the polygonal pad shape
|
||||
// This minimal value is mainly for very small pads, like SM0402.
|
||||
// Most of time pads are using the segment count given by aError value.
|
||||
const int pad_min_seg_per_circle_count = 16;
|
||||
double angle = m_orient;
|
||||
int dx = m_size.x / 2;
|
||||
int dy = m_size.y / 2;
|
||||
|
||||
wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset,
|
||||
// the pad position is NOT the shape position
|
||||
|
||||
switch( GetShape() )
|
||||
{
|
||||
case PAD_SHAPE::CIRCLE:
|
||||
case PAD_SHAPE::OVAL:
|
||||
// Note: dx == dy is not guaranteed for circle pads in legacy boards
|
||||
if( dx == dy || ( GetShape() == PAD_SHAPE::CIRCLE ) )
|
||||
{
|
||||
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx + aClearanceValue, aError,
|
||||
aErrorLoc, pad_min_seg_per_circle_count );
|
||||
}
|
||||
else
|
||||
{
|
||||
int half_width = std::min( dx, dy );
|
||||
wxPoint delta( dx - half_width, dy - half_width );
|
||||
|
||||
RotatePoint( &delta, angle );
|
||||
|
||||
TransformOvalToPolygon( aCornerBuffer, padShapePos - delta, padShapePos + delta,
|
||||
( half_width + aClearanceValue ) * 2, aError, aErrorLoc,
|
||||
pad_min_seg_per_circle_count );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PAD_SHAPE::TRAPEZOID:
|
||||
case PAD_SHAPE::RECT:
|
||||
{
|
||||
int ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.x / 2 : 0;
|
||||
int ddy = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.y / 2 : 0;
|
||||
|
||||
SHAPE_POLY_SET outline;
|
||||
TransformTrapezoidToPolygon( outline, padShapePos, m_size, angle, ddx, ddy,
|
||||
aClearanceValue, aError, aErrorLoc );
|
||||
aCornerBuffer.Append( outline );
|
||||
break;
|
||||
}
|
||||
|
||||
case PAD_SHAPE::CHAMFERED_RECT:
|
||||
case PAD_SHAPE::ROUNDRECT:
|
||||
{
|
||||
bool doChamfer = GetShape() == PAD_SHAPE::CHAMFERED_RECT;
|
||||
|
||||
SHAPE_POLY_SET outline;
|
||||
TransformRoundChamferedRectToPolygon( outline, padShapePos, m_size, angle,
|
||||
GetRoundRectCornerRadius(),
|
||||
doChamfer ? GetChamferRectRatio() : 0,
|
||||
doChamfer ? GetChamferPositions() : 0,
|
||||
aClearanceValue, aError, aErrorLoc );
|
||||
aCornerBuffer.Append( outline );
|
||||
break;
|
||||
}
|
||||
|
||||
case PAD_SHAPE::CUSTOM:
|
||||
{
|
||||
SHAPE_POLY_SET outline;
|
||||
MergePrimitivesAsPolygon( &outline, aErrorLoc );
|
||||
outline.Rotate( -DECIDEG2RAD( m_orient ) );
|
||||
outline.Move( VECTOR2I( m_pos ) );
|
||||
|
||||
if( aClearanceValue )
|
||||
{
|
||||
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
|
||||
pad_min_seg_per_circle_count );
|
||||
int clearance = aClearanceValue;
|
||||
|
||||
if( aErrorLoc == ERROR_OUTSIDE )
|
||||
{
|
||||
int actual_error = CircleToEndSegmentDeltaRadius( clearance, numSegs );
|
||||
clearance += GetCircleToPolyCorrection( actual_error );
|
||||
}
|
||||
|
||||
outline.Inflate( clearance, numSegs );
|
||||
outline.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
outline.Fracture( SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
|
||||
aCornerBuffer.Append( outline );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( "PAD::TransformShapeWithClearanceToPolygon no implementation for "
|
||||
+ PAD_SHAPE_T_asString( GetShape() ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct PAD_DESC
|
||||
{
|
||||
PAD_DESC()
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <pcb_edit_frame.h>
|
||||
#include <base_units.h>
|
||||
#include <board.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <pcb_dimension.h>
|
||||
#include <pcb_text.h>
|
||||
#include <geometry/shape_compound.h>
|
||||
|
@ -502,6 +503,39 @@ OPT_VECTOR2I PCB_DIMENSION_BASE::segCircleIntersection( CIRCLE& aCircle, SEG& aS
|
|||
}
|
||||
|
||||
|
||||
void PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIgnoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !aIgnoreLineWidth, "IgnoreLineWidth has no meaning for dimensions." );
|
||||
|
||||
for( const std::shared_ptr<SHAPE>& shape : m_shapes )
|
||||
{
|
||||
const SHAPE_CIRCLE* circle = dynamic_cast<const SHAPE_CIRCLE*>( shape.get() );
|
||||
const SHAPE_SEGMENT* seg = dynamic_cast<const SHAPE_SEGMENT*>( shape.get() );
|
||||
|
||||
if( circle )
|
||||
{
|
||||
TransformCircleToPolygon( aCornerBuffer, (wxPoint) circle->GetCenter(),
|
||||
circle->GetRadius() + m_lineThickness / 2 + aClearance,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
else if( seg )
|
||||
{
|
||||
TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A,
|
||||
(wxPoint) seg->GetSeg().B, m_lineThickness + 2 * aClearance,
|
||||
aError, aErrorLoc );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon unexpected "
|
||||
"shape type." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PCB_DIM_ALIGNED::PCB_DIM_ALIGNED( BOARD_ITEM* aParent, KICAD_T aType ) :
|
||||
PCB_DIMENSION_BASE( aParent, aType ),
|
||||
m_height( 0 )
|
||||
|
|
|
@ -240,6 +240,16 @@ bool PCB_SHAPE::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
|
|||
}
|
||||
|
||||
|
||||
void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
EDA_SHAPE::TransformShapeWithClearanceToPolygon( aCornerBuffer, aClearanceValue, aError,
|
||||
aErrorLoc, ignoreLineWidth );
|
||||
}
|
||||
|
||||
|
||||
static struct PCB_SHAPE_DESC
|
||||
{
|
||||
PCB_SHAPE_DESC()
|
||||
|
|
|
@ -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-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 1992-2021 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
|
||||
|
@ -219,6 +219,38 @@ std::shared_ptr<SHAPE> PCB_TEXT::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
|||
}
|
||||
|
||||
|
||||
void PCB_TEXT::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc ) const
|
||||
{
|
||||
struct TSEGM_2_POLY_PRMS prms;
|
||||
|
||||
wxSize size = GetTextSize();
|
||||
|
||||
if( IsMirrored() )
|
||||
size.x = -size.x;
|
||||
|
||||
int penWidth = GetEffectiveTextPenWidth();
|
||||
|
||||
prms.m_cornerBuffer = &aCornerBuffer;
|
||||
prms.m_textWidth = GetEffectiveTextPenWidth() + ( 2 * aClearanceValue );
|
||||
prms.m_error = aError;
|
||||
COLOR4D color; // not actually used, but needed by GRText
|
||||
|
||||
GRText( nullptr, GetTextPos(), color, GetShownText(), GetTextAngle(), size, GetHorizJustify(),
|
||||
GetVertJustify(), penWidth, IsItalic(), IsBold(), addTextSegmToPoly, &prms );
|
||||
}
|
||||
|
||||
|
||||
void PCB_TEXT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool aIgnoreLineWidth ) const
|
||||
{
|
||||
EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( &aCornerBuffer, aClearance );
|
||||
}
|
||||
|
||||
|
||||
static struct TEXTE_PCB_DESC
|
||||
{
|
||||
TEXTE_PCB_DESC()
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <connectivity/connectivity_data.h>
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <pcb_track.h>
|
||||
#include <base_units.h>
|
||||
#include <bitmaps.h>
|
||||
|
@ -1045,6 +1046,43 @@ std::shared_ptr<SHAPE> PCB_ARC::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
|||
}
|
||||
|
||||
|
||||
void PCB_TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearanceValue,
|
||||
int aError, ERROR_LOC aErrorLoc,
|
||||
bool ignoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for tracks." );
|
||||
|
||||
|
||||
switch( Type() )
|
||||
{
|
||||
case PCB_VIA_T:
|
||||
{
|
||||
int radius = ( m_Width / 2 ) + aClearanceValue;
|
||||
TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aError, aErrorLoc );
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_ARC_T:
|
||||
{
|
||||
const PCB_ARC* arc = static_cast<const PCB_ARC*>( this );
|
||||
int width = m_Width + ( 2 * aClearanceValue );
|
||||
|
||||
TransformArcToPolygon( aCornerBuffer, arc->GetStart(), arc->GetMid(),
|
||||
arc->GetEnd(), width, aError, aErrorLoc );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
int width = m_Width + ( 2 * aClearanceValue );
|
||||
|
||||
TransformOvalToPolygon( aCornerBuffer, m_Start, m_End, width, aError, aErrorLoc );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
|
||||
|
|
|
@ -1397,6 +1397,55 @@ std::shared_ptr<SHAPE> ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
|||
}
|
||||
|
||||
|
||||
void ZONE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
PCB_LAYER_ID aLayer, int aClearance, int aError,
|
||||
ERROR_LOC aErrorLoc, bool aIgnoreLineWidth ) const
|
||||
{
|
||||
wxASSERT_MSG( !aIgnoreLineWidth, "IgnoreLineWidth has no meaning for zones." );
|
||||
|
||||
if( !m_FilledPolysList.count( aLayer ) )
|
||||
return;
|
||||
|
||||
aCornerBuffer = m_FilledPolysList.at( aLayer );
|
||||
|
||||
int numSegs = GetArcToSegmentCount( aClearance, aError, 360.0 );
|
||||
aCornerBuffer.Inflate( aClearance, numSegs );
|
||||
aCornerBuffer.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
}
|
||||
|
||||
|
||||
void ZONE::TransformSolidAreasShapesToPolygon( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aError ) const
|
||||
{
|
||||
if( !m_FilledPolysList.count( aLayer ) || m_FilledPolysList.at( aLayer ).IsEmpty() )
|
||||
return;
|
||||
|
||||
// Just add filled areas if filled polygons outlines have no thickness
|
||||
if( !GetFilledPolysUseThickness() || GetMinThickness() == 0 )
|
||||
{
|
||||
const SHAPE_POLY_SET& polys = m_FilledPolysList.at( aLayer );
|
||||
aCornerBuffer.Append( polys );
|
||||
return;
|
||||
}
|
||||
|
||||
// Filled areas have polygons with outline thickness.
|
||||
// we must create the polygons and add inflated polys
|
||||
SHAPE_POLY_SET polys = m_FilledPolysList.at( aLayer );
|
||||
|
||||
auto board = GetBoard();
|
||||
int maxError = ARC_HIGH_DEF;
|
||||
|
||||
if( board )
|
||||
maxError = board->GetDesignSettings().m_MaxError;
|
||||
|
||||
int numSegs = GetArcToSegmentCount( GetMinThickness(), maxError, 360.0 );
|
||||
|
||||
polys.InflateWithLinkedHoles( GetMinThickness()/2, numSegs, SHAPE_POLY_SET::PM_FAST );
|
||||
|
||||
aCornerBuffer.Append( polys );
|
||||
}
|
||||
|
||||
|
||||
static struct ZONE_DESC
|
||||
{
|
||||
ZONE_DESC()
|
||||
|
|
Loading…
Reference in New Issue