2014-10-23 17:53:38 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2018-04-06 13:30:52 +00:00
|
|
|
* Copyright (C) 2009-2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
2019-05-22 13:33:48 +00:00
|
|
|
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
|
2014-10-23 17:53:38 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
#include <vector>
|
2018-07-07 11:04:01 +00:00
|
|
|
#include <bezier_curves.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <trigo.h>
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <board.h>
|
|
|
|
#include <pad.h>
|
2021-02-03 23:33:36 +00:00
|
|
|
#include <dimension.h>
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <track.h>
|
2020-10-24 02:06:42 +00:00
|
|
|
#include <kicad_string.h>
|
2020-10-04 23:34:59 +00:00
|
|
|
#include <pcb_shape.h>
|
|
|
|
#include <pcb_text.h>
|
2020-11-11 23:05:59 +00:00
|
|
|
#include <zone.h>
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <footprint.h>
|
2020-10-04 23:34:59 +00:00
|
|
|
#include <fp_shape.h>
|
2012-08-23 19:15:58 +00:00
|
|
|
#include <convert_basic_shapes_to_polygon.h>
|
2018-03-08 12:48:29 +00:00
|
|
|
#include <geometry/geometry_utils.h>
|
2020-06-22 19:35:09 +00:00
|
|
|
#include <geometry/shape_segment.h>
|
2021-02-03 23:33:36 +00:00
|
|
|
#include <geometry/shape_circle.h>
|
|
|
|
#include <geometry/shape_rect.h>
|
|
|
|
#include <geometry/shape_line_chain.h>
|
2009-11-16 08:13:40 +00:00
|
|
|
|
2020-06-22 19:35:09 +00:00
|
|
|
|
2018-04-06 13:30:52 +00:00
|
|
|
// A helper struct for the callback function
|
2014-02-28 10:51:47 +00:00
|
|
|
// These variables are parameters used in addTextSegmToPoly.
|
|
|
|
// But addTextSegmToPoly is a call-back function,
|
|
|
|
// so we cannot send them as arguments.
|
2020-10-13 10:55:24 +00:00
|
|
|
struct TSEGM_2_POLY_PRMS
|
|
|
|
{
|
2018-04-06 13:30:52 +00:00
|
|
|
int m_textWidth;
|
2019-05-22 13:33:48 +00:00
|
|
|
int m_error;
|
2018-04-06 13:30:52 +00:00
|
|
|
SHAPE_POLY_SET* m_cornerBuffer;
|
|
|
|
};
|
2020-10-13 10:55:24 +00:00
|
|
|
|
2018-04-06 13:30:52 +00:00
|
|
|
TSEGM_2_POLY_PRMS prms;
|
2014-02-28 10:51:47 +00:00
|
|
|
|
2020-10-13 10:55:24 +00:00
|
|
|
|
2019-05-31 12:15:25 +00:00
|
|
|
// This is a call back function, used by GRText to draw the 3D text shape:
|
2018-04-06 13:30:52 +00:00
|
|
|
static void addTextSegmToPoly( int x0, int y0, int xf, int yf, void* aData )
|
2014-02-28 10:51:47 +00:00
|
|
|
{
|
2018-04-06 13:30:52 +00:00
|
|
|
TSEGM_2_POLY_PRMS* prm = static_cast<TSEGM_2_POLY_PRMS*>( aData );
|
2020-09-10 23:05:20 +00:00
|
|
|
TransformOvalToPolygon( *prm->m_cornerBuffer, wxPoint( x0, y0 ), wxPoint( xf, yf ),
|
2020-10-13 10:55:24 +00:00
|
|
|
prm->m_textWidth, prm->m_error, ERROR_INSIDE );
|
2014-02-28 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
|
2021-03-06 09:27:41 +00:00
|
|
|
void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aOutlines ) const
|
2014-05-17 19:29:15 +00:00
|
|
|
{
|
2020-10-13 10:55:24 +00:00
|
|
|
int maxError = GetDesignSettings().m_MaxError;
|
|
|
|
|
2014-05-17 19:29:15 +00:00
|
|
|
// convert tracks and vias:
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const TRACK* track : m_tracks )
|
2014-05-17 19:29:15 +00:00
|
|
|
{
|
|
|
|
if( !track->IsOnLayer( aLayer ) )
|
|
|
|
continue;
|
|
|
|
|
2020-10-13 10:55:24 +00:00
|
|
|
track->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
|
|
|
ERROR_INSIDE );
|
2014-05-17 19:29:15 +00:00
|
|
|
}
|
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
// convert pads and other copper items in footprints
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const FOOTPRINT* footprint : m_footprints )
|
2014-05-17 19:29:15 +00:00
|
|
|
{
|
2020-11-13 00:43:45 +00:00
|
|
|
footprint->TransformPadsWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
|
|
|
ERROR_INSIDE );
|
2014-05-17 19:29:15 +00:00
|
|
|
|
2020-10-21 03:48:06 +00:00
|
|
|
// Micro-wave footprints may have items on copper layers
|
2020-11-13 00:43:45 +00:00
|
|
|
footprint->TransformFPShapesWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
2020-11-29 14:00:39 +00:00
|
|
|
ERROR_INSIDE,
|
|
|
|
true, /* include text */
|
|
|
|
true /* include shapes */ );
|
|
|
|
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const ZONE* zone : footprint->Zones() )
|
2020-11-29 14:00:39 +00:00
|
|
|
{
|
|
|
|
if( zone->GetLayerSet().test( aLayer ) )
|
|
|
|
zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
|
|
|
|
}
|
2014-05-17 19:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// convert copper zones
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const ZONE* zone : Zones() )
|
2014-05-17 19:29:15 +00:00
|
|
|
{
|
2020-06-24 02:19:08 +00:00
|
|
|
if( zone->GetLayerSet().test( aLayer ) )
|
2020-08-12 18:42:40 +00:00
|
|
|
zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
|
2014-05-17 19:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// convert graphic items on copper layers (texts)
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const BOARD_ITEM* item : m_drawings )
|
2014-05-17 19:29:15 +00:00
|
|
|
{
|
|
|
|
if( !item->IsOnLayer( aLayer ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_SHAPE_T:
|
2020-10-13 10:55:24 +00:00
|
|
|
{
|
2021-03-06 09:27:41 +00:00
|
|
|
const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
|
2020-10-13 10:55:24 +00:00
|
|
|
shape->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
|
|
|
ERROR_INSIDE );
|
|
|
|
}
|
2014-05-17 19:29:15 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_TEXT_T:
|
2020-10-13 10:55:24 +00:00
|
|
|
{
|
2021-03-06 09:27:41 +00:00
|
|
|
const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
|
2020-11-29 14:00:39 +00:00
|
|
|
text->TransformTextShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
|
|
|
|
ERROR_INSIDE );
|
2020-10-13 10:55:24 +00:00
|
|
|
}
|
2014-05-17 19:29:15 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
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
|
2013-05-01 19:01:14 +00:00
|
|
|
{
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const PAD* pad : m_pads )
|
2013-05-01 19:01:14 +00:00
|
|
|
{
|
2018-02-19 00:00:29 +00:00
|
|
|
if( aLayer != UNDEFINED_LAYER && !pad->IsOnLayer(aLayer) )
|
2013-05-01 19:01:14 +00:00
|
|
|
continue;
|
|
|
|
|
2020-09-29 20:40:17 +00:00
|
|
|
if( !pad->FlashLayer( aLayer ) && IsCopperLayer( aLayer ) )
|
2020-08-11 09:26:00 +00:00
|
|
|
continue;
|
|
|
|
|
2015-03-23 08:28:12 +00:00
|
|
|
// NPTH pads are not drawn on layers if the shape size and pos is the same
|
|
|
|
// as their hole:
|
2020-09-30 15:38:35 +00:00
|
|
|
if( aSkipNPTHPadsWihNoCopper && pad->GetAttribute() == PAD_ATTRIB_NPTH )
|
2015-03-23 08:28:12 +00:00
|
|
|
{
|
|
|
|
if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
|
|
|
|
{
|
|
|
|
switch( pad->GetShape() )
|
|
|
|
{
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_CIRCLE:
|
|
|
|
if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
2015-03-23 08:28:12 +00:00
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_OVAL:
|
|
|
|
if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
|
2015-03-23 08:28:12 +00:00
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-01 19:01:14 +00:00
|
|
|
|
2020-09-29 20:40:17 +00:00
|
|
|
const bool isPlated = ( ( aLayer == F_Cu ) && pad->FlashLayer( F_Mask ) ) ||
|
|
|
|
( ( aLayer == B_Cu ) && pad->FlashLayer( B_Mask ) );
|
2020-09-06 19:09:43 +00:00
|
|
|
|
|
|
|
if( aSkipPlatedPads && isPlated )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( aSkipNonPlatedPads && !isPlated )
|
|
|
|
continue;
|
|
|
|
|
2020-10-13 10:55:24 +00:00
|
|
|
wxSize clearance( aClearance, aClearance );
|
2019-06-20 22:37:41 +00:00
|
|
|
|
2013-05-01 19:01:14 +00:00
|
|
|
switch( aLayer )
|
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
case F_Mask:
|
|
|
|
case B_Mask:
|
2020-08-28 13:00:05 +00:00
|
|
|
clearance.x += pad->GetSolderMaskMargin();
|
|
|
|
clearance.y += pad->GetSolderMaskMargin();
|
2013-05-01 19:01:14 +00:00
|
|
|
break;
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
case F_Paste:
|
|
|
|
case B_Paste:
|
2020-08-28 13:00:05 +00:00
|
|
|
clearance += pad->GetSolderPasteMargin();
|
2013-05-01 19:01:14 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-08-28 13:00:05 +00:00
|
|
|
// 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.
|
|
|
|
|
2020-08-28 19:27:44 +00:00
|
|
|
if( ( clearance.x < 0 || clearance.x != clearance.y )
|
|
|
|
&& pad->GetShape() != PAD_SHAPE_CUSTOM )
|
2020-08-28 13:00:05 +00:00
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
PAD dummy( *pad );
|
2020-08-28 13:00:05 +00:00
|
|
|
dummy.SetSize( pad->GetSize() + clearance + clearance );
|
2020-10-13 10:55:24 +00:00
|
|
|
dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
|
|
|
aMaxError, aErrorLoc );
|
2020-08-28 13:00:05 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, clearance.x,
|
2020-10-13 10:55:24 +00:00
|
|
|
aMaxError, aErrorLoc );
|
2020-08-28 13:00:05 +00:00
|
|
|
}
|
2013-05-01 19:01:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
/**
|
|
|
|
* 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
|
2013-05-01 19:01:14 +00:00
|
|
|
*/
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::TransformFPShapesWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|
|
|
PCB_LAYER_ID aLayer, int aClearance,
|
|
|
|
int aError, ERROR_LOC aErrorLoc,
|
|
|
|
bool aIncludeText,
|
|
|
|
bool aIncludeShapes ) const
|
2013-05-01 19:01:14 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
std::vector<FP_TEXT*> texts; // List of FP_TEXT to convert
|
2014-02-28 10:51:47 +00:00
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
for( BOARD_ITEM* item : GraphicalItems() )
|
2013-05-01 19:01:14 +00:00
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
if( item->Type() == PCB_FP_TEXT_T && aIncludeText )
|
2019-05-14 12:39:34 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
FP_TEXT* text = static_cast<FP_TEXT*>( item );
|
2015-03-11 13:59:43 +00:00
|
|
|
|
2020-04-13 19:55:27 +00:00
|
|
|
if( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer && text->IsVisible() )
|
2019-05-14 12:39:34 +00:00
|
|
|
texts.push_back( text );
|
|
|
|
}
|
2013-05-01 19:01:14 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( item->Type() == PCB_FP_SHAPE_T && aIncludeShapes )
|
2019-09-09 12:44:41 +00:00
|
|
|
{
|
2021-03-06 09:27:41 +00:00
|
|
|
const FP_SHAPE* outline = static_cast<FP_SHAPE*>( item );
|
2013-05-01 19:01:14 +00:00
|
|
|
|
2020-04-13 19:55:27 +00:00
|
|
|
if( aLayer != UNDEFINED_LAYER && outline->GetLayer() == aLayer )
|
2020-10-13 10:55:24 +00:00
|
|
|
{
|
|
|
|
outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
|
|
|
aError, aErrorLoc );
|
|
|
|
}
|
2019-09-09 12:44:41 +00:00
|
|
|
}
|
2014-02-28 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 19:55:27 +00:00
|
|
|
if( aIncludeText )
|
2016-07-19 17:35:25 +00:00
|
|
|
{
|
2020-04-13 19:55:27 +00:00
|
|
|
if( Reference().GetLayer() == aLayer && Reference().IsVisible() )
|
|
|
|
texts.push_back( &Reference() );
|
2016-07-19 17:35:25 +00:00
|
|
|
|
2020-04-13 19:55:27 +00:00
|
|
|
if( Value().GetLayer() == aLayer && Value().IsVisible() )
|
|
|
|
texts.push_back( &Value() );
|
2016-07-19 17:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const FP_TEXT* text : texts )
|
2016-07-19 17:35:25 +00:00
|
|
|
{
|
2020-11-27 18:04:30 +00:00
|
|
|
text->TransformTextShapeWithClearanceToPolygon( aCornerBuffer, aLayer, aClearance,
|
|
|
|
aError, aErrorLoc );
|
|
|
|
}
|
|
|
|
}
|
2020-04-13 19:55:27 +00:00
|
|
|
|
2016-07-19 17:35:25 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
/**
|
|
|
|
* Function TransformTextShapeWithClearanceToPolygon
|
|
|
|
* Convert the text to a polygonSet describing the actual character strokes (one per segment).
|
|
|
|
* @aCornerBuffer = SHAPE_POLY_SET to store the polygon corners
|
|
|
|
* @aClearanceValue = the clearance around the text
|
|
|
|
* @aError = the maximum error to allow when approximating curves
|
|
|
|
*/
|
2020-11-27 18:04:30 +00:00
|
|
|
void FP_TEXT::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
2020-11-29 14:00:39 +00:00
|
|
|
PCB_LAYER_ID aLayer, int aClearance,
|
|
|
|
int aError, ERROR_LOC aErrorLoc ) const
|
2020-11-27 18:04:30 +00:00
|
|
|
{
|
|
|
|
bool forceBold = true;
|
|
|
|
int penWidth = 0; // force max width for bold text
|
2016-07-19 17:35:25 +00:00
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
prms.m_cornerBuffer = &aCornerBuffer;
|
|
|
|
prms.m_textWidth = GetEffectiveTextPenWidth() + ( 2 * aClearance );
|
|
|
|
prms.m_error = aError;
|
|
|
|
wxSize size = GetTextSize();
|
|
|
|
|
|
|
|
if( IsMirrored() )
|
|
|
|
size.x = -size.x;
|
|
|
|
|
|
|
|
GRText( NULL, GetTextPos(), BLACK, GetShownText(), GetDrawRotation(), size, GetHorizJustify(),
|
|
|
|
GetVertJustify(), penWidth, IsItalic(), forceBold, addTextSegmToPoly, &prms );
|
2013-05-01 19:01:14 +00:00
|
|
|
}
|
|
|
|
|
2019-05-14 12:39:34 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
void FP_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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
void ZONE::TransformSolidAreasShapesToPolygon( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer,
|
|
|
|
int aError ) const
|
2013-05-01 19:01:14 +00:00
|
|
|
{
|
2020-06-24 02:19:08 +00:00
|
|
|
if( !m_FilledPolysList.count( aLayer ) || m_FilledPolysList.at( aLayer ).IsEmpty() )
|
2013-05-01 19:01:14 +00:00
|
|
|
return;
|
|
|
|
|
2020-08-12 08:13:54 +00:00
|
|
|
// 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 );
|
2020-06-24 02:19:08 +00:00
|
|
|
|
2019-05-22 14:47:38 +00:00
|
|
|
auto board = GetBoard();
|
|
|
|
int maxError = ARC_HIGH_DEF;
|
|
|
|
|
|
|
|
if( board )
|
|
|
|
maxError = board->GetDesignSettings().m_MaxError;
|
2013-05-01 19:01:14 +00:00
|
|
|
|
2020-09-10 23:05:20 +00:00
|
|
|
int numSegs = GetArcToSegmentCount( GetMinThickness(), maxError, 360.0 );
|
2013-05-01 19:01:14 +00:00
|
|
|
|
2020-08-12 08:13:54 +00:00
|
|
|
polys.InflateWithLinkedHoles( GetMinThickness()/2, numSegs, SHAPE_POLY_SET::PM_FAST );
|
2015-07-14 20:23:13 +00:00
|
|
|
|
2020-08-12 08:13:54 +00:00
|
|
|
aCornerBuffer.Append( polys );
|
2013-05-01 19:01:14 +00:00
|
|
|
}
|
|
|
|
|
2019-05-14 12:39:34 +00:00
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
void EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( SHAPE_POLY_SET* aCornerBuffer,
|
|
|
|
int aClearanceValue ) const
|
2009-11-16 08:13:40 +00:00
|
|
|
{
|
2013-03-18 19:36:07 +00:00
|
|
|
if( GetText().Length() == 0 )
|
2009-11-16 08:13:40 +00:00
|
|
|
return;
|
|
|
|
|
2015-07-27 19:45:57 +00:00
|
|
|
wxPoint corners[4]; // Buffer of polygon corners
|
2009-11-16 08:13:40 +00:00
|
|
|
|
2020-04-14 12:25:00 +00:00
|
|
|
EDA_RECT rect = GetTextBox();
|
2020-04-13 19:55:27 +00:00
|
|
|
rect.Inflate( aClearanceValue + Millimeter2iu( DEFAULT_TEXT_WIDTH ) );
|
2009-11-16 08:13:40 +00:00
|
|
|
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;
|
|
|
|
|
2018-08-23 22:41:57 +00:00
|
|
|
aCornerBuffer->NewOutline();
|
2015-07-27 19:45:57 +00:00
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
for( wxPoint& corner : corners )
|
2009-11-16 08:13:40 +00:00
|
|
|
{
|
|
|
|
// Rotate polygon
|
2019-09-09 12:44:41 +00:00
|
|
|
RotatePoint( &corner.x, &corner.y, GetTextPos().x, GetTextPos().y, GetTextAngle() );
|
|
|
|
aCornerBuffer->Append( corner.x, corner.y );
|
2009-11-16 08:13:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-29 19:29:19 +00:00
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
/**
|
2020-11-29 14:00:39 +00:00
|
|
|
* Function TransformTextShapeWithClearanceToPolygon
|
|
|
|
* Convert the text to a polygonSet describing the actual character strokes (one per segment).
|
2019-09-09 12:44:41 +00:00
|
|
|
* @aCornerBuffer = SHAPE_POLY_SET to store the polygon corners
|
|
|
|
* @aClearanceValue = the clearance around the text
|
|
|
|
* @aError = the maximum error to allow when approximating curves
|
2013-05-01 19:01:14 +00:00
|
|
|
*/
|
2020-11-29 14:00:39 +00:00
|
|
|
void PCB_TEXT::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
2020-11-13 00:43:45 +00:00
|
|
|
PCB_LAYER_ID aLayer, int aClearanceValue,
|
2020-11-29 14:00:39 +00:00
|
|
|
int aError, ERROR_LOC aErrorLoc ) const
|
2013-05-01 19:01:14 +00:00
|
|
|
{
|
2017-01-23 20:30:11 +00:00
|
|
|
wxSize size = GetTextSize();
|
2012-01-29 19:29:19 +00:00
|
|
|
|
2013-05-01 19:01:14 +00:00
|
|
|
if( IsMirrored() )
|
2015-06-26 13:41:56 +00:00
|
|
|
size.x = -size.x;
|
2012-01-29 19:29:19 +00:00
|
|
|
|
2020-04-13 19:55:27 +00:00
|
|
|
bool forceBold = true;
|
2020-04-14 12:25:00 +00:00
|
|
|
int penWidth = GetEffectiveTextPenWidth();
|
2020-04-13 19:55:27 +00:00
|
|
|
|
2018-04-06 13:30:52 +00:00
|
|
|
prms.m_cornerBuffer = &aCornerBuffer;
|
2020-04-14 12:25:00 +00:00
|
|
|
prms.m_textWidth = GetEffectiveTextPenWidth() + ( 2 * aClearanceValue );
|
2019-05-22 13:33:48 +00:00
|
|
|
prms.m_error = aError;
|
2019-05-31 12:15:25 +00:00
|
|
|
COLOR4D color = COLOR4D::BLACK; // not actually used, but needed by GRText
|
2012-01-29 19:29:19 +00:00
|
|
|
|
2013-05-01 19:01:14 +00:00
|
|
|
if( IsMultilineAllowed() )
|
2012-01-29 19:29:19 +00:00
|
|
|
{
|
2015-01-15 20:01:53 +00:00
|
|
|
wxArrayString strings_list;
|
|
|
|
wxStringSplit( GetShownText(), strings_list, '\n' );
|
2013-12-27 07:24:36 +00:00
|
|
|
std::vector<wxPoint> positions;
|
2015-01-15 20:01:53 +00:00
|
|
|
positions.reserve( strings_list.Count() );
|
2020-04-14 12:25:00 +00:00
|
|
|
GetLinePositions( positions, strings_list.Count() );
|
2012-01-29 19:29:19 +00:00
|
|
|
|
2015-01-15 20:01:53 +00:00
|
|
|
for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
|
2012-01-29 19:29:19 +00:00
|
|
|
{
|
2015-01-15 20:01:53 +00:00
|
|
|
wxString txt = strings_list.Item( ii );
|
2019-05-31 12:15:25 +00:00
|
|
|
GRText( NULL, positions[ii], color, txt, GetTextAngle(), size, GetHorizJustify(),
|
2020-04-18 20:04:41 +00:00
|
|
|
GetVertJustify(), penWidth, IsItalic(), forceBold, addTextSegmToPoly, &prms );
|
2012-01-29 19:29:19 +00:00
|
|
|
}
|
2013-05-01 19:01:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-05-31 12:15:25 +00:00
|
|
|
GRText( NULL, GetTextPos(), color, GetShownText(), GetTextAngle(), size, GetHorizJustify(),
|
2020-04-18 20:04:41 +00:00
|
|
|
GetVertJustify(), penWidth, IsItalic(), forceBold, addTextSegmToPoly, &prms );
|
2012-01-29 19:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-04 23:34:59 +00:00
|
|
|
void PCB_SHAPE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
2020-10-13 10:55:24 +00:00
|
|
|
PCB_LAYER_ID aLayer, int aClearanceValue,
|
|
|
|
int aError, ERROR_LOC aErrorLoc,
|
2020-10-04 23:34:59 +00:00
|
|
|
bool ignoreLineWidth ) const
|
2009-11-16 08:13:40 +00:00
|
|
|
{
|
2020-11-13 21:00:28 +00:00
|
|
|
int width = ignoreLineWidth ? 0 : m_width;
|
2018-11-14 23:34:32 +00:00
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
width += 2 * aClearanceValue;
|
2015-04-09 18:53:36 +00:00
|
|
|
|
2020-11-13 21:00:28 +00:00
|
|
|
switch( m_shape )
|
2009-11-16 08:13:40 +00:00
|
|
|
{
|
|
|
|
case S_CIRCLE:
|
2020-11-14 01:16:02 +00:00
|
|
|
if( IsFilled() )
|
2020-10-13 10:55:24 +00:00
|
|
|
{
|
2020-11-14 01:16:02 +00:00
|
|
|
TransformCircleToPolygon( aCornerBuffer, GetCenter(), GetRadius() + width / 2, aError,
|
2020-10-13 10:55:24 +00:00
|
|
|
aErrorLoc );
|
|
|
|
}
|
2020-06-27 11:57:40 +00:00
|
|
|
else
|
2020-10-13 10:55:24 +00:00
|
|
|
{
|
|
|
|
TransformRingToPolygon( aCornerBuffer, GetCenter(), GetRadius(), width, aError,
|
|
|
|
aErrorLoc );
|
|
|
|
}
|
2009-11-16 08:13:40 +00:00
|
|
|
break;
|
|
|
|
|
2020-06-15 19:50:20 +00:00
|
|
|
case S_RECT:
|
|
|
|
{
|
2020-09-07 00:40:18 +00:00
|
|
|
std::vector<wxPoint> pts = GetRectCorners();
|
2020-06-15 19:50:20 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
if( IsFilled() )
|
2020-06-27 11:57:40 +00:00
|
|
|
{
|
|
|
|
aCornerBuffer.NewOutline();
|
2020-06-15 19:50:20 +00:00
|
|
|
|
2020-06-27 11:57:40 +00:00
|
|
|
for( const wxPoint& pt : pts )
|
|
|
|
aCornerBuffer.Append( pt );
|
|
|
|
}
|
2020-06-19 15:42:47 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
if( width > 0 || !IsFilled() )
|
2020-06-19 15:42:47 +00:00
|
|
|
{
|
2020-06-27 11:57:40 +00:00
|
|
|
// Add in segments
|
2020-10-13 10:55:24 +00:00
|
|
|
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 );
|
2020-06-19 15:42:47 +00:00
|
|
|
}
|
2020-06-15 19:50:20 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2009-11-16 08:13:40 +00:00
|
|
|
case S_ARC:
|
2020-10-23 22:27:02 +00:00
|
|
|
TransformArcToPolygon( aCornerBuffer, GetArcStart(), GetArcMid(), GetArcEnd(), width,
|
2020-10-13 10:55:24 +00:00
|
|
|
aError, aErrorLoc );
|
2009-11-16 08:13:40 +00:00
|
|
|
break;
|
|
|
|
|
2015-04-09 18:53:36 +00:00
|
|
|
case S_SEGMENT:
|
2020-11-13 21:00:28 +00:00
|
|
|
TransformOvalToPolygon( aCornerBuffer, m_start, m_end, width, aError, aErrorLoc );
|
2015-04-09 18:53:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case S_POLYGON:
|
2020-11-14 01:16:02 +00:00
|
|
|
{
|
|
|
|
if( !IsPolyShapeValid() )
|
|
|
|
break;
|
2017-12-21 14:06:29 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
// The polygon is expected to be a simple polygon; not self intersecting, no hole.
|
|
|
|
FOOTPRINT* footprint = GetParentFootprint();
|
|
|
|
double orientation = footprint ? footprint->GetOrientation() : 0.0;
|
|
|
|
wxPoint offset;
|
2015-04-09 18:53:36 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
if( footprint )
|
|
|
|
offset = footprint->GetPosition();
|
2015-04-09 18:53:36 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
// Build the polygon with the actual position and orientation:
|
2021-03-06 09:27:41 +00:00
|
|
|
std::vector<wxPoint> poly;
|
2020-11-14 01:16:02 +00:00
|
|
|
poly = BuildPolyPointsList();
|
2015-11-02 09:24:32 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
for( wxPoint& point : poly )
|
|
|
|
{
|
|
|
|
RotatePoint( &point, orientation );
|
|
|
|
point += offset;
|
|
|
|
}
|
2018-12-02 04:41:17 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
if( IsFilled() )
|
|
|
|
{
|
|
|
|
aCornerBuffer.NewOutline();
|
2018-12-02 04:41:17 +00:00
|
|
|
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const wxPoint& point : poly )
|
2020-11-14 01:16:02 +00:00
|
|
|
aCornerBuffer.Append( point.x, point.y );
|
|
|
|
}
|
2015-04-09 18:53:36 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
if( width > 0 || !IsFilled() )
|
|
|
|
{
|
|
|
|
wxPoint pt1( poly[ poly.size() - 1] );
|
2015-04-09 18:53:36 +00:00
|
|
|
|
2021-03-06 09:27:41 +00:00
|
|
|
for( const wxPoint& pt2 : poly )
|
2020-11-14 01:16:02 +00:00
|
|
|
{
|
|
|
|
if( pt2 != pt1 )
|
|
|
|
TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError, aErrorLoc );
|
|
|
|
|
|
|
|
pt1 = pt2;
|
2015-04-09 18:53:36 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-14 01:16:02 +00:00
|
|
|
}
|
2015-04-09 18:53:36 +00:00
|
|
|
break;
|
|
|
|
|
2018-07-07 11:04:01 +00:00
|
|
|
case S_CURVE: // Bezier curve
|
2020-11-14 01:16:02 +00:00
|
|
|
{
|
|
|
|
std::vector<wxPoint> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
|
|
|
|
BEZIER_POLY converter( ctrlPoints );
|
|
|
|
std::vector< wxPoint> poly;
|
|
|
|
converter.GetPoly( poly, m_width );
|
2018-07-07 11:04:01 +00:00
|
|
|
|
2020-11-14 01:16:02 +00:00
|
|
|
for( unsigned ii = 1; ii < poly.size(); ii++ )
|
|
|
|
{
|
2021-03-06 09:27:41 +00:00
|
|
|
TransformOvalToPolygon( aCornerBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
|
2018-07-07 11:04:01 +00:00
|
|
|
}
|
2020-11-14 01:16:02 +00:00
|
|
|
}
|
2015-04-09 18:53:36 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-10-04 23:34:59 +00:00
|
|
|
wxFAIL_MSG( "PCB_SHAPE::TransformShapeWithClearanceToPolygon no implementation for "
|
2020-11-13 21:00:28 +00:00
|
|
|
+ PCB_SHAPE_TYPE_T_asString( m_shape ) );
|
2009-11-16 08:13:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
void TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
2020-10-13 10:55:24 +00:00
|
|
|
PCB_LAYER_ID aLayer, int aClearanceValue,
|
|
|
|
int aError, ERROR_LOC aErrorLoc,
|
2019-09-09 12:44:41 +00:00
|
|
|
bool ignoreLineWidth ) const
|
2009-11-14 19:47:52 +00:00
|
|
|
{
|
2018-11-14 23:34:32 +00:00
|
|
|
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for tracks." );
|
|
|
|
|
2020-04-09 08:37:17 +00:00
|
|
|
|
2009-11-14 19:47:52 +00:00
|
|
|
switch( Type() )
|
|
|
|
{
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_VIA_T:
|
2020-09-10 23:05:20 +00:00
|
|
|
{
|
2019-09-09 12:44:41 +00:00
|
|
|
int radius = ( m_Width / 2 ) + aClearanceValue;
|
2020-10-13 10:55:24 +00:00
|
|
|
TransformCircleToPolygon( aCornerBuffer, m_Start, radius, aError, aErrorLoc );
|
2020-09-10 23:05:20 +00:00
|
|
|
}
|
2020-04-09 08:37:17 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_ARC_T:
|
2020-09-10 23:05:20 +00:00
|
|
|
{
|
2020-04-09 08:37:17 +00:00
|
|
|
const ARC* arc = static_cast<const ARC*>( this );
|
2020-09-10 23:05:20 +00:00
|
|
|
int width = m_Width + ( 2 * aClearanceValue );
|
|
|
|
|
2021-03-06 09:27:41 +00:00
|
|
|
TransformArcToPolygon( aCornerBuffer, arc->GetStart(), arc->GetMid(),
|
|
|
|
arc->GetEnd(), width, aError, aErrorLoc );
|
2020-09-10 23:05:20 +00:00
|
|
|
}
|
2009-11-14 19:47:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-09-10 23:05:20 +00:00
|
|
|
{
|
|
|
|
int width = m_Width + ( 2 * aClearanceValue );
|
|
|
|
|
2020-10-13 10:55:24 +00:00
|
|
|
TransformOvalToPolygon( aCornerBuffer, m_Start, m_End, width, aError, aErrorLoc );
|
2020-09-10 23:05:20 +00:00
|
|
|
}
|
2009-11-14 19:47:52 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|
|
|
PCB_LAYER_ID aLayer, int aClearanceValue,
|
|
|
|
int aError, ERROR_LOC aErrorLoc,
|
|
|
|
bool ignoreLineWidth ) const
|
2009-11-14 19:47:52 +00:00
|
|
|
{
|
2018-11-14 23:34:32 +00:00
|
|
|
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for pads." );
|
|
|
|
|
2019-06-17 15:26:15 +00:00
|
|
|
// 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;
|
2020-07-31 12:37:22 +00:00
|
|
|
double angle = m_orient;
|
|
|
|
int dx = m_size.x / 2;
|
|
|
|
int dy = m_size.y / 2;
|
2009-11-14 19:47:52 +00:00
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset,
|
|
|
|
// the pad position is NOT the shape position
|
2009-11-14 19:47:52 +00:00
|
|
|
|
2014-01-26 14:20:58 +00:00
|
|
|
switch( GetShape() )
|
2009-11-14 19:47:52 +00:00
|
|
|
{
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_CIRCLE:
|
|
|
|
case PAD_SHAPE_OVAL:
|
2019-10-25 14:26:10 +00:00
|
|
|
if( dx == dy )
|
2009-11-14 19:47:52 +00:00
|
|
|
{
|
2020-10-13 10:55:24 +00:00
|
|
|
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx + aClearanceValue, aError,
|
|
|
|
aErrorLoc );
|
2009-11-14 19:47:52 +00:00
|
|
|
}
|
2019-10-25 14:26:10 +00:00
|
|
|
else
|
2009-11-14 19:47:52 +00:00
|
|
|
{
|
2020-06-22 19:35:09 +00:00
|
|
|
int half_width = std::min( dx, dy );
|
|
|
|
wxPoint delta( dx - half_width, dy - half_width );
|
|
|
|
|
|
|
|
RotatePoint( &delta, angle );
|
2019-10-25 14:26:10 +00:00
|
|
|
|
2020-06-22 19:35:09 +00:00
|
|
|
TransformOvalToPolygon( aCornerBuffer, padShapePos - delta, padShapePos + delta,
|
2020-10-13 10:55:24 +00:00
|
|
|
( half_width + aClearanceValue ) * 2, aError, aErrorLoc );
|
2012-08-23 19:15:58 +00:00
|
|
|
}
|
2009-11-14 19:47:52 +00:00
|
|
|
|
2012-08-23 19:15:58 +00:00
|
|
|
break;
|
2009-11-14 19:47:52 +00:00
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_TRAPEZOID:
|
|
|
|
case PAD_SHAPE_RECT:
|
2014-11-29 12:58:34 +00:00
|
|
|
{
|
2020-07-31 12:37:22 +00:00
|
|
|
int ddx = GetShape() == PAD_SHAPE_TRAPEZOID ? m_deltaSize.x / 2 : 0;
|
|
|
|
int ddy = GetShape() == PAD_SHAPE_TRAPEZOID ? m_deltaSize.y / 2 : 0;
|
2020-06-22 19:35:09 +00:00
|
|
|
|
2014-11-29 12:58:34 +00:00
|
|
|
wxPoint corners[4];
|
2020-07-31 12:37:21 +00:00
|
|
|
corners[0] = wxPoint( -dx - ddy, dy + ddx );
|
|
|
|
corners[1] = wxPoint( dx + ddy, dy - ddx );
|
|
|
|
corners[2] = wxPoint( dx - ddy, -dy + ddx );
|
|
|
|
corners[3] = wxPoint( -dx + ddy, -dy - ddx );
|
2009-11-14 19:47:52 +00:00
|
|
|
|
2015-07-27 19:45:57 +00:00
|
|
|
SHAPE_POLY_SET outline;
|
|
|
|
outline.NewOutline();
|
2009-11-14 19:47:52 +00:00
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
for( wxPoint& corner : corners )
|
2014-12-01 14:54:33 +00:00
|
|
|
{
|
2020-06-22 19:35:09 +00:00
|
|
|
RotatePoint( &corner, angle );
|
2019-09-09 12:44:41 +00:00
|
|
|
corner += padShapePos;
|
|
|
|
outline.Append( corner.x, corner.y );
|
2014-12-01 14:54:33 +00:00
|
|
|
}
|
2014-11-29 12:58:34 +00:00
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
if( aClearanceValue )
|
|
|
|
{
|
2020-09-10 23:05:20 +00:00
|
|
|
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
|
|
|
|
pad_min_seg_per_circle_count );
|
2020-11-02 17:42:47 +00:00
|
|
|
int clearance = aClearanceValue;
|
|
|
|
|
|
|
|
if( aErrorLoc == ERROR_OUTSIDE )
|
|
|
|
clearance += GetCircleToPolyCorrection( aError );
|
|
|
|
|
2019-09-09 12:44:41 +00:00
|
|
|
outline.Inflate( clearance, numSegs );
|
|
|
|
}
|
2016-04-06 18:15:49 +00:00
|
|
|
|
|
|
|
aCornerBuffer.Append( outline );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-08-29 07:13:07 +00:00
|
|
|
case PAD_SHAPE_CHAMFERED_RECT:
|
2016-04-06 18:15:49 +00:00
|
|
|
case PAD_SHAPE_ROUNDRECT:
|
|
|
|
{
|
2020-10-13 10:55:24 +00:00
|
|
|
int radius = GetRoundRectCornerRadius();
|
2020-07-31 12:37:22 +00:00
|
|
|
wxSize shapesize( m_size );
|
2020-10-13 10:55:24 +00:00
|
|
|
bool doChamfer = GetShape() == PAD_SHAPE_CHAMFERED_RECT;
|
2019-05-14 12:39:34 +00:00
|
|
|
|
2020-11-14 07:50:58 +00:00
|
|
|
double chamferRatio = doChamfer ? GetChamferRectRatio() : 0.0;
|
|
|
|
|
|
|
|
if( aClearanceValue )
|
|
|
|
{
|
|
|
|
radius += aClearanceValue;
|
|
|
|
shapesize.x += aClearanceValue * 2;
|
|
|
|
shapesize.y += aClearanceValue * 2;
|
|
|
|
|
|
|
|
// The chamfer position (the 45 deg line on corner) must be
|
|
|
|
// offsetted by aClearanceValue from the base shape chamfer pos
|
|
|
|
// So we recalculate the chamferRatio to do that
|
|
|
|
//
|
|
|
|
// the chamfered shape is square with widet = w, and a corner dist from center
|
|
|
|
// is w*1.414 / 2 = w*0.707
|
|
|
|
// the distance from corner to chamfer line is ch = chamfer_size/707
|
|
|
|
// the distance from center to chamfer line is
|
|
|
|
// d = w*707 - ch/707
|
|
|
|
// so we have:
|
|
|
|
// base shape: d1 = w1*707 - ch1/707 = 0.707 * ( w1 - w1*chamferRatio)
|
|
|
|
// shape with clearance: d2 = w2*707 - ch2/707 = d1 + aClearanceValue
|
|
|
|
const double rootsq_2 = 1.41421356237/2;
|
|
|
|
int d1 = rootsq_2 * std::min( m_size.x, m_size.y ) * ( 1 - GetChamferRectRatio() );
|
|
|
|
int d2 = d1 + aClearanceValue;
|
|
|
|
// d2 = 0.707 * w2 * ( 1 - chamferRatio2 )
|
|
|
|
// 1 - d2 / ( 0.707 * w2 ) = chamferRatio2
|
|
|
|
chamferRatio = 1.0 - d2 / ( rootsq_2 * std::min( shapesize.x, shapesize.y ) );
|
|
|
|
|
|
|
|
// Ensure chamferRatio = 0.0 ... 0.5
|
|
|
|
if( chamferRatio < 0.0 )
|
|
|
|
chamferRatio = 0.0;
|
|
|
|
|
|
|
|
if( chamferRatio > 0.5 )
|
|
|
|
chamferRatio = 0.5;
|
|
|
|
}
|
2015-07-14 20:23:13 +00:00
|
|
|
|
2019-06-17 12:28:52 +00:00
|
|
|
SHAPE_POLY_SET outline;
|
2019-09-09 12:44:41 +00:00
|
|
|
TransformRoundChamferedRectToPolygon( outline, padShapePos, shapesize, angle, radius,
|
2020-11-14 07:50:58 +00:00
|
|
|
chamferRatio,
|
2020-10-13 10:55:24 +00:00
|
|
|
doChamfer ? GetChamferPositions() : 0,
|
|
|
|
aError, aErrorLoc );
|
2015-07-27 19:45:57 +00:00
|
|
|
|
|
|
|
aCornerBuffer.Append( outline );
|
2014-11-29 12:58:34 +00:00
|
|
|
}
|
2009-11-14 19:47:52 +00:00
|
|
|
break;
|
2017-01-13 17:51:22 +00:00
|
|
|
|
|
|
|
case PAD_SHAPE_CUSTOM:
|
|
|
|
{
|
2020-06-22 19:35:09 +00:00
|
|
|
SHAPE_POLY_SET outline;
|
2020-08-12 21:18:13 +00:00
|
|
|
MergePrimitivesAsPolygon( &outline, aLayer );
|
2020-07-31 12:37:22 +00:00
|
|
|
outline.Rotate( -DECIDEG2RAD( m_orient ) );
|
|
|
|
outline.Move( VECTOR2I( m_pos ) );
|
2020-06-22 19:35:09 +00:00
|
|
|
|
2020-01-11 16:50:18 +00:00
|
|
|
if( aClearanceValue )
|
|
|
|
{
|
2020-09-10 23:05:20 +00:00
|
|
|
int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
|
|
|
|
pad_min_seg_per_circle_count );
|
2020-10-13 10:55:24 +00:00
|
|
|
int clearance = aClearanceValue;
|
|
|
|
|
|
|
|
if( aErrorLoc == ERROR_OUTSIDE )
|
|
|
|
clearance += GetCircleToPolyCorrection( aError );
|
2020-01-11 16:50:18 +00:00
|
|
|
|
|
|
|
outline.Inflate( clearance, numSegs );
|
2020-09-22 21:05:09 +00:00
|
|
|
outline.Simplify( SHAPE_POLY_SET::PM_FAST );
|
|
|
|
outline.Fracture( SHAPE_POLY_SET::PM_FAST );
|
2020-01-11 16:50:18 +00:00
|
|
|
}
|
|
|
|
|
2017-01-13 17:51:22 +00:00
|
|
|
aCornerBuffer.Append( outline );
|
|
|
|
}
|
|
|
|
break;
|
2020-06-30 12:41:59 +00:00
|
|
|
|
|
|
|
default:
|
2020-11-12 22:30:02 +00:00
|
|
|
wxFAIL_MSG( "PAD::TransformShapeWithClearanceToPolygon no implementation for "
|
2020-06-30 12:41:59 +00:00
|
|
|
+ PAD_SHAPE_T_asString( GetShape() ) );
|
|
|
|
break;
|
2009-11-14 19:47:52 +00:00
|
|
|
}
|
|
|
|
}
|
2009-11-16 08:13:40 +00:00
|
|
|
|
2016-04-06 18:15:49 +00:00
|
|
|
|
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
bool PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
|
|
|
|
int aError, ERROR_LOC aErrorLoc ) const
|
2013-05-01 19:01:14 +00:00
|
|
|
{
|
|
|
|
wxSize drillsize = GetDrillSize();
|
|
|
|
|
2014-05-17 19:29:15 +00:00
|
|
|
if( !drillsize.x || !drillsize.y )
|
2013-05-01 19:01:14 +00:00
|
|
|
return false;
|
|
|
|
|
2020-07-04 18:48:22 +00:00
|
|
|
const SHAPE_SEGMENT* seg = GetEffectiveHoleShape();
|
2013-05-01 19:01:14 +00:00
|
|
|
|
2020-09-10 23:05:20 +00:00
|
|
|
TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
|
2020-10-13 10:55:24 +00:00
|
|
|
seg->GetWidth() + aInflateValue * 2, aError, aErrorLoc );
|
2013-05-01 19:01:14 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2009-11-16 08:13:40 +00:00
|
|
|
|
2017-11-29 18:51:01 +00:00
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
void ZONE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|
|
|
PCB_LAYER_ID aLayer, int aClearance, int aError,
|
2021-02-03 23:33:36 +00:00
|
|
|
ERROR_LOC aErrorLoc, bool aIgnoreLineWidth ) const
|
2017-11-29 18:51:01 +00:00
|
|
|
{
|
2021-02-03 23:33:36 +00:00
|
|
|
wxASSERT_MSG( !aIgnoreLineWidth, "IgnoreLineWidth has no meaning for zones." );
|
2020-06-24 02:19:08 +00:00
|
|
|
|
|
|
|
if( !m_FilledPolysList.count( aLayer ) )
|
|
|
|
return;
|
2018-11-14 23:34:32 +00:00
|
|
|
|
2020-06-24 02:19:08 +00:00
|
|
|
aCornerBuffer = m_FilledPolysList.at( aLayer );
|
2020-09-21 19:20:20 +00:00
|
|
|
|
|
|
|
int numSegs = GetArcToSegmentCount( aClearance, aError, 360.0 );
|
|
|
|
aCornerBuffer.Inflate( aClearance, numSegs );
|
2017-11-29 18:51:01 +00:00
|
|
|
aCornerBuffer.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
|
|
|
}
|
2021-02-03 23:33:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
void 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( "DIMENSION::TransformShapeWithClearanceToPolygon unexpected shape type." );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|