Cleanup pad geometry handling.

There were a lot of plotters, exporters, etc. that were rolling their
own implementations.

This also introduces a lazily-built set of SHAPE objects for doing
collision detection and some forms of rendering (and later DRC).
This commit is contained in:
Jeff Young 2020-06-22 20:35:09 +01:00
parent c57c21f577
commit d01b29ab37
28 changed files with 732 additions and 2355 deletions

View File

@ -603,21 +603,10 @@ class BOARD_ADAPTER
SHAPE_POLY_SET &aCornerBuffer,
int aWidth) const;
void transformPadsShapesWithClearanceToPolygon( const PADS &aPads,
PCB_LAYER_ID aLayer,
SHAPE_POLY_SET &aCornerBuffer,
int aInflateValue,
bool aSkipNPTHPadsWihNoCopper) const;
void transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule,
PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer ) const;
void buildPadShapePolygon( const D_PAD *aPad,
SHAPE_POLY_SET &aCornerBuffer,
wxSize aInflateValue ) const;
public:
SFVEC3D m_BgColorBot; ///< background bottom color
SFVEC3D m_BgColorTop; ///< background top color

View File

@ -33,7 +33,6 @@
#include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h"
#include <board_adapter.h>
#include <class_board.h>
@ -45,13 +44,15 @@
#include <class_text_mod.h>
#include <convert_basic_shapes_to_polygon.h>
#include <trigo.h>
#include <geometry/shape_segment.h>
#include <geometry/geometry_utils.h>
#include <geometry/shape_circle.h>
#include <geometry/shape_simple.h>
#include <gr_text.h>
#include <utility>
#include <vector>
// These variables are parameters used in addTextSegmToContainer.
// But addTextSegmToContainer is a call-back function,
// so we cannot send them as arguments.
@ -310,261 +311,89 @@ void BOARD_ADAPTER::createNewTrack( const TRACK* aTrack, CGENERICCONTAINER2D *aD
}
// Based on:
// void D_PAD:: TransformShapeWithClearanceToPolygon(
// board_items_to_polygon_shape_transform.cpp
void BOARD_ADAPTER::createNewPadWithClearance( const D_PAD* aPad,
CGENERICCONTAINER2D *aDstContainer,
wxSize aClearanceValue ) const
{
// note: for most of shapes, aClearanceValue.x = aClearanceValue.y
// only rectangular and oval shapes can have different values
// when drawn on the solder paste layer, because we can have a margin that is a
// percent of pad size
const int dx = (aPad->GetSize().x / 2) + aClearanceValue.x;
const int dy = (aPad->GetSize().y / 2) + aClearanceValue.y;
SHAPE_POLY_SET poly;
if( !dx || !dy )
if( aClearanceValue.x != aClearanceValue.y )
{
wxLogTrace( m_logTrace,
wxT( "BOARD_ADAPTER::createNewPadWithClearance - found an invalid pad" ) );
return;
}
wxPoint PadShapePos = aPad->ShapePos(); // Note: for pad having a shape offset,
// the pad position is NOT the shape position
switch( aPad->GetShape() )
{
case PAD_SHAPE_CIRCLE:
{
const float radius = dx * m_biuTo3Dunits;
const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits,
-PadShapePos.y * m_biuTo3Dunits );
aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) );
}
break;
case PAD_SHAPE_OVAL:
{
if( dx == dy )
{
// The segment object cannot store start and end the same position,
// so add a circle instead
const float radius = dx * m_biuTo3Dunits;
const SFVEC2F center( PadShapePos.x * m_biuTo3Dunits,
-PadShapePos.y * m_biuTo3Dunits );
aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius, *aPad ) );
// Our shape-based builder can't handle differing x:y clearance values (which
// get generated when relative paste margin is used with an oblong pad). So
// we fake a larger pad and run the general-purpose polygon builder on it.
D_PAD dummy( *aPad );
dummy.SetSize( aPad->GetSize() + aClearanceValue + aClearanceValue );
dummy.TransformShapeWithClearanceToPolygon( poly, 0 );
}
else
{
// An oval pad has the same shape as a segment with rounded ends
int iwidth;
wxPoint shape_offset = wxPoint( 0, 0 );
if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis
for( const std::shared_ptr<SHAPE>& shape : aPad->GetEffectiveShapes() )
{
shape_offset.y = dy - dx;
iwidth = dx * 2;
}
else //if( dy < dx )
switch( shape->Type() )
{
shape_offset.x = dy - dx;
iwidth = dy * 2;
}
RotatePoint( &shape_offset, aPad->GetOrientation() );
const wxPoint start = PadShapePos - shape_offset;
const wxPoint end = PadShapePos + shape_offset;
const SFVEC2F start3DU( start.x * m_biuTo3Dunits, -start.y * m_biuTo3Dunits );
const SFVEC2F end3DU ( end.x * m_biuTo3Dunits, -end.y * m_biuTo3Dunits );
case SH_SEGMENT:
{
const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape.get();
const SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits,
-seg->GetSeg().A.y * m_biuTo3Dunits );
const SFVEC2F end3DU ( seg->GetSeg().B.x * m_biuTo3Dunits,
-seg->GetSeg().B.y * m_biuTo3Dunits );
const int width = seg->GetWidth() + aClearanceValue.x * 2;
// Cannot add segments that have the same start and end point
if( Is_segment_a_circle( start3DU, end3DU ) )
{
aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU,
(iwidth / 2) * m_biuTo3Dunits,
( width / 2) * m_biuTo3Dunits,
*aPad ) );
}
else
{
aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU,
iwidth * m_biuTo3Dunits,
width * m_biuTo3Dunits,
*aPad ) );
}
}
}
break;
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_RECT:
case SH_CIRCLE:
{
// see pcbnew/board_items_to_polygon_shape_transform.cpp
wxPoint corners[4];
bool drawOutline;
const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape.get();
const int radius = circle->GetRadius() + aClearanceValue.x;
const SFVEC2F center( circle->GetCenter().x * m_biuTo3Dunits,
-circle->GetCenter().y * m_biuTo3Dunits );
// For aClearanceValue.x == aClearanceValue.y and > 0 we use the pad shape
// and draw outlines with thicknes = aClearanceValue.
// Otherwise we draw only the inflated/deflated shape
if( aClearanceValue.x > 0 && aClearanceValue.x == aClearanceValue.y )
{
drawOutline = true;
aPad->BuildPadPolygon( corners, wxSize( 0, 0 ), aPad->GetOrientation() );
}
else
{
drawOutline = false;
aPad->BuildPadPolygon( corners, aClearanceValue, aPad->GetOrientation() );
}
SFVEC2F corners3DU[4];
// Note: for pad having a shape offset,
// the pad position is NOT the shape position
for( unsigned int ii = 0; ii < 4; ++ii )
{
corners[ii] += aPad->ShapePos(); // Shift origin to position
corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits,
-corners[ii].y * m_biuTo3Dunits );
}
// Learn more at:
// https://lists.launchpad.net/kicad-developers/msg18729.html
// Add the PAD polygon
aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0],
corners3DU[1],
corners3DU[2],
corners3DU[3],
*aPad ) );
// Add the PAD contours
// Round segments cannot have 0-length elements, so we approximate them
// as a small circle
if( drawOutline )
{
for( int i = 1; i <= 4; i++ )
{
if( Is_segment_a_circle( corners3DU[i - 1], corners3DU[i & 3] ) )
{
aDstContainer->Add( new CFILLEDCIRCLE2D( corners3DU[i - 1],
aClearanceValue.x * m_biuTo3Dunits,
*aPad ) );
}
else
{
aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[i - 1],
corners3DU[i & 3],
aClearanceValue.x * 2.0f * m_biuTo3Dunits,
*aPad ) );
}
}
}
aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius * m_biuTo3Dunits, *aPad ) );
}
break;
case PAD_SHAPE_ROUNDRECT:
{
wxSize shapesize( aPad->GetSize() );
shapesize.x += aClearanceValue.x * 2;
shapesize.y += aClearanceValue.y * 2;
int rounding_radius = aPad->GetRoundRectCornerRadius( shapesize );
wxPoint corners[4];
GetRoundRectCornerCenters( corners,
rounding_radius,
PadShapePos,
shapesize,
aPad->GetOrientation() );
SFVEC2F corners3DU[4];
for( unsigned int ii = 0; ii < 4; ++ii )
corners3DU[ii] = SFVEC2F( corners[ii].x * m_biuTo3Dunits,
-corners[ii].y * m_biuTo3Dunits );
// Add the PAD polygon (For some reason the corners need
// to be inverted to display with the correctly orientation)
aDstContainer->Add( new CPOLYGON4PTS2D( corners3DU[0],
corners3DU[3],
corners3DU[2],
corners3DU[1],
*aPad ) );
// Add the PAD contours
// Round segments cannot have 0-length elements, so we approximate them
// as a small circle
for( int i = 1; i <= 4; i++ )
{
if( Is_segment_a_circle( corners3DU[i - 1], corners3DU[i & 3] ) )
{
aDstContainer->Add( new CFILLEDCIRCLE2D( corners3DU[i - 1],
rounding_radius * m_biuTo3Dunits,
*aPad ) );
}
else
{
aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[i - 1],
corners3DU[i & 3],
rounding_radius * 2.0f * m_biuTo3Dunits,
*aPad ) );
}
}
}
case SH_SIMPLE:
poly.AddOutline( static_cast<SHAPE_SIMPLE*>( shape.get() )->Vertices() );
break;
case PAD_SHAPE_CHAMFERED_RECT:
{
wxSize shapesize( aPad->GetSize() );
shapesize.x += aClearanceValue.x * 2;
shapesize.y += aClearanceValue.y * 2;
SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates
int corner_radius = aPad->GetRoundRectCornerRadius( shapesize );
TransformRoundChamferedRectToPolygon( polyList, PadShapePos, shapesize, aPad->GetOrientation(),
corner_radius, aPad->GetChamferRectRatio(),
aPad->GetChamferPositions(), ARC_HIGH_DEF );
// Add the PAD polygon
Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad );
}
case SH_POLY_SET:
poly = *(SHAPE_POLY_SET*) shape.get();
break;
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates
polyList.Append( aPad->GetCustomShapeAsPolygon() );
aPad->CustomShapeAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() );
default:
wxFAIL_MSG( "BOARD_ADAPTER::createNewPadWithClearance unimplemented shape" );
break;
}
}
}
if( !poly.IsEmpty() )
{
if( aClearanceValue.x )
polyList.Inflate( aClearanceValue.x, 32 );
poly.Inflate( aClearanceValue.x, 32 );
// Add the PAD polygon
Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad );
}
break;
Convert_shape_line_polygon_to_triangles( poly, *aDstContainer, m_biuTo3Dunits, *aPad );
}
}
// Based on:
// BuildPadDrillShapePolygon
// board_items_to_polygon_shape_transform.cpp
COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValue )
{
wxSize drillSize = aPad->GetDrillSize();
@ -587,30 +416,17 @@ COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValu
}
else // Oblong hole
{
wxPoint start, end;
int width;
const std::shared_ptr<SHAPE_SEGMENT>& seg = aPad->GetEffectiveHoleShape();
float width = seg->GetWidth() + aInflateValue * 2;
aPad->GetOblongGeometry( aPad->GetDrillSize(), &start, &end, &width );
SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits,
-seg->GetSeg().A.y * m_biuTo3Dunits );
width += aInflateValue * 2;
start += aPad->GetPosition();
end += aPad->GetPosition();
SFVEC2F end3DU ( seg->GetSeg().B.x * m_biuTo3Dunits,
-seg->GetSeg().B.y * m_biuTo3Dunits );
SFVEC2F start3DU( start.x * m_biuTo3Dunits,
-start.y * m_biuTo3Dunits );
SFVEC2F end3DU ( end.x * m_biuTo3Dunits,
-end.y * m_biuTo3Dunits );
if( Is_segment_a_circle( start3DU, end3DU ) )
{
return new CFILLEDCIRCLE2D( start3DU, (width / 2) * m_biuTo3Dunits, *aPad );
}
else
{
return new CROUNDSEGMENT2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad );
}
}
return NULL;
}
@ -951,14 +767,12 @@ void BOARD_ADAPTER::AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneC
float radius = line_thickness/2;
if( radius > 0.0 ) // degenerated circles crash 3D viewer
aDstContainer->Add(
new CFILLEDCIRCLE2D( start3DU, radius,
aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, radius,
*aZoneContainer ) );
}
else
{
aDstContainer->Add(
new CROUNDSEGMENT2D( start3DU, end3DU, line_thickness,
aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, line_thickness,
*aZoneContainer ) );
}
}
@ -986,12 +800,9 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad,
return;
}
// For other shapes, draw polygon outlines
// For other shapes, add outlines as thick segments in polygon buffer
SHAPE_POLY_SET corners;
aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ) );
// Add outlines as thick segments in polygon buffer
aPad->TransformShapeWithClearanceToPolygon( corners, 0 );
const SHAPE_LINE_CHAIN& path = corners.COutline( 0 );
@ -1005,13 +816,13 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad,
if( Is_segment_a_circle( start3DU, end3DU ) )
{
aDstContainer->Add(
new CFILLEDCIRCLE2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits, *aPad ) );
aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
*aPad ) );
}
else
{
aDstContainer->Add(
new CROUNDSEGMENT2D( start3DU, end3DU, aWidth * m_biuTo3Dunits, *aPad ) );
aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
*aPad ) );
}
}
}

View File

@ -33,13 +33,7 @@
#include "board_adapter.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/croundsegment2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h"
#include "../3d_rendering/3d_render_raytracing/accelerators/ccontainer2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h"
#include "../3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h"
#include <class_board.h>
#include <class_module.h>
@ -47,16 +41,17 @@
#include <class_pcb_text.h>
#include <class_edge_mod.h>
#include <class_zone.h>
#include <class_text_mod.h>
#include <convert_basic_shapes_to_polygon.h>
#include <trigo.h>
#include <utility>
#include <vector>
#include <thread>
#include <algorithm>
#include <atomic>
#ifdef PRINT_STATISTICS_3D_VIEWER
#include <profile.h>
#endif
void BOARD_ADAPTER::destroyLayers()
{
@ -501,13 +496,13 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED )
{
pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly, inflate );
pad->BuildPadDrillShapePolygon( m_through_inner_holes_poly, 0 );
pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly, inflate );
pad->TransformHoleWithClearanceToPolygon( m_through_inner_holes_poly, 0 );
}
else
{
// If not plated, no copper.
pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly_NPTH, inflate );
pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly_NPTH, inflate );
}
}
}
@ -568,8 +563,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
// Note: NPTH pads are not drawn on copper layers when the pad
// has same shape as its hole
transformPadsShapesWithClearanceToPolygon( module->Pads(),
curr_layer_id,
module->TransformPadsShapesWithClearanceToPolygon( curr_layer_id,
*layerPoly,
0,
true );
@ -983,8 +977,8 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
}
else
{
transformPadsShapesWithClearanceToPolygon(
module->Pads(), curr_layer_id, *layerPoly, 0, false );
module->TransformPadsShapesWithClearanceToPolygon( curr_layer_id, *layerPoly, 0,
false );
}
// On tech layers, use a poor circle approximation, only for texts (stroke font)

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2020 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
@ -25,9 +25,7 @@
/**
* @file create_layer_poly.cpp
* @brief This file implements the creation of the pcb board items in the poly
* contours format. It is based on the function found in the files:
* board_items_to_polygon_shape_transform.cpp
* board_items_to_polygon_shape_transform.cpp
* contours format.
*/
#include "board_adapter.h"
@ -36,93 +34,20 @@
#include <class_module.h>
// This is the same function as in board_items_to_polygon_shape_transform.cpp
// but it adds the rect/trapezoid shapes with a different winding
void BOARD_ADAPTER::buildPadShapePolygon( const D_PAD* aPad,
SHAPE_POLY_SET& aCornerBuffer,
wxSize aInflateValue ) const
{
wxPoint PadShapePos = aPad->ShapePos(); /* Note: for pad having a shape offset,
* the pad position is NOT the shape position */
switch( aPad->GetShape() )
{
case PAD_SHAPE_CIRCLE:
case PAD_SHAPE_OVAL:
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_CHAMFERED_RECT:
{
// We are using TransformShapeWithClearanceToPolygon to build the shape.
// Currently, this method uses only the same inflate value for X and Y dirs.
// so because here this is not the case, we use a inflated dummy pad to build
// the polygonal shape
// TODO: remove this dummy pad when TransformShapeWithClearanceToPolygon will use
// a wxSize to inflate the pad size
D_PAD dummy( *aPad );
wxSize new_size = aPad->GetSize() + aInflateValue + aInflateValue;
dummy.SetSize( new_size );
dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 );
}
break;
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_RECT:
{
wxPoint corners[4];
SHAPE_LINE_CHAIN aLineChain;
aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() );
for( int ii = 0; ii < 4; ++ii )
{
corners[3-ii] += PadShapePos; // Shift origin to position
aLineChain.Append( corners[3-ii].x, corners[3-ii].y );
}
aLineChain.SetClosed( true );
aCornerBuffer.AddOutline( aLineChain );
}
break;
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates
auto inflate_val = std::max( aInflateValue.x, aInflateValue.y );
polyList.Append( aPad->GetCustomShapeAsPolygon() );
aPad->CustomShapeAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() );
if( inflate_val > 0 )
{
int numSegs = GetNrSegmentsCircle( inflate_val );
polyList.Inflate( inflate_val, numSegs );
}
aCornerBuffer.Append( polyList );
}
break;
}
}
void BOARD_ADAPTER::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
SHAPE_POLY_SET& aCornerBuffer,
int aWidth ) const
{
if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring
{
TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(),
aPad->GetSize().x / 2, ARC_HIGH_DEF, aWidth );
TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), aPad->GetSize().x / 2,
ARC_HIGH_DEF, aWidth );
return;
}
// For other shapes, draw polygon outlines
// For other shapes, add outlines as thick segments in polygon buffer
SHAPE_POLY_SET corners;
buildPadShapePolygon( aPad, corners, wxSize( 0, 0 ) );
// Add outlines as thick segments in polygon buffer
aPad->TransformShapeWithClearanceToPolygon( corners, 0 );
const SHAPE_LINE_CHAIN& path = corners.COutline( 0 );
@ -131,94 +56,24 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
const VECTOR2I& a = path.CPoint( ii );
const VECTOR2I& b = path.CPoint( ii + 1 );
TransformSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ),
wxPoint( b.x, b.y ), ARC_HIGH_DEF, aWidth );
TransformSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ),
ARC_HIGH_DEF, aWidth );
}
}
// Based on the same function name in board_items_to_polyshape_transform.cpp
// It was implemented here to allow dynamic segments count per pad shape
void BOARD_ADAPTER::transformPadsShapesWithClearanceToPolygon( const PADS& aPads, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer,
int aInflateValue,
bool aSkipNPTHPadsWihNoCopper ) const
{
wxSize margin;
for( auto pad : aPads )
{
if( !pad->IsOnLayer(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_HOLE_NOT_PLATED) )
{
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;
}
}
}
switch( aLayer )
{
case F_Mask:
case B_Mask:
margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue;
break;
case F_Paste:
case B_Paste:
margin = pad->GetSolderPasteMargin();
margin.x += aInflateValue;
margin.y += aInflateValue;
break;
default:
margin.x = margin.y = aInflateValue;
break;
}
buildPadShapePolygon( pad, aCornerBuffer, margin );
}
}
void BOARD_ADAPTER::transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule,
PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer ) const
{
for( auto item : aModule->GraphicalItems() )
for( BOARD_ITEM* item : aModule->GraphicalItems() )
{
switch( item->Type() )
if( item->Type() == PCB_MODULE_EDGE_T )
{
case PCB_MODULE_EDGE_T:
{
EDGE_MODULE*outline = (EDGE_MODULE*) item;
if( outline->GetLayer() != aLayer )
break;
EDGE_MODULE* outline = (EDGE_MODULE*) item;
if( outline->GetLayer() == aLayer )
outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 );
}
break;
default:
break;
}
}
}

View File

@ -817,10 +817,10 @@ void C3D_RENDER_OGL_LEGACY::generate_3D_Vias_and_Pads()
double correctionFactor = m_boardAdapter.GetCircleCorrectionFactor( nrSegments );
int correction = radius * ( correctionFactor - 1 );
pad->BuildPadDrillShapePolygon(
tht_outer_holes_poly, copperThickness + correction );
pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly,
copperThickness + correction );
pad->BuildPadDrillShapePolygon( tht_inner_holes_poly, correction );
pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, correction );
}
}
}

View File

@ -110,8 +110,8 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint&
* 4 = BOTTOM_LEFT
* 8 = BOTTOM_RIGHT
* One can have more than one chamfered corner by ORing the corner identifers
* @param aApproxErrorMax = the IU allowed for error in approximation
* @param aMinSegPerCircleCount = the minimal segments per circle count in approximation
* @param aError = the IU allowed for error in approximation
* @param aSegsPerCircle = the minimal segments per circle count in approximation
* (aApproxErrorMax can generate must more seg count than aMinSegPerCircleCount)
* To allow a reasonable good shape even for very small shapes, the min count is 16
* (must be a multiple of 4 becauseusually arcs are 90 deg.
@ -119,8 +119,7 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint&
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer,
const wxPoint& aPosition, const wxSize& aSize,
double aRotation, int aCornerRadius,
double aChamferRatio, int aChamferCorners,
int aApproxErrorMax, int aMinSegPerCircleCount = 16 );
double aChamferRatio, int aChamferCorners, int aError );
/**
* Function TransformRoundedEndsSegmentToPolygon

View File

@ -5,7 +5,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2020 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
@ -38,8 +38,7 @@
#include <trigo.h>
void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aBuffer,
wxPoint aCenter, int aRadius,
void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aCornerBuffer, wxPoint aCenter, int aRadius,
int aError )
{
wxPoint corner_position;
@ -56,10 +55,10 @@ void TransformCircleToPolygon( SHAPE_LINE_CHAIN& aBuffer,
double angle = (ii * delta) + halfstep;
RotatePoint( &corner_position, angle );
corner_position += aCenter;
aBuffer.Append( corner_position.x, corner_position.y );
aCornerBuffer.Append( corner_position.x, corner_position.y );
}
aBuffer.SetClosed( true );
aCornerBuffer.SetClosed( true );
}
@ -192,8 +191,8 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
}
void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius,
const wxPoint& aPosition, const wxSize& aSize, double aRotation )
void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint& aPosition,
const wxSize& aSize, double aRotation )
{
wxSize size( aSize/2 );
@ -202,25 +201,16 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius,
// Ensure size is > 0, to avoid generating unusable shapes
// which can crash kicad.
if( size.x <= 1 )
size.x = 1;
if( size.y <= 1 )
size.y = 1;
size.x = std::max( 1, size.x );
size.y = std::max( 1, size.y );
aCenters[0].x = -size.x;
aCenters[0].y = size.y;
aCenters[1].x = size.x;
aCenters[1].y = size.y;
aCenters[2].x = size.x;
aCenters[2].y = -size.y;
aCenters[3].x = -size.x;
aCenters[3].y = -size.y;
aCenters[0] = wxPoint( -size.x, size.y );
aCenters[1] = wxPoint( size.x, size.y );
aCenters[2] = wxPoint( size.x, -size.y );
aCenters[3] = wxPoint( -size.x, -size.y );
// Rotate the polygon
if( aRotation )
if( aRotation != 0.0 )
{
for( int ii = 0; ii < 4; ii++ )
RotatePoint( &aCenters[ii], aRotation );
@ -232,11 +222,10 @@ void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius,
}
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer,
const wxPoint& aPosition, const wxSize& aSize,
double aRotation, int aCornerRadius,
double aChamferRatio, int aChamferCorners,
int aApproxErrorMax, int aMinSegPerCircleCount )
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition,
const wxSize& aSize, double aRotation,
int aCornerRadius, double aChamferRatio,
int aChamferCorners, int aError )
{
// Build the basic shape in orientation 0.0, position 0,0 for chamfered corners
// or in actual position/orientation for round rect only
@ -248,11 +237,12 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer,
SHAPE_POLY_SET outline;
outline.NewOutline();
for( int ii = 0; ii < 4; ++ii )
outline.Append( corners[ii].x, corners[ii].y );
for( const wxPoint& corner : corners)
outline.Append( corner );
int numSegs = std::max( GetArcToSegmentCount( aCornerRadius, aApproxErrorMax, 360.0 ),
aMinSegPerCircleCount );
// These are small radius corners (of which there may be many), so peg the segs-per-circle
// to no more than 16.
int numSegs = std::max( GetArcToSegmentCount( aCornerRadius, aError, 360.0 ), 16 );
outline.Inflate( aCornerRadius, numSegs );
if( aChamferCorners == RECT_NO_CHAMFER ) // no chamfer
@ -322,8 +312,7 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer,
}
void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
wxPoint aStart, wxPoint aEnd,
void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aEnd,
int aError, int aWidth )
{
int radius = aWidth / 2;
@ -392,9 +381,8 @@ void TransformSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
}
void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
wxPoint aCentre, wxPoint aStart, double aArcAngle,
int aError, int aWidth )
void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, wxPoint aStart,
double aArcAngle, int aError, int aWidth )
{
wxPoint arc_start, arc_end;
int dist = EuclideanNorm( aCentre - aStart );
@ -404,9 +392,7 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
arc_end = arc_start = aStart;
if( aArcAngle != 3600 )
{
RotatePoint( &arc_end, aCentre, -aArcAngle );
}
if( aArcAngle < 0 )
{
@ -422,8 +408,7 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
{
curr_end = arc_start;
RotatePoint( &curr_end, aCentre, -ii );
TransformSegmentToPolygon( aCornerBuffer, curr_start, curr_end, aError,
aWidth );
TransformSegmentToPolygon( aCornerBuffer, curr_start, curr_end, aError, aWidth );
curr_start = curr_end;
}
@ -432,12 +417,10 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
}
void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer,
wxPoint aCentre, int aRadius,
void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aCentre, int aRadius,
int aError, int aWidth )
{
// Compute the corners positions and creates the poly
wxPoint curr_point;
int inner_radius = aRadius - ( aWidth / 2 );
int outer_radius = inner_radius + aWidth;

View File

@ -40,8 +40,10 @@
#include <class_edge_mod.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/geometry_utils.h>
#include <geometry/shape_segment.h>
#include <math/util.h> // for KiROUND
// A helper struct for the callback function
// These variables are parameters used in addTextSegmToPoly.
// But addTextSegmToPoly is a call-back function,
@ -519,8 +521,8 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
// 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) + aClearanceValue;
int dy = (m_Size.y / 2) + aClearanceValue;
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
@ -528,35 +530,20 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
switch( GetShape() )
{
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError );
break;
case PAD_SHAPE_OVAL:
// If the oval is actually a circle (same x/y size), treat it the same
if( dx == dy )
{
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError );
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx + aClearanceValue, aError );
}
else
{
int width;
wxPoint shape_offset;
if( dy > dx ) // Oval pad X/Y ratio for choosing translation axis
{
shape_offset.y = dy - dx;
width = dx * 2;
}
else //if( dy <= dx )
{
shape_offset.x = dy - dx;
width = dy * 2;
}
int half_width = std::min( dx, dy );
wxPoint delta( dx - half_width, dy - half_width );
RotatePoint( &shape_offset, angle );
wxPoint start = padShapePos - shape_offset;
wxPoint end = padShapePos + shape_offset;
TransformOvalToPolygon( aCornerBuffer, start, end, width, aError );
RotatePoint( &delta, angle );
TransformOvalToPolygon( aCornerBuffer, padShapePos - delta, padShapePos + delta,
half_width * 2 + aClearanceValue, aError );
}
break;
@ -564,14 +551,21 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
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;
wxPoint corners[4];
BuildPadPolygon( corners, wxSize( 0, 0 ), angle );
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 );
SHAPE_POLY_SET outline;
outline.NewOutline();
for( wxPoint& corner : corners )
{
RotatePoint( &corner, angle );
corner += padShapePos;
outline.Append( corner.x, corner.y );
}
@ -615,9 +609,11 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET outline; // Will contain the corners in board coordinates
outline.Append( m_customShapeAsPolygon );
CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() );
SHAPE_POLY_SET outline;
MergePrimitivesAsPolygon( &outline );
outline.Rotate( -DECIDEG2RAD( m_Orient ) );
outline.Move( VECTOR2I( m_Pos ) );
// TODO: do we need the Simplify() & Fracture() if we're not inflating?
outline.Simplify( SHAPE_POLY_SET::PM_FAST );
@ -640,66 +636,7 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
/*
* Function BuildPadShapePolygon
* Build the corner list of the polygonal shape, depending on shape, clearance and orientation
* Note: for round & oval pads this function is equivalent to TransformShapeWithClearanceToPolygon,
* but not for other shapes
*/
void D_PAD::BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue,
int aError ) const
{
switch( GetShape() )
{
case PAD_SHAPE_CIRCLE:
case PAD_SHAPE_OVAL:
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_CHAMFERED_RECT:
{
// We are using TransformShapeWithClearanceToPolygon to build the shape.
// Currently, this method uses only the same inflate value for X and Y dirs.
// so because here this is not the case, we use a inflated dummy pad to build
// the polygonal shape
// TODO: remove this dummy pad when TransformShapeWithClearanceToPolygon will use
// a wxSize to inflate the pad size
D_PAD dummy( *this );
dummy.SetSize( GetSize() + aInflateValue + aInflateValue );
dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 );
}
break;
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_RECT:
{
wxPoint corners[4];
wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset,
// the pad position is NOT the shape position
aCornerBuffer.NewOutline();
BuildPadPolygon( corners, aInflateValue, m_Orient );
for( wxPoint& corner : corners )
{
corner += padShapePos; // Shift origin to position
aCornerBuffer.Append( corner.x, corner.y );
}
}
break;
case PAD_SHAPE_CUSTOM:
{
// For a custom shape, that is in fact a polygon (with holes), we use only a single
// inflate value (different values for X and Y have no definition for a custom pad).
int inflate = ( aInflateValue.x + aInflateValue.y ) / 2;
TransformShapeWithClearanceToPolygon( aCornerBuffer, inflate );
}
break;
}
}
bool D_PAD::BuildPadDrillShapePolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
bool D_PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
int aError ) const
{
wxSize drillsize = GetDrillSize();
@ -707,24 +644,10 @@ bool D_PAD::BuildPadDrillShapePolygon( SHAPE_POLY_SET& aCornerBuffer, int aInfla
if( !drillsize.x || !drillsize.y )
return false;
if( drillsize.x == drillsize.y ) // usual round hole
{
int radius = ( drillsize.x / 2 ) + aInflateValue;
TransformCircleToPolygon( aCornerBuffer, GetPosition(), radius, aError );
}
else // Oblong hole
{
wxPoint start, end;
int width;
const std::shared_ptr<SHAPE_SEGMENT>& seg = GetEffectiveHoleShape();
GetOblongGeometry( GetDrillSize(), &start, &end, &width );
start += GetPosition();
end += GetPosition();
width += aInflateValue * 2;
TransformSegmentToPolygon( aCornerBuffer, start, end, aError, width );
}
TransformSegmentToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
aError, seg->GetWidth() + aInflateValue * 2 );
return true;
}

View File

@ -243,15 +243,8 @@ public:
double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; }
void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; }
void SetZoneConnection( ZONE_CONNECTION aType )
{
m_ZoneConnection = aType;
}
ZONE_CONNECTION GetZoneConnection() const
{
return m_ZoneConnection;
}
void SetZoneConnection( ZONE_CONNECTION aType ) { m_ZoneConnection = aType; }
ZONE_CONNECTION GetZoneConnection() const { return m_ZoneConnection; }
void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; }
int GetThermalWidth() const { return m_ThermalWidth; }

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,8 @@
class DRAWSEGMENT;
class PARAM_CFG;
class SHAPE;
class SHAPE_SEGMENT;
enum CUST_PAD_SHAPE_IN_ZONE
{
@ -52,7 +54,6 @@ class EDA_3D_CANVAS;
class MODULE;
class EDGE_MODULE;
class TRACK;
class MSG_PANEL_INFO;
namespace KIGFX
{
@ -115,10 +116,6 @@ public:
class D_PAD : public BOARD_CONNECTED_ITEM
{
public:
static int m_PadSketchModePenSize; ///< Pen size used to draw pads in sketch mode
///< (mode used to print pads on silkscreen layer)
public:
D_PAD( MODULE* parent );
@ -181,34 +178,14 @@ public:
* Set the pad name (sometimes called pad number, although
* it can be an array reference like AA12).
*/
void SetName( const wxString& aName )
{
m_name = aName;
}
void SetName( const wxString& aName ) { m_name = aName; }
const wxString& GetName() const { return m_name; }
/**
* Set the pad function (pin name in schematic)
*/
void SetPinFunction( const wxString& aName )
{
m_pinFunction = aName;
}
/**
* @return the pad name
*/
const wxString& GetName() const
{
return m_name;
}
/**
* @return the pad function (pin name in schematic)
*/
const wxString& GetPinFunction() const
{
return m_pinFunction;
}
void SetPinFunction( const wxString& aName ) { m_pinFunction = aName; }
const wxString& GetPinFunction() const { return m_pinFunction; }
bool PadNameEqual( const D_PAD* other ) const
{
@ -219,8 +196,8 @@ public:
* Function GetShape
* @return the shape of this pad.
*/
void SetShape( PAD_SHAPE_T aShape ) { m_padShape = aShape; m_shapesDirty = true; }
PAD_SHAPE_T GetShape() const { return m_padShape; }
void SetShape( PAD_SHAPE_T aShape ) { m_padShape = aShape; m_boundingRadius = -1; }
void SetPosition( const wxPoint& aPos ) override { m_Pos = aPos; }
const wxPoint GetPosition() const override { return m_Pos; }
@ -259,7 +236,7 @@ public:
void SetAnchorPadShape( PAD_SHAPE_T aShape )
{
m_anchorPadShape = ( aShape == PAD_SHAPE_RECT ) ? PAD_SHAPE_RECT : PAD_SHAPE_CIRCLE;
m_boundingRadius = -1;
m_shapesDirty = true;
}
/**
@ -272,8 +249,8 @@ public:
return ( GetLayerSet() & LSET::AllCuMask() ) != 0;
}
void SetY( int y ) { m_Pos.y = y; }
void SetX( int x ) { m_Pos.x = x; }
void SetY( int y ) { m_Pos.y = y; m_shapesDirty = true; }
void SetX( int x ) { m_Pos.x = x; m_shapesDirty = true; }
void SetPos0( const wxPoint& aPos ) { m_Pos0 = aPos; }
const wxPoint& GetPos0() const { return m_Pos0; }
@ -281,16 +258,16 @@ public:
void SetY0( int y ) { m_Pos0.y = y; }
void SetX0( int x ) { m_Pos0.x = x; }
void SetSize( const wxSize& aSize ) { m_Size = aSize; m_boundingRadius = -1; }
void SetSize( const wxSize& aSize ) { m_Size = aSize; m_shapesDirty = true; }
const wxSize& GetSize() const { return m_Size; }
void SetDelta( const wxSize& aSize ) { m_DeltaSize = aSize; m_boundingRadius = -1; }
void SetDelta( const wxSize& aSize ) { m_DeltaSize = aSize; m_shapesDirty = true; }
const wxSize& GetDelta() const { return m_DeltaSize; }
void SetDrillSize( const wxSize& aSize ) { m_Drill = aSize; }
void SetDrillSize( const wxSize& aSize ) { m_Drill = aSize; m_shapesDirty = true; }
const wxSize& GetDrillSize() const { return m_Drill; }
void SetOffset( const wxPoint& aOffset ) { m_Offset = aOffset; }
void SetOffset( const wxPoint& aOffset ) { m_Offset = aOffset; m_shapesDirty = true; }
const wxPoint& GetOffset() const { return m_Offset; }
/**
@ -303,67 +280,35 @@ public:
* a arc
* a curve
*/
void AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness,
bool aMergePrimitives = true );
void AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness,
bool aMergePrimitives = true );
void AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
bool aMergePrimitives = true );
void AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness,
bool aMergePrimitives = true ); ///< ring or circle basic shape
void AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
bool aMergePrimitives = true );
void AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness );
void AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness );
void AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness );
void AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness );
void AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness );
void AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
int aThickness, bool aMergePrimitives = true );
int aThickness );
void AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1,
const wxPoint& aCtrl2, int aThickness, bool aMergePrimitives = true );
const wxPoint& aCtrl2, int aThickness );
bool GetBestAnchorPosition( VECTOR2I& aPos );
/**
* Merge all basic shapes, converted to a polygon in one polygon,
* in m_customShapeAsPolygon
* @return true if OK, false in there is more than one polygon
* in m_customShapeAsPolygon
* @param aMergedPolygon = the SHAPE_POLY_SET to fill.
* if NULL, m_customShapeAsPolygon is the target
* @param aCircleToSegmentsCount = number of segment to approximate a circle
* (default = 32)
* Merge all basic shapes to a SHAPE_POLY_SET
* Note: The corners coordinates are relative to the pad position, orientation 0,
*/
bool MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon = NULL );
void MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon ) const;
/**
* clear the basic shapes list
*/
void DeletePrimitivesList();
/**
* When created, the corners coordinates are relative to the pad position, orientation 0,
* in m_customShapeAsPolygon
* CustomShapeAsPolygonToBoardPosition transform these coordinates to actual
* (board) coordinates
* @param aMergedPolygon = the corners coordinates, relative to aPosition and
* rotated by aRotation
* @param aPosition = the position of the shape (usually the pad shape, but
* not always, when moving the pad)
* @param aRotation = the rotation of the shape (usually the pad rotation, but
* not always, in DRC)
*/
void CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon,
wxPoint aPosition, double aRotation ) const;
/**
* Accessor to the basic shape list
*/
const std::vector<PAD_CS_PRIMITIVE>& GetPrimitives() const { return m_basicShapes; }
/**
* Accessor to the custom shape as one polygon
*/
const SHAPE_POLY_SET& GetCustomShapeAsPolygon() const { return m_customShapeAsPolygon; }
void Flip( const wxPoint& aCentre, bool aFlipLeftRight ) override;
/**
@ -373,24 +318,18 @@ public:
/**
* Mirror the primitives about a coordinate
*
* @param aX the x coordinate about which to mirror
*/
void MirrorXPrimitives( int aX );
/**
* Import to the basic shape list
* @return true if OK, false if issues
* (more than one polygon to build the polygon shape list)
*/
bool SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList );
void SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList );
/**
* Add to the basic shape list
* @return true if OK, false if issues
* (more than one polygon to build the polygon shape list)
*/
bool AddPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList );
void AddPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList );
/**
@ -414,21 +353,11 @@ public:
double GetOrientationDegrees() const { return m_Orient/10.0; }
double GetOrientationRadians() const { return m_Orient*M_PI/1800; }
void SetDrillShape( PAD_DRILL_SHAPE_T aDrillShape )
{ m_drillShape = aDrillShape; }
void SetDrillShape( PAD_DRILL_SHAPE_T aShape ) { m_drillShape = aShape; m_shapesDirty = true; }
PAD_DRILL_SHAPE_T GetDrillShape() const { return m_drillShape; }
/**
* Function GetOblongGeometry calculates the start point, end point and width of an
* equivalent segment which have the same position and width as the pad (for circular
* of oval pads) or hole
*
* NB: points returned are RELATIVE to the PAD POSITION. For board coordinates holes
* will need to be offset by GetPosition() and pads by ShapePos().
*
* @param aStartPoint = first point of the equivalent segment, relative to the pad position.
* @param aEndPoint = second point of the equivalent segment, relative to the pad position.
* @param aWidth = width equivalent segment.
* JEY TODO: temporary until Tom is done with DRC stuff....
*/
void GetOblongGeometry( const wxSize& aDrillOrPadSize,
wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const;
@ -461,20 +390,47 @@ public:
double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; }
void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; }
/**
* Function TransformShapeWithClearanceToPolygon
* Convert the pad shape to a closed polygon
* Used in filling zones calculations
* Circles and arcs are approximated by segments
* Convert the pad shape to a closed polygon. Circles and arcs are approximated by segments.
* @param aCornerBuffer = a buffer to store the polygon
* @param aClearanceValue = the clearance around the pad
* @param aMaxError = Maximum error from true when converting arcs
* @param ignoreLineWidth = used for edge cut items where the line width is only
* for visualization
* @param aMaxError = maximum error from true when converting arcs
* @param ignoreLineWidth = used for edge cuts where the line width is only for visualization
*/
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue,
int aMaxError = ARC_HIGH_DEF, bool ignoreLineWidth = false ) const override;
int aMaxError = ARC_HIGH_DEF,
bool ignoreLineWidth = false ) const override;
/**
* Function TransformHoleWithClearanceToPolygon
* Build the Corner list of the polygonal drill shape in the board coordinate system.
* @param aCornerBuffer = a buffer to fill.
* @param aInflateValue = the clearance or margin value.
* @param aError = maximum deviation of an arc from the polygon approximation
* @return false if the pad has no hole, true otherwise
*/
bool TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
int aError = ARC_HIGH_DEF ) const;
/**
* Function GetEffectiveShapes
* Returns a list of SHAPE objects representing the pad's copper.
*/
const std::vector<std::shared_ptr<SHAPE>>& GetEffectiveShapes() const;
/**
* Function GetEffectiveHoleShape
* Returns a list of SHAPE objects representing the pad's hole.
*/
const std::shared_ptr<SHAPE_SEGMENT>& GetEffectiveHoleShape() const;
/**
* Function GetBoundingRadius
* returns the radius of a minimum sized circle which fully encloses this pad.
* The center is the pad position NOT THE SHAPE POS!
*/
int GetBoundingRadius() const;
/**
* Function GetLocalClearanceOverrides
@ -515,17 +471,14 @@ public:
*/
wxSize GetSolderPasteMargin() const;
void SetZoneConnection( ZONE_CONNECTION aType )
{
m_ZoneConnection = aType;
}
void SetZoneConnection( ZONE_CONNECTION aType ) { m_ZoneConnection = aType; }
ZONE_CONNECTION GetZoneConnection() const { return m_ZoneConnection; }
ZONE_CONNECTION GetZoneConnection() const;
ZONE_CONNECTION GetLocalZoneConnection() const
{
return m_ZoneConnection;
}
/**
* Return the zone connection in effect (either locally overridden or overridden in the
* parent module).
*/
ZONE_CONNECTION GetEffectiveZoneConnection() const;
void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; }
int GetThermalWidth() const;
@ -534,110 +487,12 @@ public:
int GetThermalGap() const;
/**
* Function BuildPadPolygon
* Has meaning only for polygonal pads (trapezoid and rectangular)
* Build the Corner list of the polygonal shape,
* depending on shape, extra size (clearance ...) and orientation
* @param aCoord = a buffer to fill (4 corners).
* @param aInflateValue = wxSize: the clearance or margin value. value > 0:
* inflate, < 0 deflate
* @param aRotation = full rotation of the polygon
*/
void BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, double aRotation ) const;
/**
* Function GetRoundRectCornerRadius
* Function SetRoundRectCornerRadius
* Has meaning only for rounded rect pads
* @return The radius of the rounded corners for this pad.
*/
int GetRoundRectCornerRadius() const
{
return GetRoundRectCornerRadius( m_Size );
}
/**
* Helper function GetRoundRectCornerRadius
* Has meaning only for rounded rect pads
* Returns the radius of the rounded corners of a rectangle
* size aSize, using others setting of the pad
* @param aSize = size of the of the round rect. Usually the pad size
* but can be the size of the pad on solder mask or solder paste
* @return The radius of the rounded corners for this pad size.
*/
int GetRoundRectCornerRadius( const wxSize& aSize ) const;
/**
* Set the rounded rectangle radius ratio based on a given radius
* @param aRadius = desired radius of curvature
*/
void SetRoundRectCornerRadius( double aRadius );
/**
* Function BuildPadShapePolygon
* Build the Corner list of the polygonal shape,
* depending on shape, extra size (clearance ...) pad and orientation
* This function is similar to TransformShapeWithClearanceToPolygon,
* but the difference is BuildPadShapePolygon creates a polygon shape exactly
* similar to pad shape, which a size inflated by aInflateValue
* and TransformShapeWithClearanceToPolygon creates a more complex shape (for instance
* a rectangular pad is converted in a rectangulr shape with ronded corners)
* @param aCornerBuffer = a buffer to fill.
* @param aInflateValue = the clearance or margin value.
* value > 0: inflate, < 0 deflate, = 0 : no change
* the clearance can have different values for x and y directions
* (relative to the pad)
* @param aError = Maximum deviation of an arc from the polygon segment
*/
void BuildPadShapePolygon(
SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue, int aError = ARC_HIGH_DEF ) const;
/**
* Function BuildPadDrillShapePolygon
* Build the Corner list of the polygonal drill shape,
* depending on shape pad hole and orientation
* @param aCornerBuffer = a buffer to fill.
* @param aInflateValue = the clearance or margin value.
* value > 0: inflate, < 0 deflate, = 0 : no change
* @param aError = Maximum deviation of an arc from the polygon approximation
* @return false if the pad has no hole, true otherwise
*/
bool BuildPadDrillShapePolygon(
SHAPE_POLY_SET& aCornerBuffer, int aInflateValue, int aError = ARC_HIGH_DEF ) const;
/**
* Function BuildSegmentFromOvalShape
* Has meaning only for OVAL (and ROUND) pads
* Build an equivalent segment having the same shape as the OVAL shape,
* Useful in draw function and in DRC and HitTest functions,
* because segments are already well handled by track tests
* @param aSegStart = the starting point of the equivalent segment relative to the shape
* position.
* @param aSegEnd = the ending point of the equivalent segment, relative to the shape position
* @param aRotation = full rotation of the segment
* @param aRotation = full rotation of the segment
* @param aMargin = a margin around the shape (for instance mask margin)
* @return the width of the segment
*/
int BuildSegmentFromOvalShape( wxPoint& aSegStart, wxPoint& aSegEnd,
double aRotation, const wxSize& aMargin ) const;
/**
* Function GetBoundingRadius
* returns the radius of a minimum sized circle which fully encloses this pad.
* The center is the pad position
*/
int GetBoundingRadius() const
{
// Any member function which would affect this calculation should set
// m_boundingRadius to -1 to re-trigger the calculation from here.
// Currently that is only m_Size, m_DeltaSize, and m_padShape accessors.
if( m_boundingRadius == -1 )
{
m_boundingRadius = boundingRadius();
}
return m_boundingRadius;
}
int GetRoundRectCornerRadius() const;
wxPoint ShapePos() const;
@ -648,36 +503,8 @@ public:
* Cannot be > 0.5
* the normalized IPC-7351C value is 0.25
*/
double GetRoundRectRadiusRatio() const
{
return m_padRoundRectRadiusScale;
}
/**
* has meaning only for rounded rect pads
* Set the scaling factor between the smaller Y or Y size and the radius
* of the rounded corners.
* Cannot be < 0.5 and obviously must be > 0
* the normalized IPC-7351C value is 0.25
*/
void SetRoundRectRadiusRatio( double aRadiusScale )
{
if( aRadiusScale < 0.0 )
aRadiusScale = 0.0;
m_padRoundRectRadiusScale = std::min( aRadiusScale, 0.5 );
}
/**
* has meaning only for chamfered rect pads
* @return the ratio between the smaller Y or Y size and the radius
* of the rounded corners.
* Cannot be > 0.5
*/
double GetChamferRectRatio() const
{
return m_padChamferRectScale;
}
void SetRoundRectRadiusRatio( double aRadiusScale );
double GetRoundRectRadiusRatio() const { return m_padRoundRectRadiusScale; }
/**
* has meaning only for chamfered rect pads
@ -685,19 +512,8 @@ public:
* of the rounded corners.
* Cannot be < 0.5 and obviously must be > 0
*/
void SetChamferRectRatio( double aChamferScale )
{
if( aChamferScale < 0.0 )
aChamferScale = 0.0;
m_padChamferRectScale = std::min( aChamferScale, 0.5 );
}
/**
* has meaning only for chamfered rect pads
* @return the position of the chamfer for a 0 orientation
*/
int GetChamferPositions() const { return m_chamferPositions; }
void SetChamferRectRatio( double aChamferScale );
double GetChamferRectRatio() const { return m_padChamferRectScale; }
/**
* has meaning only for chamfered rect pads
@ -705,10 +521,8 @@ public:
* RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT,
* RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT
*/
void SetChamferPositions( int aChamferPositions )
{
m_chamferPositions = aChamferPositions;
}
void SetChamferPositions( int aPositions ) { m_chamferPositions = aPositions; }
int GetChamferPositions() const { return m_chamferPositions; }
/**
* Function GetSubRatsnest
@ -814,40 +628,36 @@ public:
private:
/**
* Function boundingRadius
* Function calcBoundingRadius
* returns a calculated radius of a bounding circle for this pad.
*/
int boundingRadius() const;
int calcBoundingRadius() const;
bool buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError );
void addCustomPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError ) const;
private: // Private variable members:
// Actually computed and cached on demand by the accessor
mutable int m_boundingRadius; ///< radius of the circle containing the pad shape
void buildEffectiveShapes() const;
private:
wxString m_name; ///< pad name (pin number in schematic)
wxString m_pinFunction; ///< pin function in schematic
// TODO: Remove m_Pos from Pad or make private. View positions calculated from m_Pos0
wxPoint m_Pos; ///< pad Position on board
PAD_SHAPE_T m_padShape; ///< Shape: PAD_SHAPE_CIRCLE, PAD_SHAPE_RECT,
///< PAD_SHAPE_OVAL, PAD_SHAPE_TRAPEZOID,
///< PAD_SHAPE_ROUNDRECT, PAD_SHAPE_POLYGON
mutable bool m_shapesDirty;
mutable int m_effectiveBoundingRadius;
mutable std::vector<std::shared_ptr<SHAPE>> m_effectiveShapes;
mutable std::shared_ptr<SHAPE_SEGMENT> m_effectiveHoleShape;
/** for free shape pads: a list of basic shapes,
* in local coordinates, orient 0, coordinates relative to m_Pos
* They are expected to define only one copper area.
*/
std::vector<PAD_CS_PRIMITIVE> m_basicShapes;
/** for free shape pads: the set of basic shapes, merged as one polygon,
* in local coordinates, orient 0, coordinates relative to m_Pos
*/
SHAPE_POLY_SET m_customShapeAsPolygon;
/**
* How to build the custom shape in zone, to create the clearance area:
* CUST_PAD_SHAPE_IN_ZONE_OUTLINE = use pad shape
@ -871,7 +681,7 @@ private: // Private variable members:
///< to corner radius, default 0.25
double m_padChamferRectScale; ///< scaling factor from smallest m_Size coord
///< to chamfer value, default 0.25
int m_chamferPositions; ///< the positions of the chamfered position for a 0 orientation
int m_chamferPositions; ///< the positions of the chamfers for a 0 orientation
PAD_SHAPE_T m_anchorPadShape; ///< for custom shaped pads: shape of pad anchor,
///< PAD_SHAPE_RECT, PAD_SHAPE_CIRCLE

View File

@ -784,10 +784,10 @@ void ZONE_CONTAINER::Mirror( const wxPoint& aMirrorRef, bool aMirrorLeftRight )
ZONE_CONNECTION ZONE_CONTAINER::GetPadConnection( D_PAD* aPad ) const
{
if( aPad == NULL || aPad->GetZoneConnection() == ZONE_CONNECTION::INHERITED )
if( aPad == NULL || aPad->GetEffectiveZoneConnection() == ZONE_CONNECTION::INHERITED )
return m_PadConnection;
else
return aPad->GetZoneConnection();
return aPad->GetEffectiveZoneConnection();
}

View File

@ -105,7 +105,7 @@ const VECTOR2I CN_ITEM::GetAnchor( int n ) const
RotatePoint( pt1, pad->ShapePos(), pad->GetOrientation() );
SHAPE_POLY_SET padPolySet;
pad->BuildPadShapePolygon( padPolySet, wxSize( 0, 0 ), ARC_LOW_DEF );
pad->TransformShapeWithClearanceToPolygon( padPolySet, 0, ARC_LOW_DEF );
const SHAPE_LINE_CHAIN& padOutline = padPolySet.COutline( 0 );
SHAPE_LINE_CHAIN::INTERSECTIONS intersections;

View File

@ -551,7 +551,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
else
m_SolderPasteMarginRatioCtrl->SetValue( msg );
switch( m_dummyPad->GetLocalZoneConnection() )
switch( m_dummyPad->GetZoneConnection() )
{
default:
case ZONE_CONNECTION::INHERITED: m_ZoneConnectionChoice->SetSelection( 0 ); break;
@ -1199,7 +1199,10 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK()
if( m_dummyPad->GetShape() == PAD_SHAPE_CUSTOM )
{
if( !m_dummyPad->MergePrimitivesAsPolygon( ) )
SHAPE_POLY_SET mergedPolygon;
m_dummyPad->MergePrimitivesAsPolygon( &mergedPolygon );
if( mergedPolygon.OutlineCount() > 1 )
error_msgs.Add( _( "Incorrect pad shape: the shape must be equivalent to only one polygon" ) );
}
@ -1446,7 +1449,7 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
m_currentPad->SetRoundRectRadiusRatio( m_padMaster->GetRoundRectRadiusRatio() );
m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() );
m_currentPad->SetChamferPositions( m_padMaster->GetChamferPositions() );
m_currentPad->SetZoneConnection( m_padMaster->GetZoneConnection() );
m_currentPad->SetZoneConnection( m_padMaster->GetEffectiveZoneConnection() );
// rounded rect pads with radius ratio = 0 are in fact rect pads.
// So set the right shape (and perhaps issues with a radius = 0)

View File

@ -1085,9 +1085,8 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
// Fast test to detect a pad candidate inside the text bounding box
// Finer test (time consumming) is made only for pads near the text.
int bb_radius = pad->GetBoundingRadius() + minClearance;
VECTOR2I shape_pos( pad->ShapePos() );
if( !rect_area.Collide( SEG( shape_pos, shape_pos ), bb_radius ) )
if( !rect_area.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) )
continue;
SHAPE_POLY_SET padOutline;

View File

@ -685,230 +685,37 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual )
{
// relativePadPos is the aPad shape position relative to the aRefPad shape position
wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
int center2center = KiROUND( EuclideanNorm( relativePadPos ) );
int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - aRefPad->ShapePos() ) );
// Quick test: Clearance is OK if the bounding circles are further away than aMinClearance
if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
return true;
/* Here, pads are near and DRC depends on the pad shapes. We must compare distance using
* a fine shape analysis.
* Because a circle or oval shape is the easier shape to test, swap pads to have aRefPad be
* a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. If aRefPad = TRAPEZOID and aPad = RECT, also swap.
*/
bool swap_pads;
swap_pads = false;
// JEY TODO:
// TOM TODO: MTV only works as a proxy for actual-distance for convex shapes
// swap pads to make comparisons easier
// Note also a ROUNDRECT pad with a corner radius = r can be considered as
// a smaller RECT (size - 2*r) with a clearance increased by r
// priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
VECTOR2I mtv;
VECTOR2I maxMtv( 0, 0 );
for( const std::shared_ptr<SHAPE>& aShape : aRefPad->GetEffectiveShapes() )
{
// pad ref shape is here oval, rect, roundrect, chamfered rect, trapezoid or custom
switch( aPad->GetShape() )
for( const std::shared_ptr<SHAPE>& bShape : aPad->GetEffectiveShapes() )
{
case PAD_SHAPE_CIRCLE:
swap_pads = true;
break;
case PAD_SHAPE_OVAL:
swap_pads = true;
break;
case PAD_SHAPE_RECT:
case PAD_SHAPE_ROUNDRECT:
if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
swap_pads = true;
break;
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_CUSTOM:
break;
if( aShape->Collide( bShape.get(), aMinClearance, mtv ) )
{
if( mtv.SquaredEuclideanNorm() > maxMtv.SquaredEuclideanNorm() )
maxMtv = mtv;
}
}
}
if( swap_pads )
if( maxMtv.x > 0 || maxMtv.y > 0 )
{
std::swap( aRefPad, aPad );
relativePadPos = -relativePadPos;
*aActual = std::max( 0, aMinClearance - maxMtv.EuclideanNorm() );
return false;
}
bool diag = true;
if( ( aRefPad->GetShape() == PAD_SHAPE_CIRCLE || aRefPad->GetShape() == PAD_SHAPE_OVAL ) )
{
/* Treat an oval pad as a line segment along the hole's major axis,
* shortened by half its minor axis.
* A circular pad is just a degenerate case of an oval hole.
*/
wxPoint refPadStart, refPadEnd;
int refPadWidth;
aRefPad->GetOblongGeometry( aRefPad->GetSize(), &refPadStart, &refPadEnd, &refPadWidth );
refPadStart += aRefPad->ShapePos();
refPadEnd += aRefPad->ShapePos();
SEG refPadSeg( refPadStart, refPadEnd );
diag = checkClearanceSegmToPad( refPadSeg, refPadWidth, aPad, aMinClearance, aActual );
}
else
{
int dist_extra = 0;
// corners of aRefPad (used only for rect/roundrect/trap pad)
wxPoint polyref[4];
// corners of aRefPad (used only for custom pad)
SHAPE_POLY_SET polysetref;
if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
int padRadius = aRefPad->GetRoundRectCornerRadius();
dist_extra = padRadius;
GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(),
aRefPad->GetOrientation() );
}
else if( aRefPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
{
BOARD* board = aRefPad->GetBoard();
int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
// The reference pad can be rotated. Calculate the rotated coordinates.
// (note, the ref pad position is the origin of coordinates for this drc test)
int padRadius = aRefPad->GetRoundRectCornerRadius();
TransformRoundChamferedRectToPolygon( polysetref, wxPoint( 0, 0 ), aRefPad->GetSize(),
aRefPad->GetOrientation(),
padRadius, aRefPad->GetChamferRectRatio(),
aRefPad->GetChamferPositions(), maxError );
}
else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
// The reference pad can be rotated. Calculate the rotated coordinates.
// (note, the ref pad position is the origin of coordinates for this drc test)
aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref, wxPoint( 0, 0 ),
aRefPad->GetOrientation() );
}
else
{
// BuildPadPolygon has meaning for rect a trapeziod shapes and returns the 4 corners.
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
}
// corners of aPad (used only for rect/roundrect/trap pad)
wxPoint polycompare[4];
// corners of aPad (used only custom pad)
SHAPE_POLY_SET polysetcompare;
switch( aPad->GetShape() )
{
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_RECT:
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CUSTOM:
if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
int padRadius = aPad->GetRoundRectCornerRadius();
dist_extra = padRadius;
GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(),
aPad->GetOrientation() );
}
else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
{
BOARD* board = aRefPad->GetBoard();
int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
// The pad to compare can be rotated. Calculate the rotated coordinates.
// ( note, the pad to compare position is the relativePadPos for this drc test)
int padRadius = aPad->GetRoundRectCornerRadius();
TransformRoundChamferedRectToPolygon( polysetcompare, relativePadPos,
aPad->GetSize(), aPad->GetOrientation(),
padRadius, aPad->GetChamferRectRatio(),
aPad->GetChamferPositions(), maxError );
}
else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
// The pad to compare can be rotated. Calculate the rotated coordinates.
// ( note, the pad to compare position is the relativePadPos for this drc test)
aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare, relativePadPos,
aPad->GetOrientation() );
}
else
{
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
// Move aPad shape to relativePadPos
for( int ii = 0; ii < 4; ii++ )
polycompare[ii] += relativePadPos;
}
// And now test polygons: We have 3 cases:
// one poly is complex and the other is basic (has only 4 corners)
// both polys are complex
// both polys are basic (have only 4 corners) the most usual case
if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
{
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
polycompare, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
{
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC((wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
polyref, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
{
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC((wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
(wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else
{
if( !poly2polyDRC( polyref, 4, polycompare, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
break;
default:
wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
break;
}
}
return diag;
return true;
}

View File

@ -594,8 +594,14 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
{
fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
int ddx = pad->GetDelta().x / 2;
int ddy = pad->GetDelta().y / 2;
wxPoint poly[4];
pad->BuildPadPolygon( poly, wxSize( 0, 0 ), 0 );
poly[0] = wxPoint( -dx + ddy, dy + ddx );
poly[1] = wxPoint( dx - ddy, dy - ddx );
poly[2] = wxPoint( dx + ddy, -dy + ddx );
poly[3] = wxPoint( -dx - ddy, -dy - ddx );
for( int cur = 0; cur < 4; ++cur )
{
@ -613,7 +619,8 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
{
fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
const SHAPE_POLY_SET& outline = pad->GetCustomShapeAsPolygon();
SHAPE_POLY_SET outline;
pad->MergePrimitivesAsPolygon( &outline );
for( int jj = 0; jj < outline.OutlineCount(); ++jj )
{

View File

@ -1151,7 +1151,7 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aTinLayer, D_P
case PAD_SHAPE_CHAMFERED_RECT:
{
SHAPE_POLY_SET polySet;
const int corner_radius = aPad->GetRoundRectCornerRadius( aPad->GetSize() );
const int corner_radius = aPad->GetRoundRectCornerRadius();
TransformRoundChamferedRectToPolygon( polySet, wxPoint( 0, 0 ), aPad->GetSize(),
0.0, corner_radius, 0.0, 0, ARC_HIGH_DEF );
std::vector< wxRealPoint > cornerList;

View File

@ -1479,8 +1479,8 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const
if( aPad->GetLocalClearance() != 0 )
StrPrintf( &output, " (clearance %s)", FormatInternalUnits( aPad->GetLocalClearance() ).c_str() );
if( aPad->GetZoneConnection() != ZONE_CONNECTION::INHERITED )
StrPrintf( &output, " (zone_connect %d)", static_cast<int>( aPad->GetZoneConnection() ) );
if( aPad->GetEffectiveZoneConnection() != ZONE_CONNECTION::INHERITED )
StrPrintf( &output, " (zone_connect %d)", static_cast<int>( aPad->GetEffectiveZoneConnection() ) );
if( aPad->GetThermalWidth() != 0 )
StrPrintf( &output, " (thermal_width %s)", FormatInternalUnits( aPad->GetThermalWidth() ).c_str() );

View File

@ -146,7 +146,7 @@ void PAD_CS_PRIMITIVE::Rotate( const wxPoint& aRotCentre, double aAngle )
* the shape is a polygon (can be with thick outline), segment, circle or arc
*/
void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool aMergePrimitives )
void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness )
{
std::vector<wxPoint> points;
@ -158,39 +158,33 @@ void D_PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool
for( auto iter = poly_no_hole.CIterate(); iter; iter++ )
points.emplace_back( iter->x, iter->y );
AddPrimitivePoly( points, aThickness, aMergePrimitives );
AddPrimitivePoly( points, aThickness );
}
void D_PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness,
bool aMergePrimitives )
void D_PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_POLYGON );
shape.m_Poly = aPoly;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
if( aMergePrimitives )
MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
void D_PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
bool aMergePrimitives )
void D_PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_SEGMENT );
shape.m_Start = aStart;
shape.m_End = aEnd;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
if( aMergePrimitives )
MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
void D_PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
int aThickness, bool aMergePrimitives )
int aThickness )
{
PAD_CS_PRIMITIVE shape( S_ARC );
shape.m_Start = aCenter;
@ -198,14 +192,12 @@ void D_PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int
shape.m_ArcAngle = aArcAngle;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
if( aMergePrimitives )
MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
void D_PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1,
const wxPoint& aCtrl2, int aThickness, bool aMergePrimitives )
const wxPoint& aCtrl2, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_CURVE );
shape.m_Start = aStart;
@ -214,41 +206,33 @@ void D_PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const
shape.m_Ctrl2 = aCtrl2;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
if( aMergePrimitives )
MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
void D_PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness,
bool aMergePrimitives )
void D_PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_CIRCLE );
shape.m_Start = aCenter;
shape.m_Radius = aRadius;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
if( aMergePrimitives )
MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
void D_PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
bool aMergePrimitives )
void D_PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_RECT );
shape.m_Start = aStart;
shape.m_End = aEnd;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
if( aMergePrimitives )
MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
bool D_PAD::SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
void D_PAD::SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
{
// clear old list
m_basicShapes.clear();
@ -257,17 +241,16 @@ bool D_PAD::SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList
if( aPrimitivesList.size() )
m_basicShapes = aPrimitivesList;
// Only one polygon is expected (pad area = only one copper area)
return MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
bool D_PAD::AddPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
void D_PAD::AddPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList )
{
for( const auto& prim : aPrimitivesList )
m_basicShapes.push_back( prim );
return MergePrimitivesAsPolygon();
m_shapesDirty = true;
}
@ -275,44 +258,44 @@ bool D_PAD::AddPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList
void D_PAD::DeletePrimitivesList()
{
m_basicShapes.clear();
m_customShapeAsPolygon.RemoveAllContours();
m_shapesDirty = true;
}
bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError )
void D_PAD::addCustomPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError ) const
{
SHAPE_POLY_SET aux_polyset;
SHAPE_POLY_SET polyset;
for( PAD_CS_PRIMITIVE& bshape : m_basicShapes )
for( const PAD_CS_PRIMITIVE& bshape : m_basicShapes )
{
switch( bshape.m_Shape )
{
case S_CURVE:
{
std::vector<wxPoint> ctrlPoints = { bshape.m_Start, bshape.m_Ctrl1, bshape.m_Ctrl2, bshape.m_End };
std::vector<wxPoint> ctrlPoints = { bshape.m_Start, bshape.m_Ctrl1, bshape.m_Ctrl2,
bshape.m_End };
BEZIER_POLY converter( ctrlPoints );
std::vector< wxPoint> poly;
converter.GetPoly( poly, bshape.m_Thickness );
for( unsigned ii = 1; ii < poly.size(); ii++ )
{
TransformSegmentToPolygon(
aux_polyset, poly[ ii - 1 ], poly[ ii ], aError, bshape.m_Thickness );
TransformSegmentToPolygon( polyset, poly[ ii - 1 ], poly[ ii ], aError,
bshape.m_Thickness );
}
break;
}
case S_SEGMENT: // usual segment : line with rounded ends
{
TransformSegmentToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, aError,
TransformSegmentToPolygon( polyset, bshape.m_Start, bshape.m_End, aError,
bshape.m_Thickness );
break;
}
case S_ARC: // Arc with rounded ends
{
TransformArcToPolygon( aux_polyset, bshape.m_Start, bshape.m_End, bshape.m_ArcAngle,
TransformArcToPolygon( polyset, bshape.m_Start, bshape.m_End, bshape.m_ArcAngle,
aError, bshape.m_Thickness );
break;
}
@ -320,80 +303,63 @@ bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError )
case S_CIRCLE: // ring or circle
{
if( bshape.m_Thickness ) // ring
TransformRingToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError,
TransformRingToPolygon( polyset, bshape.m_Start, bshape.m_Radius, aError,
bshape.m_Thickness );
else // Filled circle
TransformCircleToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError );
TransformCircleToPolygon( polyset, bshape.m_Start, bshape.m_Radius, aError );
break;
}
case S_RECT:
bshape.m_Poly.clear();
bshape.m_Poly.emplace_back( bshape.m_Start );
bshape.m_Poly.emplace_back( bshape.m_End.x, bshape.m_Start.y );
bshape.m_Poly.emplace_back( bshape.m_End );
bshape.m_Poly.emplace_back( bshape.m_Start.x, bshape.m_End.y );
KI_FALLTHROUGH;
case S_POLYGON: // polygon
{
if( bshape.m_Poly.size() < 2 )
break; // Malformed polygon.
SHAPE_POLY_SET poly;
poly.NewOutline();
// Insert the polygon:
const std::vector< wxPoint>& poly = bshape.m_Poly;
aux_polyset.NewOutline();
if( bshape.m_Thickness )
if( bshape.m_Shape == S_RECT )
{
SHAPE_POLY_SET polyset;
polyset.NewOutline();
for( const wxPoint& pt : poly )
polyset.Append( pt.x, pt.y );
int numSegs = std::max( GetArcToSegmentCount( bshape.m_Thickness / 2, aError, 360.0 ), 6 );
polyset.Inflate( bshape.m_Thickness / 2, numSegs );
aux_polyset.Append( polyset );
poly.Append( bshape.m_Start );
poly.Append( bshape.m_End.x, bshape.m_Start.y );
poly.Append( bshape.m_End );
poly.Append( bshape.m_Start.x, bshape.m_End.y );
}
else
{
for( const wxPoint& pt : poly )
aux_polyset.Append( pt.x, pt.y );
for( const wxPoint& pt : bshape.m_Poly )
poly.Append( pt );
}
if( bshape.m_Shape == S_RECT )
bshape.m_Poly.clear();
if( bshape.m_Thickness )
{
int numSegs = std::max( GetArcToSegmentCount( bshape.m_Thickness / 2, aError, 360.0 ), 6 );
poly.Inflate( bshape.m_Thickness / 2, numSegs );
}
// Insert the polygon:
polyset.NewOutline();
polyset.Append( poly );
}
break;
default:
// un-handled primitive
wxASSERT_MSG( false, wxT( "D_PAD::buildCustomPadPolygon not implemented for "
+ BOARD_ITEM::ShowShape( bshape.m_Shape ) ) );
wxASSERT_MSG( false, "D_PAD::addCustomPadPrimitivesToPolygon not implemented for "
+ BOARD_ITEM::ShowShape( bshape.m_Shape ) );
break;
}
}
aux_polyset.Simplify( SHAPE_POLY_SET::PM_FAST );
polyset.Simplify( SHAPE_POLY_SET::PM_FAST );
// Merge all polygons with the initial pad anchor shape
if( aux_polyset.OutlineCount() )
if( polyset.OutlineCount() )
{
aMergedPolygon->BooleanAdd( aux_polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}
return aMergedPolygon->OutlineCount() <= 1;
}
/* Merge all basic shapes, converted to a polygon in one polygon,
* return true if OK, false in there is more than one polygon
* in aMergedPolygon
*/
bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon )
void D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon ) const
{
auto board = GetBoard();
int maxError = ARC_HIGH_DEF;
@ -401,58 +367,35 @@ bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon )
if( board )
maxError = board->GetDesignSettings().m_MaxError;
// if aMergedPolygon == NULL, use m_customShapeAsPolygon as target
if( !aMergedPolygon )
aMergedPolygon = &m_customShapeAsPolygon;
aMergedPolygon->RemoveAllContours();
// Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
// The anchor pad is always at 0,0
switch( GetAnchorPadShape() )
{
default:
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError );
break;
case PAD_SHAPE_RECT:
{
SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
aMergedPolygon->AddOutline( rect.Outline() );
}
break;
default:
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError );
break;
}
}
if( !buildCustomPadPolygon( aMergedPolygon, maxError ) )
return false;
m_boundingRadius = -1; // The current bounding radius is no longer valid.
return aMergedPolygon->OutlineCount() <= 1;
}
void D_PAD::CustomShapeAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon,
wxPoint aPosition, double aRotation ) const
{
if( aMergedPolygon->OutlineCount() == 0 )
return;
// Move, rotate, ... coordinates in aMergedPolygon according to the
// pad position and orientation
aMergedPolygon->Rotate( -DECIDEG2RAD( aRotation ) );
aMergedPolygon->Move( VECTOR2I( aPosition ) );
addCustomPadPrimitivesToPolygon( aMergedPolygon, maxError );
}
bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
{
SHAPE_POLY_SET poly;
addCustomPadPrimitivesToPolygon( &poly, ARC_LOW_DEF );
if( !buildCustomPadPolygon( &poly, ARC_LOW_DEF ) )
if( poly.OutlineCount() > 1 )
return false;
const int minSteps = 10;

View File

@ -44,7 +44,9 @@
#include <gal/graphics_abstraction_layer.h>
#include <geometry/geometry_utils.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_circle.h>
#include <geometry/shape_simple.h>
using namespace KIGFX;
@ -790,161 +792,59 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
// Choose drawing settings depending on if we are drawing a pad itself or a hole
if( aLayer == LAYER_PADS_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
{
m_gal->Save();
m_gal->Translate( VECTOR2D( aPad->GetPosition() ) );
m_gal->Rotate( -aPad->GetOrientationRadians() );
const std::shared_ptr<SHAPE_SEGMENT>& seg = aPad->GetEffectiveHoleShape();
// Drawing hole: has same shape as PAD_CIRCLE or PAD_OVAL
size = getDrillSize( aPad ) / 2.0;
if( getDrillShape( aPad ) == PAD_DRILL_SHAPE_OBLONG )
{
if( size.y >= size.x )
{
m = ( size.y - size.x );
n = size.x;
m_gal->DrawArc( VECTOR2D( 0, -m ), n, -M_PI, 0 );
m_gal->DrawArc( VECTOR2D( 0, m ), n, M_PI, 0 );
if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] )
{
m_gal->DrawLine( VECTOR2D( -n, -m ), VECTOR2D( -n, m ) );
m_gal->DrawLine( VECTOR2D( n, -m ), VECTOR2D( n, m ) );
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() );
}
else
{
m_gal->DrawRectangle( VECTOR2D( -n, -m ), VECTOR2D( n, m ) );
}
}
else
{
m = ( size.x - size.y );
n = size.y;
m_gal->DrawArc( VECTOR2D( -m, 0 ), n, M_PI / 2, 3 * M_PI / 2 );
m_gal->DrawArc( VECTOR2D( m, 0 ), n, M_PI / 2, -M_PI / 2 );
if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] )
{
m_gal->DrawLine( VECTOR2D( -m, -n ), VECTOR2D( m, -n ) );
m_gal->DrawLine( VECTOR2D( -m, n ), VECTOR2D( m, n ) );
}
else
{
m_gal->DrawRectangle( VECTOR2D( -m, -n ), VECTOR2D( m, n ) );
}
}
}
else
{
m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), size.x );
}
m_gal->Restore();
}
else
{
SHAPE_POLY_SET polySet;
wxSize pad_size = aPad->GetSize();
wxSize margin;
switch( aLayer )
{
case F_Mask:
case B_Mask:
{
int clearance = aPad->GetSolderMaskMargin();
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
{
int radius = (aPad->GetSize().x/2) + clearance;
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius );
}
else if( aPad->GetShape() == PAD_SHAPE_OVAL )
{
if( aPad->GetSize().x == aPad->GetSize().y )
{
int radius = (aPad->GetSize().x/2) + clearance;
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius );
}
else
{
wxPoint seg_start, seg_end;
aPad->BuildSegmentFromOvalShape( seg_start, seg_end,
aPad->GetOrientation(),
wxSize( 0, 0 ) );
int seg_width = std::min( aPad->GetSize().x, aPad->GetSize().y )
+ 2*clearance;
m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ),
VECTOR2D( aPad->ShapePos()+seg_end ),
seg_width );
}
}
else
{
aPad->TransformShapeWithClearanceToPolygon( polySet, clearance );
m_gal->DrawPolygon( polySet );
}
}
margin.x = margin.y = aPad->GetSolderMaskMargin();
break;
case F_Paste:
case B_Paste:
{
wxSize pad_size = aPad->GetSize();
wxSize margin = aPad->GetSolderPasteMargin();
const_cast<D_PAD*>(aPad)->SetSize( pad_size + margin + margin );
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 );
else if( aPad->GetShape() == PAD_SHAPE_OVAL )
{
if( aPad->GetSize().x == aPad->GetSize().y )
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 );
else
{
wxPoint seg_start, seg_end;
aPad->BuildSegmentFromOvalShape( seg_start, seg_end,
aPad->GetOrientation(),
wxSize( 0, 0 ) );
int seg_width = std::min( aPad->GetSize().x, aPad->GetSize().y );
m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ),
VECTOR2D( aPad->ShapePos()+seg_end ),
seg_width );
}
}
else
{
aPad->TransformShapeWithClearanceToPolygon( polySet, 0 );
m_gal->DrawPolygon( polySet );
}
const_cast<D_PAD*>(aPad)->SetSize( pad_size );
}
margin = aPad->GetSolderPasteMargin();
break;
default:
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 );
else if( aPad->GetShape() == PAD_SHAPE_OVAL )
{
if( aPad->GetSize().x == aPad->GetSize().y )
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), aPad->GetSize().x/2 );
else
{
wxPoint seg_start, seg_end;
aPad->BuildSegmentFromOvalShape( seg_start, seg_end,
aPad->GetOrientation(),
wxSize( 0, 0 ) );
m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ),
VECTOR2D( aPad->ShapePos()+seg_end ),
std::min( aPad->GetSize().x, aPad->GetSize().y ) );
}
}
else
{
aPad->TransformShapeWithClearanceToPolygon( polySet, 0 );
m_gal->DrawPolygon( polySet );
}
margin.x = margin.y = 0;
break;
}
if( margin.x != margin.y )
{
const_cast<D_PAD*>( aPad )->SetSize( pad_size + margin + margin );
margin.x = margin.y = 0;
}
const std::vector<std::shared_ptr<SHAPE>>& shapes = aPad->GetEffectiveShapes();
if( shapes.size() == 1 && shapes[0]->Type() == SH_SEGMENT )
{
const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shapes[0].get();
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() + 2 * margin.x );
}
else if( shapes.size() == 1 && shapes[0]->Type() == SH_CIRCLE )
{
const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shapes[0].get();
m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + margin.x );
}
else
{
SHAPE_POLY_SET polySet;
aPad->TransformShapeWithClearanceToPolygon( polySet, margin.x );
m_gal->DrawPolygon( polySet );
}
if( aPad->GetSize() != pad_size )
const_cast<D_PAD*>( aPad )->SetSize( pad_size );
}
// Clearance outlines
@ -961,31 +861,17 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
m_gal->SetStrokeColor( color );
int clearance = aPad->GetClearance();
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
{
int radius = (aPad->GetSize().x/2) + clearance;
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius );
}
else if( aPad->GetShape() == PAD_SHAPE_OVAL )
{
if( aPad->GetSize().x == aPad->GetSize().y )
{
int radius = (aPad->GetSize().x/2) + clearance;
m_gal->DrawCircle( VECTOR2D( aPad->ShapePos() ), radius );
}
else
{
wxPoint seg_start, seg_end;
const std::vector<std::shared_ptr<SHAPE>>& shapes = aPad->GetEffectiveShapes();
aPad->BuildSegmentFromOvalShape( seg_start, seg_end,
aPad->GetOrientation(),
wxSize( 0, 0 ) );
int seg_width = std::min( aPad->GetSize().x, aPad->GetSize().y )
+ 2*clearance;
m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ),
VECTOR2D( aPad->ShapePos()+seg_end ),
seg_width );
if( shapes.size() == 1 && shapes[0]->Type() == SH_SEGMENT )
{
const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shapes[0].get();
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() + 2 * clearance );
}
else if( shapes.size() == 1 && shapes[0]->Type() == SH_CIRCLE )
{
const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shapes[0].get();
m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + clearance );
}
else
{

View File

@ -3352,33 +3352,33 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent )
case T_gr_arc:
dummysegm = parseDRAWSEGMENT();
pad->AddPrimitiveArc( dummysegm->GetCenter(), dummysegm->GetArcStart(),
dummysegm->GetAngle(), dummysegm->GetWidth(), false );
dummysegm->GetAngle(), dummysegm->GetWidth() );
break;
case T_gr_line:
dummysegm = parseDRAWSEGMENT();
pad->AddPrimitiveSegment( dummysegm->GetStart(), dummysegm->GetEnd(),
dummysegm->GetWidth(), false );
dummysegm->GetWidth() );
break;
case T_gr_circle:
dummysegm = parseDRAWSEGMENT( true ); // Circles with 0 thickness are allowed
// ( filled circles )
pad->AddPrimitiveCircle( dummysegm->GetCenter(), dummysegm->GetRadius(),
dummysegm->GetWidth(), false );
dummysegm->GetWidth() );
break;
case T_gr_rect:
dummysegm = parseDRAWSEGMENT( true );
pad->AddPrimitiveRect( dummysegm->GetStart(), dummysegm->GetEnd(),
dummysegm->GetWidth(), false );
dummysegm->GetWidth() );
break;
case T_gr_poly:
dummysegm = parseDRAWSEGMENT();
pad->AddPrimitivePoly( dummysegm->BuildPolyPointsList(),
dummysegm->GetWidth(), false );
dummysegm->GetWidth() );
break;
case T_gr_curve:
@ -3386,7 +3386,7 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent )
pad->AddPrimitiveCurve( dummysegm->GetStart(), dummysegm->GetEnd(),
dummysegm->GetBezControl1(),
dummysegm->GetBezControl2(),
dummysegm->GetWidth(), false );
dummysegm->GetWidth() );
break;
default:
@ -3411,10 +3411,6 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent )
}
}
// Be sure the custom shape polygon is built:
if( pad->GetShape() == PAD_SHAPE_CUSTOM )
pad->MergePrimitivesAsPolygon();
return pad.release();
}

View File

@ -35,6 +35,7 @@
#include <base_struct.h>
#include <gr_text.h>
#include <geometry/geometry_utils.h>
#include <geometry/shape_segment.h>
#include <trigo.h>
#include <pcb_base_frame.h>
#include <macros.h>
@ -249,70 +250,6 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
continue;
}
wxSize margin;
double width_adj = 0;
if( onCopperLayer )
width_adj = itemplotter.getFineWidthAdj();
if( onSolderMaskLayer )
margin.x = margin.y = pad->GetSolderMaskMargin();
if( onSolderPasteLayer )
margin = pad->GetSolderPasteMargin();
// Now offset the pad size by margin + width_adj
// this is easy for most shapes, but not for a trapezoid or a custom shape
wxSize padPlotsSize;
wxSize extraSize = margin * 2;
extraSize.x += width_adj;
extraSize.y += width_adj;
// Store these parameters that can be modified to plot inflated/deflated pads shape
wxSize deltaSize = pad->GetDelta(); // has meaning only for trapezoidal pads
PAD_SHAPE_T padShape = pad->GetShape();
double padCornerRadius = pad->GetRoundRectCornerRadius();
if( pad->GetShape() == PAD_SHAPE_TRAPEZOID )
{ // The easy way is to use BuildPadPolygon to calculate
// size and delta of the trapezoidal pad after offseting:
wxPoint coord[4];
pad->BuildPadPolygon( coord, extraSize/2, 0.0 );
// Calculate the size and delta from polygon corners coordinates:
// coord[0] is the lower left
// coord[1] is the upper left
// coord[2] is the upper right
// coord[3] is the lower right
// the size is the distance between middle of segments
// (left/right or top/bottom)
// size X is the dist between left and right middle points:
padPlotsSize.x = ( ( -coord[0].x + coord[3].x ) // the lower segment X length
+ ( -coord[1].x + coord[2].x ) ) // the upper segment X length
/ 2; // the Y size is the half sum
// size Y is the dist between top and bottom middle points:
padPlotsSize.y = ( ( coord[0].y - coord[1].y ) // the left segment Y lenght
+ ( coord[3].y - coord[2].y ) ) // the right segment Y lenght
/ 2; // the Y size is the half sum
// calculate the delta ( difference of lenght between 2 opposite edges )
// The delta.x is the delta along the X axis, therefore the delta of Y lenghts
wxSize delta;
if( coord[0].y != coord[3].y )
delta.x = coord[0].y - coord[3].y;
else
delta.y = coord[1].x - coord[0].x;
pad->SetDelta( delta );
}
else
padPlotsSize = pad->GetSize() + extraSize;
// Don't draw a null size item :
if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 )
continue;
COLOR4D color = COLOR4D::BLACK;
if( pad->GetLayerSet()[B_Cu] )
@ -326,8 +263,30 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
else if( sketchPads && aLayerMask[B_Fab] )
color = aPlotOpt.ColorSettings()->GetColor( B_Fab );
// Temporary set the pad size to the required plot size:
wxSize tmppadsize = pad->GetSize();
wxSize margin;
int width_adj = 0;
if( onCopperLayer )
width_adj = itemplotter.getFineWidthAdj();
if( onSolderMaskLayer )
margin.x = margin.y = pad->GetSolderMaskMargin();
if( onSolderPasteLayer )
margin = pad->GetSolderPasteMargin();
// Now offset the pad size by margin + width_adj
wxSize padPlotsSize = pad->GetSize() + margin * 2 + wxSize( width_adj, width_adj );
// Store these parameters that can be modified to plot inflated/deflated pads shape
PAD_SHAPE_T padShape = pad->GetShape();
wxSize padSize = pad->GetSize();
wxSize padDelta = pad->GetDelta(); // has meaning only for trapezoidal pads
double padCornerRadius = pad->GetRoundRectCornerRadius();
// Don't draw a null size item :
if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 )
continue;
switch( pad->GetShape() )
{
@ -348,14 +307,26 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
if( margin.x > 0 )
{
pad->SetShape( PAD_SHAPE_ROUNDRECT );
pad->SetSize( padPlotsSize );
pad->SetRoundRectCornerRadius( margin.x );
}
KI_FALLTHROUGH;
pad->SetSize( padPlotsSize );
itemplotter.PlotPad( pad, color, padPlotMode );
break;
case PAD_SHAPE_TRAPEZOID:
{
wxSize scale( padPlotsSize.x / padSize.x, padPlotsSize.y / padSize.y );
pad->SetDelta( wxSize( padDelta.x * scale.x, padDelta.y * scale.y ) );
pad->SetSize( padPlotsSize );
itemplotter.PlotPad( pad, color, padPlotMode );
}
break;
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_CHAMFERED_RECT:
// Chamfer and rounding are stored as a percent and so don't need scaling
pad->SetSize( padPlotsSize );
itemplotter.PlotPad( pad, color, padPlotMode );
break;
@ -373,8 +344,7 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
int numSegs = std::max( GetArcToSegmentCount( margin.x, maxError, 360.0 ), 6 );
shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST );
dummy.DeletePrimitivesList();
dummy.AddPrimitivePoly( shape, 0, false );
dummy.MergePrimitivesAsPolygon();
dummy.AddPrimitivePoly( shape, 0 );
// Be sure the anchor pad is not bigger than the deflated shape because this
// anchor will be added to the pad shape when plotting the pad. So now the
@ -388,8 +358,8 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
}
// Restore the pad parameters modified by the plot code
pad->SetSize( tmppadsize );
pad->SetDelta( deltaSize );
pad->SetSize( padSize );
pad->SetDelta( padDelta );
pad->SetShape( padShape );
pad->SetRoundRectCornerRadius( padCornerRadius );
}
@ -630,7 +600,6 @@ static const PCB_LAYER_ID plot_seq[] = {
void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
const PCB_PLOT_PARAMS& aPlotOpt )
{
BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
itemplotter.SetLayerSet( aLayerMask );
@ -646,7 +615,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
outlines.Simplify( SHAPE_POLY_SET::PM_FAST );
// Plot outlines
std::vector< wxPoint > cornerList;
std::vector<wxPoint> cornerList;
// Now we have one or more basic polygons: plot each polygon
for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
@ -673,9 +642,9 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
int smallDrill = (aPlotOpt.GetDrillMarksType() == PCB_PLOT_PARAMS::SMALL_DRILL_SHAPE)
? SMALL_DRILL : INT_MAX;
for( auto module : aBoard->Modules() )
for( MODULE* module : aBoard->Modules() )
{
for( auto pad : module->Pads() )
for( D_PAD* pad : module->Pads() )
{
wxSize hole = pad->GetDrillSize();
@ -690,19 +659,17 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
else
{
// Note: small drill marks have no significance when applied to slots
wxPoint drl_start, drl_end;
int width;
pad->GetOblongGeometry( pad->GetDrillSize(), &drl_start, &drl_end, &width );
aPlotter->ThickSegment( pad->GetPosition() + drl_start,
pad->GetPosition() + drl_end, width, SKETCH, NULL );
const std::shared_ptr<SHAPE_SEGMENT>& seg = pad->GetEffectiveHoleShape();
aPlotter->ThickSegment( (wxPoint) seg->GetSeg().A,
(wxPoint) seg->GetSeg().B,
seg->GetWidth(), SKETCH, NULL );
}
}
}
}
// Plot vias holes
for( auto track : aBoard->Tracks() )
for( TRACK* track : aBoard->Tracks() )
{
const VIA* via = dyn_cast<const VIA*>( track );

View File

@ -224,13 +224,9 @@ void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, COLOR4D aColor, EDA_DRAW_MODE_T aPl
aPad->GetOrientation(), aPlotMode, &gbr_metadata );
break;
case PAD_SHAPE_TRAPEZOID:
{
wxPoint coord[4];
aPad->BuildPadPolygon( coord, wxSize(0,0), 0 );
m_plotter->FlashPadTrapez( shape_pos, coord,
aPad->GetOrientation(), aPlotMode, &gbr_metadata );
}
case PAD_SHAPE_RECT:
m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), aPad->GetOrientation(), aPlotMode,
&gbr_metadata );
break;
case PAD_SHAPE_ROUNDRECT:
@ -238,40 +234,21 @@ void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, COLOR4D aColor, EDA_DRAW_MODE_T aPl
aPad->GetOrientation(), aPlotMode, &gbr_metadata );
break;
default:
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CHAMFERED_RECT:
{
SHAPE_POLY_SET polygons;
const int corner_radius = aPad->GetRoundRectCornerRadius( aPad->GetSize() );
TransformRoundChamferedRectToPolygon( polygons, shape_pos, aPad->GetSize(),
aPad->GetOrientation(), corner_radius, aPad->GetChamferRectRatio(),
aPad->GetChamferPositions(), m_board->GetDesignSettings().m_MaxError );
if( polygons.OutlineCount() == 0 )
break;
int min_dim = std::min( aPad->GetSize().x, aPad->GetSize().y ) /2;
m_plotter->FlashPadCustom( shape_pos,wxSize( min_dim, min_dim ), &polygons, aPlotMode, &gbr_metadata );
}
break;
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polygons;
aPad->MergePrimitivesAsPolygon( &polygons );
aPad->TransformShapeWithClearanceToPolygon( polygons, 0 );
if( polygons.OutlineCount() == 0 )
break;
aPad->CustomShapeAsPolygonToBoardPosition( &polygons, shape_pos, aPad->GetOrientation() );
m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), &polygons, aPlotMode, &gbr_metadata );
m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), &polygons, aPlotMode,
&gbr_metadata );
}
break;
case PAD_SHAPE_RECT:
default:
m_plotter->FlashPadRect( shape_pos, aPad->GetSize(),
aPad->GetOrientation(), aPlotMode, &gbr_metadata );
break;
}
}

View File

@ -617,24 +617,23 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE_BASE::syncPad( D_PAD* aPad )
wxPoint offset = aPad->GetOffset();
VECTOR2I c( wx_c.x, wx_c.y );
VECTOR2I sz( wx_sz.x, wx_sz.y );
RotatePoint( &offset, aPad->GetOrientation() );
solid->SetPos( VECTOR2I( c.x - offset.x, c.y - offset.y ) );
solid->SetOffset( VECTOR2I( offset.x, offset.y ) );
double orient = aPad->GetOrientation() / 10.0;
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
if( aPad->GetEffectiveShapes().size() == 1 )
{
solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
solid->SetShape( aPad->GetEffectiveShapes()[0]->Clone() );
}
else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
else
{
// JEY TODO:
// TOM TODO: move to SHAPE_COMPOUND...
SHAPE_POLY_SET outline;
outline.Append( aPad->GetCustomShapeAsPolygon() );
aPad->CustomShapeAsPolygonToBoardPosition( &outline, wx_c, aPad->GetOrientation() );
aPad->TransformShapeWithClearanceToPolygon( outline, 0 );
SHAPE_SIMPLE* shape = new SHAPE_SIMPLE();
@ -643,138 +642,7 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE_BASE::syncPad( D_PAD* aPad )
solid->SetShape( shape );
}
else
{
if( orient == 0.0 || orient == 90.0 || orient == 180.0 || orient == 270.0 )
{
if( orient == 90.0 || orient == 270.0 )
sz = VECTOR2I( sz.y, sz.x );
switch( aPad->GetShape() )
{
case PAD_SHAPE_OVAL:
if( sz.x == sz.y )
solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
else
{
VECTOR2I delta;
if( sz.x > sz.y )
delta = VECTOR2I( ( sz.x - sz.y ) / 2, 0 );
else
delta = VECTOR2I( 0, ( sz.y - sz.x ) / 2 );
SHAPE_SEGMENT* shape = new SHAPE_SEGMENT( c - delta, c + delta,
std::min( sz.x, sz.y ) );
solid->SetShape( shape );
}
break;
case PAD_SHAPE_RECT:
solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) );
break;
case PAD_SHAPE_TRAPEZOID:
{
wxPoint coords[4];
aPad->BuildPadPolygon( coords, wxSize( 0, 0 ), aPad->GetOrientation() );
SHAPE_SIMPLE* shape = new SHAPE_SIMPLE();
for( int ii = 0; ii < 4; ii++ )
{
shape->Append( wx_c + coords[ii] );
}
solid->SetShape( shape );
break;
}
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_ROUNDRECT:
{
SHAPE_POLY_SET outline;
aPad->BuildPadShapePolygon( outline, wxSize( 0, 0 ) );
// TransformRoundRectToPolygon creates only one convex polygon
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
SHAPE_SIMPLE* shape = new SHAPE_SIMPLE();
for( int ii = 0; ii < poly.PointCount(); ++ii )
shape->Append( poly.CPoint( ii ) );
solid->SetShape( shape );
}
break;
default:
wxLogTrace( "PNS", "unsupported pad shape" );
return nullptr;
}
}
else
{
switch( aPad->GetShape() )
{
// PAD_SHAPE_CIRCLE and PAD_SHAPE_CUSTOM already handled above
case PAD_SHAPE_OVAL:
if( sz.x == sz.y )
solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
else
{
wxPoint start;
wxPoint end;
int w = aPad->BuildSegmentFromOvalShape( start, end, aPad->GetOrientation(),
wxSize( 0, 0 ) );
SHAPE_SEGMENT* shape = new SHAPE_SEGMENT( start, end, w );
shape->Move( aPad->ShapePos() );
solid->SetShape( shape );
}
break;
case PAD_SHAPE_RECT:
case PAD_SHAPE_TRAPEZOID:
{
wxPoint coords[4];
aPad->BuildPadPolygon( coords, wxSize( 0, 0 ), aPad->GetOrientation() );
SHAPE_SIMPLE* shape = new SHAPE_SIMPLE();
for( int ii = 0; ii < 4; ii++ )
{
shape->Append( wx_c + coords[ii] );
}
solid->SetShape( shape );
break;
}
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_ROUNDRECT:
{
SHAPE_POLY_SET outline;
aPad->BuildPadShapePolygon( outline, wxSize( 0, 0 ) );
// TransformRoundRectToPolygon creates only one convex polygon
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
SHAPE_SIMPLE* shape = new SHAPE_SIMPLE();
for( int ii = 0; ii < poly.PointCount(); ++ii )
{
shape->Append( wxPoint( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );
}
solid->SetShape( shape );
break;
}
default:
wxLogTrace( "PNS", "unsupported pad shape" );
return nullptr;
}
}
}
return solid;
}

View File

@ -534,15 +534,17 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad )
case PAD_SHAPE_CUSTOM:
{
std::vector<wxPoint> polygonal_shape;
const SHAPE_POLY_SET& pad_shape = aPad->GetCustomShapeAsPolygon();
SHAPE_POLY_SET pad_shape;
aPad->MergePrimitivesAsPolygon( &pad_shape );
#ifdef EXPORT_CUSTOM_PADS_CONVEX_HULL
#ifdef EXPORT_CUSTOM_PADS_CONVEX_HULL
BuildConvexHull( polygonal_shape, pad_shape );
#else
#else
const SHAPE_LINE_CHAIN& p_outline = pad_shape.COutline( 0 );
for( int ii = 0; ii < p_outline.PointCount(); ++ii )
polygonal_shape.push_back( wxPoint( p_outline.CPoint( ii ) ) );
#endif
#endif
// The polygon must be closed
if( polygonal_shape.front() != polygonal_shape.back() )

View File

@ -596,13 +596,14 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent )
}
pad->SetPosition( wxPoint( anchor->x, anchor->y ) );
pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle );
pad->AddPrimitives( shapes );
pad->ClearFlags();
bool result = pad->MergePrimitivesAsPolygon();
pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle );
SHAPE_POLY_SET mergedPolygon;
pad->MergePrimitivesAsPolygon( &mergedPolygon );
if( !result )
if( mergedPolygon.OutlineCount() > 1 )
{
DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n"
"selected items do not form a single solid shape.") );

View File

@ -391,18 +391,14 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles )
{
if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
// the pad shape in zone can be its convex hull or the shape itself
SHAPE_POLY_SET outline( aPad->GetCustomShapeAsPolygon() );
int numSegs = std::max( GetArcToSegmentCount( aGap, m_high_def, 360.0 ), 6 );
double correction = GetCircletoPolyCorrectionFactor( numSegs );
outline.Inflate( KiROUND( aGap * correction ), numSegs );
aPad->CustomShapeAsPolygonToBoardPosition( &outline, aPad->GetPosition(),
aPad->GetOrientation() );
SHAPE_POLY_SET poly;
aPad->TransformShapeWithClearanceToPolygon( poly, aGap, m_high_def );
// the pad shape in zone can be its convex hull or the shape itself
if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
{
std::vector<wxPoint> convex_hull;
BuildConvexHull( convex_hull, outline );
BuildConvexHull( convex_hull, poly );
aHoles.NewOutline();
@ -410,7 +406,7 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles )
aHoles.Append( pt );
}
else
aHoles.Append( outline );
aHoles.Append( poly );
}
else
{