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, SHAPE_POLY_SET &aCornerBuffer,
int aWidth) const; 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, void transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule,
PCB_LAYER_ID aLayer, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer ) const; SHAPE_POLY_SET& aCornerBuffer ) const;
void buildPadShapePolygon( const D_PAD *aPad,
SHAPE_POLY_SET &aCornerBuffer,
wxSize aInflateValue ) const;
public: public:
SFVEC3D m_BgColorBot; ///< background bottom color SFVEC3D m_BgColorBot; ///< background bottom color
SFVEC3D m_BgColorTop; ///< background top 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/cring2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.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/croundsegment2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cpolygon4pts2d.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h" #include "../3d_rendering/3d_render_raytracing/shapes2D/ctriangle2d.h"
#include <board_adapter.h> #include <board_adapter.h>
#include <class_board.h> #include <class_board.h>
@ -45,13 +44,15 @@
#include <class_text_mod.h> #include <class_text_mod.h>
#include <convert_basic_shapes_to_polygon.h> #include <convert_basic_shapes_to_polygon.h>
#include <trigo.h> #include <trigo.h>
#include <geometry/shape_segment.h>
#include <geometry/geometry_utils.h> #include <geometry/geometry_utils.h>
#include <geometry/shape_circle.h>
#include <geometry/shape_simple.h>
#include <gr_text.h> #include <gr_text.h>
#include <utility> #include <utility>
#include <vector> #include <vector>
// These variables are parameters used in addTextSegmToContainer. // These variables are parameters used in addTextSegmToContainer.
// But addTextSegmToContainer is a call-back function, // But addTextSegmToContainer is a call-back function,
// so we cannot send them as arguments. // 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, void BOARD_ADAPTER::createNewPadWithClearance( const D_PAD* aPad,
CGENERICCONTAINER2D *aDstContainer, CGENERICCONTAINER2D *aDstContainer,
wxSize aClearanceValue ) const wxSize aClearanceValue ) const
{ {
// note: for most of shapes, aClearanceValue.x = aClearanceValue.y SHAPE_POLY_SET poly;
// 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;
if( !dx || !dy ) if( aClearanceValue.x != aClearanceValue.y )
{ {
wxLogTrace( m_logTrace, // Our shape-based builder can't handle differing x:y clearance values (which
wxT( "BOARD_ADAPTER::createNewPadWithClearance - found an invalid pad" ) ); // 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.
return; D_PAD dummy( *aPad );
dummy.SetSize( aPad->GetSize() + aClearanceValue + aClearanceValue );
dummy.TransformShapeWithClearanceToPolygon( poly, 0 );
} }
else
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: for( const std::shared_ptr<SHAPE>& shape : aPad->GetEffectiveShapes() )
{
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, switch( shape->Type() )
// 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 ) );
}
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
{ {
shape_offset.y = dy - dx; case SH_SEGMENT:
iwidth = dx * 2;
}
else //if( dy < dx )
{ {
shape_offset.x = dy - dx; const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape.get();
iwidth = dy * 2; 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;
RotatePoint( &shape_offset, aPad->GetOrientation() ); // Cannot add segments that have the same start and end point
if( Is_segment_a_circle( start3DU, end3DU ) )
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 );
// 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,
*aPad ) );
}
else
{
aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU,
iwidth * m_biuTo3Dunits,
*aPad ) );
}
}
}
break;
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_RECT:
{
// see pcbnew/board_items_to_polygon_shape_transform.cpp
wxPoint corners[4];
bool drawOutline;
// 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], aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU,
aClearanceValue.x * m_biuTo3Dunits, ( width / 2) * m_biuTo3Dunits,
*aPad ) ); *aPad ) );
} }
else else
{ {
aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[i - 1], aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU,
corners3DU[i & 3], width * m_biuTo3Dunits,
aClearanceValue.x * 2.0f * m_biuTo3Dunits,
*aPad ) ); *aPad ) );
} }
} }
} break;
}
break;
case PAD_SHAPE_ROUNDRECT: case SH_CIRCLE:
{
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], const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape.get();
rounding_radius * m_biuTo3Dunits, const int radius = circle->GetRadius() + aClearanceValue.x;
*aPad ) ); const SFVEC2F center( circle->GetCenter().x * m_biuTo3Dunits,
-circle->GetCenter().y * m_biuTo3Dunits );
aDstContainer->Add( new CFILLEDCIRCLE2D( center, radius * m_biuTo3Dunits, *aPad ) );
} }
else break;
{
aDstContainer->Add( new CROUNDSEGMENT2D( corners3DU[i - 1], case SH_SIMPLE:
corners3DU[i & 3], poly.AddOutline( static_cast<SHAPE_SIMPLE*>( shape.get() )->Vertices() );
rounding_radius * 2.0f * m_biuTo3Dunits, break;
*aPad ) );
case SH_POLY_SET:
poly = *(SHAPE_POLY_SET*) shape.get();
break;
default:
wxFAIL_MSG( "BOARD_ADAPTER::createNewPadWithClearance unimplemented shape" );
break;
} }
} }
} }
break;
case PAD_SHAPE_CHAMFERED_RECT: if( !poly.IsEmpty() )
{ {
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 );
}
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() );
if( aClearanceValue.x ) if( aClearanceValue.x )
polyList.Inflate( aClearanceValue.x, 32 ); poly.Inflate( aClearanceValue.x, 32 );
// Add the PAD polygon // Add the PAD polygon
Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad ); Convert_shape_line_polygon_to_triangles( poly, *aDstContainer, m_biuTo3Dunits, *aPad );
}
break;
} }
} }
// Based on:
// BuildPadDrillShapePolygon
// board_items_to_polygon_shape_transform.cpp
COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValue ) COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValue )
{ {
wxSize drillSize = aPad->GetDrillSize(); wxSize drillSize = aPad->GetDrillSize();
@ -587,29 +416,16 @@ COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValu
} }
else // Oblong hole else // Oblong hole
{ {
wxPoint start, end; const std::shared_ptr<SHAPE_SEGMENT>& seg = aPad->GetEffectiveHoleShape();
int width; 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; SFVEC2F end3DU ( seg->GetSeg().B.x * m_biuTo3Dunits,
start += aPad->GetPosition(); -seg->GetSeg().B.y * m_biuTo3Dunits );
end += aPad->GetPosition();
SFVEC2F start3DU( start.x * m_biuTo3Dunits, return new CROUNDSEGMENT2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad );
-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; return NULL;
@ -951,15 +767,13 @@ void BOARD_ADAPTER::AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneC
float radius = line_thickness/2; float radius = line_thickness/2;
if( radius > 0.0 ) // degenerated circles crash 3D viewer if( radius > 0.0 ) // degenerated circles crash 3D viewer
aDstContainer->Add( aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, radius,
new CFILLEDCIRCLE2D( start3DU, radius, *aZoneContainer ) );
*aZoneContainer ) );
} }
else else
{ {
aDstContainer->Add( aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, line_thickness,
new CROUNDSEGMENT2D( start3DU, end3DU, line_thickness, *aZoneContainer ) );
*aZoneContainer ) );
} }
} }
} }
@ -986,12 +800,9 @@ void BOARD_ADAPTER::buildPadShapeThickOutlineAsSegments( const D_PAD* aPad,
return; return;
} }
// For other shapes, draw polygon outlines // For other shapes, add outlines as thick segments in polygon buffer
SHAPE_POLY_SET corners; SHAPE_POLY_SET corners;
aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ) ); aPad->TransformShapeWithClearanceToPolygon( corners, 0 );
// Add outlines as thick segments in polygon buffer
const SHAPE_LINE_CHAIN& path = corners.COutline( 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 ) ) if( Is_segment_a_circle( start3DU, end3DU ) )
{ {
aDstContainer->Add( aDstContainer->Add( new CFILLEDCIRCLE2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
new CFILLEDCIRCLE2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits, *aPad ) ); *aPad ) );
} }
else else
{ {
aDstContainer->Add( aDstContainer->Add( new CROUNDSEGMENT2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
new CROUNDSEGMENT2D( start3DU, end3DU, aWidth * m_biuTo3Dunits, *aPad ) ); *aPad ) );
} }
} }
} }

View File

@ -33,13 +33,7 @@
#include "board_adapter.h" #include "board_adapter.h"
#include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.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/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/ccylinder.h"
#include "../3d_rendering/3d_render_raytracing/shapes3D/clayeritem.h"
#include <class_board.h> #include <class_board.h>
#include <class_module.h> #include <class_module.h>
@ -47,16 +41,17 @@
#include <class_pcb_text.h> #include <class_pcb_text.h>
#include <class_edge_mod.h> #include <class_edge_mod.h>
#include <class_zone.h> #include <class_zone.h>
#include <class_text_mod.h>
#include <convert_basic_shapes_to_polygon.h> #include <convert_basic_shapes_to_polygon.h>
#include <trigo.h> #include <trigo.h>
#include <utility>
#include <vector> #include <vector>
#include <thread> #include <thread>
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#ifdef PRINT_STATISTICS_3D_VIEWER
#include <profile.h> #include <profile.h>
#endif
void BOARD_ADAPTER::destroyLayers() void BOARD_ADAPTER::destroyLayers()
{ {
@ -501,13 +496,13 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED ) if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED )
{ {
pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly, inflate ); pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly, inflate );
pad->BuildPadDrillShapePolygon( m_through_inner_holes_poly, 0 ); pad->TransformHoleWithClearanceToPolygon( m_through_inner_holes_poly, 0 );
} }
else else
{ {
// If not plated, no copper. // If not plated, no copper.
pad->BuildPadDrillShapePolygon( m_through_outer_holes_poly_NPTH, inflate ); pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly_NPTH, inflate );
} }
} }
} }
@ -568,11 +563,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
// Note: NPTH pads are not drawn on copper layers when the pad // Note: NPTH pads are not drawn on copper layers when the pad
// has same shape as its hole // has same shape as its hole
transformPadsShapesWithClearanceToPolygon( module->Pads(), module->TransformPadsShapesWithClearanceToPolygon( curr_layer_id,
curr_layer_id, *layerPoly,
*layerPoly, 0,
0, true );
true );
// Micro-wave modules may have items on copper layers // Micro-wave modules may have items on copper layers
module->TransformGraphicTextWithClearanceToPolygonSet( curr_layer_id, module->TransformGraphicTextWithClearanceToPolygonSet( curr_layer_id,
@ -983,8 +977,8 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
} }
else else
{ {
transformPadsShapesWithClearanceToPolygon( module->TransformPadsShapesWithClearanceToPolygon( curr_layer_id, *layerPoly, 0,
module->Pads(), curr_layer_id, *layerPoly, 0, false ); false );
} }
// On tech layers, use a poor circle approximation, only for texts (stroke font) // 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. * 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) 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -25,9 +25,7 @@
/** /**
* @file create_layer_poly.cpp * @file create_layer_poly.cpp
* @brief This file implements the creation of the pcb board items in the poly * @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: * contours format.
* board_items_to_polygon_shape_transform.cpp
* board_items_to_polygon_shape_transform.cpp
*/ */
#include "board_adapter.h" #include "board_adapter.h"
@ -36,93 +34,20 @@
#include <class_module.h> #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, void BOARD_ADAPTER::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
SHAPE_POLY_SET& aCornerBuffer, SHAPE_POLY_SET& aCornerBuffer,
int aWidth ) const int aWidth ) const
{ {
if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) // Draw a ring
{ {
TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), TransformRingToPolygon( aCornerBuffer, aPad->ShapePos(), aPad->GetSize().x / 2,
aPad->GetSize().x / 2, ARC_HIGH_DEF, aWidth ); ARC_HIGH_DEF, aWidth );
return; return;
} }
// For other shapes, add outlines as thick segments in polygon buffer
// For other shapes, draw polygon outlines
SHAPE_POLY_SET corners; SHAPE_POLY_SET corners;
aPad->TransformShapeWithClearanceToPolygon( corners, 0 );
buildPadShapePolygon( aPad, corners, wxSize( 0, 0 ) );
// Add outlines as thick segments in polygon buffer
const SHAPE_LINE_CHAIN& path = corners.COutline( 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& a = path.CPoint( ii );
const VECTOR2I& b = path.CPoint( ii + 1 ); const VECTOR2I& b = path.CPoint( ii + 1 );
TransformSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), TransformSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ),
wxPoint( b.x, b.y ), ARC_HIGH_DEF, aWidth ); 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, void BOARD_ADAPTER::transformGraphicModuleEdgeToPolygonSet( const MODULE *aModule,
PCB_LAYER_ID aLayer, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer ) const 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;
{
EDGE_MODULE*outline = (EDGE_MODULE*) item;
if( outline->GetLayer() != aLayer ) if( outline->GetLayer() == aLayer )
break; outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, 0 );
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 ); double correctionFactor = m_boardAdapter.GetCircleCorrectionFactor( nrSegments );
int correction = radius * ( correctionFactor - 1 ); int correction = radius * ( correctionFactor - 1 );
pad->BuildPadDrillShapePolygon( pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly,
tht_outer_holes_poly, copperThickness + correction ); 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 * 4 = BOTTOM_LEFT
* 8 = BOTTOM_RIGHT * 8 = BOTTOM_RIGHT
* One can have more than one chamfered corner by ORing the corner identifers * One can have more than one chamfered corner by ORing the corner identifers
* @param aApproxErrorMax = the IU allowed for error in approximation * @param aError = the IU allowed for error in approximation
* @param aMinSegPerCircleCount = the minimal segments per circle count in approximation * @param aSegsPerCircle = the minimal segments per circle count in approximation
* (aApproxErrorMax can generate must more seg count than aMinSegPerCircleCount) * (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 * 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. * (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, void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer,
const wxPoint& aPosition, const wxSize& aSize, const wxPoint& aPosition, const wxSize& aSize,
double aRotation, int aCornerRadius, double aRotation, int aCornerRadius,
double aChamferRatio, int aChamferCorners, double aChamferRatio, int aChamferCorners, int aError );
int aApproxErrorMax, int aMinSegPerCircleCount = 16 );
/** /**
* Function TransformRoundedEndsSegmentToPolygon * Function TransformRoundedEndsSegmentToPolygon

View File

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

View File

@ -40,8 +40,10 @@
#include <class_edge_mod.h> #include <class_edge_mod.h>
#include <convert_basic_shapes_to_polygon.h> #include <convert_basic_shapes_to_polygon.h>
#include <geometry/geometry_utils.h> #include <geometry/geometry_utils.h>
#include <geometry/shape_segment.h>
#include <math/util.h> // for KiROUND #include <math/util.h> // for KiROUND
// A helper struct for the callback function // A helper struct for the callback function
// These variables are parameters used in addTextSegmToPoly. // These variables are parameters used in addTextSegmToPoly.
// But addTextSegmToPoly is a call-back function, // 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. // Most of time pads are using the segment count given by aError value.
const int pad_min_seg_per_circle_count = 16; const int pad_min_seg_per_circle_count = 16;
double angle = m_Orient; double angle = m_Orient;
int dx = (m_Size.x / 2) + aClearanceValue; int dx = m_Size.x / 2;
int dy = (m_Size.y / 2) + aClearanceValue; int dy = m_Size.y / 2;
wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset, wxPoint padShapePos = ShapePos(); // Note: for pad having a shape offset,
// the pad position is NOT the shape position // the pad position is NOT the shape position
@ -528,35 +530,20 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
switch( GetShape() ) switch( GetShape() )
{ {
case PAD_SHAPE_CIRCLE: case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError );
break;
case PAD_SHAPE_OVAL: case PAD_SHAPE_OVAL:
// If the oval is actually a circle (same x/y size), treat it the same
if( dx == dy ) if( dx == dy )
{ {
TransformCircleToPolygon( aCornerBuffer, padShapePos, dx, aError ); TransformCircleToPolygon( aCornerBuffer, padShapePos, dx + aClearanceValue, aError );
} }
else else
{ {
int width; int half_width = std::min( dx, dy );
wxPoint shape_offset; wxPoint delta( dx - half_width, dy - half_width );
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;
}
RotatePoint( &shape_offset, angle ); RotatePoint( &delta, angle );
wxPoint start = padShapePos - shape_offset;
wxPoint end = padShapePos + shape_offset; TransformOvalToPolygon( aCornerBuffer, padShapePos - delta, padShapePos + delta,
TransformOvalToPolygon( aCornerBuffer, start, end, width, aError ); half_width * 2 + aClearanceValue, aError );
} }
break; break;
@ -564,14 +551,21 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_RECT: 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]; 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; SHAPE_POLY_SET outline;
outline.NewOutline(); outline.NewOutline();
for( wxPoint& corner : corners ) for( wxPoint& corner : corners )
{ {
RotatePoint( &corner, angle );
corner += padShapePos; corner += padShapePos;
outline.Append( corner.x, corner.y ); outline.Append( corner.x, corner.y );
} }
@ -615,9 +609,11 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
case PAD_SHAPE_CUSTOM: case PAD_SHAPE_CUSTOM:
{ {
SHAPE_POLY_SET outline; // Will contain the corners in board coordinates SHAPE_POLY_SET outline;
outline.Append( m_customShapeAsPolygon ); MergePrimitivesAsPolygon( &outline );
CustomShapeAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() ); outline.Rotate( -DECIDEG2RAD( m_Orient ) );
outline.Move( VECTOR2I( m_Pos ) );
// TODO: do we need the Simplify() & Fracture() if we're not inflating? // TODO: do we need the Simplify() & Fracture() if we're not inflating?
outline.Simplify( SHAPE_POLY_SET::PM_FAST ); outline.Simplify( SHAPE_POLY_SET::PM_FAST );
@ -640,91 +636,18 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
/* bool D_PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
* Function BuildPadShapePolygon int aError ) const
* 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,
int aError ) const
{ {
wxSize drillsize = GetDrillSize(); wxSize drillsize = GetDrillSize();
if( !drillsize.x || !drillsize.y ) if( !drillsize.x || !drillsize.y )
return false; return false;
if( drillsize.x == drillsize.y ) // usual round hole const std::shared_ptr<SHAPE_SEGMENT>& seg = GetEffectiveHoleShape();
{
int radius = ( drillsize.x / 2 ) + aInflateValue;
TransformCircleToPolygon( aCornerBuffer, GetPosition(), radius, aError );
}
else // Oblong hole
{
wxPoint start, end;
int width;
GetOblongGeometry( GetDrillSize(), &start, &end, &width ); TransformSegmentToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
aError, seg->GetWidth() + aInflateValue * 2 );
start += GetPosition();
end += GetPosition();
width += aInflateValue * 2;
TransformSegmentToPolygon( aCornerBuffer, start, end, aError, width );
}
return true; return true;
} }

View File

@ -243,15 +243,8 @@ public:
double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; } double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; }
void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; } void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; }
void SetZoneConnection( ZONE_CONNECTION aType ) void SetZoneConnection( ZONE_CONNECTION aType ) { m_ZoneConnection = aType; }
{ ZONE_CONNECTION GetZoneConnection() const { return m_ZoneConnection; }
m_ZoneConnection = aType;
}
ZONE_CONNECTION GetZoneConnection() const
{
return m_ZoneConnection;
}
void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; } void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; }
int GetThermalWidth() const { return m_ThermalWidth; } 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 DRAWSEGMENT;
class PARAM_CFG; class PARAM_CFG;
class SHAPE;
class SHAPE_SEGMENT;
enum CUST_PAD_SHAPE_IN_ZONE enum CUST_PAD_SHAPE_IN_ZONE
{ {
@ -52,7 +54,6 @@ class EDA_3D_CANVAS;
class MODULE; class MODULE;
class EDGE_MODULE; class EDGE_MODULE;
class TRACK; class TRACK;
class MSG_PANEL_INFO;
namespace KIGFX namespace KIGFX
{ {
@ -115,10 +116,6 @@ public:
class D_PAD : public BOARD_CONNECTED_ITEM 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: public:
D_PAD( MODULE* parent ); D_PAD( MODULE* parent );
@ -181,34 +178,14 @@ public:
* Set the pad name (sometimes called pad number, although * Set the pad name (sometimes called pad number, although
* it can be an array reference like AA12). * it can be an array reference like AA12).
*/ */
void SetName( const wxString& aName ) void SetName( const wxString& aName ) { m_name = aName; }
{ const wxString& GetName() const { return m_name; }
m_name = aName;
}
/** /**
* Set the pad function (pin name in schematic) * Set the pad function (pin name in schematic)
*/ */
void SetPinFunction( const wxString& aName ) void SetPinFunction( const wxString& aName ) { m_pinFunction = aName; }
{ const wxString& GetPinFunction() const { return m_pinFunction; }
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;
}
bool PadNameEqual( const D_PAD* other ) const bool PadNameEqual( const D_PAD* other ) const
{ {
@ -219,8 +196,8 @@ public:
* Function GetShape * Function GetShape
* @return the shape of this pad. * @return the shape of this pad.
*/ */
PAD_SHAPE_T GetShape() const { return m_padShape; } void SetShape( PAD_SHAPE_T aShape ) { m_padShape = aShape; m_shapesDirty = true; }
void SetShape( PAD_SHAPE_T aShape ) { m_padShape = aShape; m_boundingRadius = -1; } PAD_SHAPE_T GetShape() const { return m_padShape; }
void SetPosition( const wxPoint& aPos ) override { m_Pos = aPos; } void SetPosition( const wxPoint& aPos ) override { m_Pos = aPos; }
const wxPoint GetPosition() const override { return m_Pos; } const wxPoint GetPosition() const override { return m_Pos; }
@ -259,7 +236,7 @@ public:
void SetAnchorPadShape( PAD_SHAPE_T aShape ) void SetAnchorPadShape( PAD_SHAPE_T aShape )
{ {
m_anchorPadShape = ( aShape == PAD_SHAPE_RECT ) ? PAD_SHAPE_RECT : PAD_SHAPE_CIRCLE; 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; return ( GetLayerSet() & LSET::AllCuMask() ) != 0;
} }
void SetY( int y ) { m_Pos.y = y; } void SetY( int y ) { m_Pos.y = y; m_shapesDirty = true; }
void SetX( int x ) { m_Pos.x = x; } void SetX( int x ) { m_Pos.x = x; m_shapesDirty = true; }
void SetPos0( const wxPoint& aPos ) { m_Pos0 = aPos; } void SetPos0( const wxPoint& aPos ) { m_Pos0 = aPos; }
const wxPoint& GetPos0() const { return m_Pos0; } const wxPoint& GetPos0() const { return m_Pos0; }
@ -281,16 +258,16 @@ public:
void SetY0( int y ) { m_Pos0.y = y; } void SetY0( int y ) { m_Pos0.y = y; }
void SetX0( int x ) { m_Pos0.x = x; } 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; } 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; } 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; } 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; } const wxPoint& GetOffset() const { return m_Offset; }
/** /**
@ -303,67 +280,35 @@ public:
* a arc * a arc
* a curve * a curve
*/ */
void AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, void AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness );
bool aMergePrimitives = true ); void AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness );
void AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness, void AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness );
bool aMergePrimitives = true ); void AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness );
void AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness, void AddPrimitiveRect( 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 AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle, 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, 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 ); bool GetBestAnchorPosition( VECTOR2I& aPos );
/** /**
* Merge all basic shapes, converted to a polygon in one polygon, * Merge all basic shapes to a SHAPE_POLY_SET
* 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)
* Note: The corners coordinates are relative to the pad position, orientation 0, * 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 * clear the basic shapes list
*/ */
void DeletePrimitivesList(); 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 * Accessor to the basic shape list
*/ */
const std::vector<PAD_CS_PRIMITIVE>& GetPrimitives() const { return m_basicShapes; } 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; void Flip( const wxPoint& aCentre, bool aFlipLeftRight ) override;
/** /**
@ -373,24 +318,18 @@ public:
/** /**
* Mirror the primitives about a coordinate * Mirror the primitives about a coordinate
*
* @param aX the x coordinate about which to mirror
*/ */
void MirrorXPrimitives( int aX ); void MirrorXPrimitives( int aX );
/** /**
* Import to the basic shape list * 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 * 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 GetOrientationDegrees() const { return m_Orient/10.0; }
double GetOrientationRadians() const { return m_Orient*M_PI/1800; } double GetOrientationRadians() const { return m_Orient*M_PI/1800; }
void SetDrillShape( PAD_DRILL_SHAPE_T aDrillShape ) void SetDrillShape( PAD_DRILL_SHAPE_T aShape ) { m_drillShape = aShape; m_shapesDirty = true; }
{ m_drillShape = aDrillShape; }
PAD_DRILL_SHAPE_T GetDrillShape() const { return m_drillShape; } PAD_DRILL_SHAPE_T GetDrillShape() const { return m_drillShape; }
/** /**
* Function GetOblongGeometry calculates the start point, end point and width of an * JEY TODO: temporary until Tom is done with DRC stuff....
* 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.
*/ */
void GetOblongGeometry( const wxSize& aDrillOrPadSize, void GetOblongGeometry( const wxSize& aDrillOrPadSize,
wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const; wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const;
@ -461,20 +390,47 @@ public:
double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; } double GetLocalSolderPasteMarginRatio() const { return m_LocalSolderPasteMarginRatio; }
void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; } void SetLocalSolderPasteMarginRatio( double aRatio ) { m_LocalSolderPasteMarginRatio = aRatio; }
/** /**
* Function TransformShapeWithClearanceToPolygon * Function TransformShapeWithClearanceToPolygon
* Convert the pad shape to a closed polygon * Convert the pad shape to a closed polygon. Circles and arcs are approximated by segments.
* Used in filling zones calculations
* Circles and arcs are approximated by segments
* @param aCornerBuffer = a buffer to store the polygon * @param aCornerBuffer = a buffer to store the polygon
* @param aClearanceValue = the clearance around the pad * @param aClearanceValue = the clearance around the pad
* @param aMaxError = Maximum error from true when converting arcs * @param aMaxError = maximum error from true when converting arcs
* @param ignoreLineWidth = used for edge cut items where the line width is only * @param ignoreLineWidth = used for edge cuts where the line width is only for visualization
* for visualization
*/ */
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, 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 * Function GetLocalClearanceOverrides
@ -515,17 +471,14 @@ public:
*/ */
wxSize GetSolderPasteMargin() const; wxSize GetSolderPasteMargin() const;
void SetZoneConnection( ZONE_CONNECTION aType ) void SetZoneConnection( ZONE_CONNECTION aType ) { m_ZoneConnection = aType; }
{ ZONE_CONNECTION GetZoneConnection() const { return m_ZoneConnection; }
m_ZoneConnection = aType;
}
ZONE_CONNECTION GetZoneConnection() const; /**
* Return the zone connection in effect (either locally overridden or overridden in the
ZONE_CONNECTION GetLocalZoneConnection() const * parent module).
{ */
return m_ZoneConnection; ZONE_CONNECTION GetEffectiveZoneConnection() const;
}
void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; } void SetThermalWidth( int aWidth ) { m_ThermalWidth = aWidth; }
int GetThermalWidth() const; int GetThermalWidth() const;
@ -534,110 +487,12 @@ public:
int GetThermalGap() const; int GetThermalGap() const;
/** /**
* Function BuildPadPolygon * Function SetRoundRectCornerRadius
* 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
* Has meaning only for rounded rect pads * Has meaning only for rounded rect pads
* @return The radius of the rounded corners for this pad. * @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 ); void SetRoundRectCornerRadius( double aRadius );
int GetRoundRectCornerRadius() const;
/**
* 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;
}
wxPoint ShapePos() const; wxPoint ShapePos() const;
@ -648,36 +503,8 @@ public:
* Cannot be > 0.5 * Cannot be > 0.5
* the normalized IPC-7351C value is 0.25 * the normalized IPC-7351C value is 0.25
*/ */
double GetRoundRectRadiusRatio() const void SetRoundRectRadiusRatio( double aRadiusScale );
{ double GetRoundRectRadiusRatio() const { return m_padRoundRectRadiusScale; }
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;
}
/** /**
* has meaning only for chamfered rect pads * has meaning only for chamfered rect pads
@ -685,19 +512,8 @@ public:
* of the rounded corners. * of the rounded corners.
* Cannot be < 0.5 and obviously must be > 0 * Cannot be < 0.5 and obviously must be > 0
*/ */
void SetChamferRectRatio( double aChamferScale ) void SetChamferRectRatio( double aChamferScale );
{ double GetChamferRectRatio() const { return m_padChamferRectScale; }
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; }
/** /**
* has meaning only for chamfered rect pads * has meaning only for chamfered rect pads
@ -705,10 +521,8 @@ public:
* RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT, * RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT,
* RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT * RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT
*/ */
void SetChamferPositions( int aChamferPositions ) void SetChamferPositions( int aPositions ) { m_chamferPositions = aPositions; }
{ int GetChamferPositions() const { return m_chamferPositions; }
m_chamferPositions = aChamferPositions;
}
/** /**
* Function GetSubRatsnest * Function GetSubRatsnest
@ -814,40 +628,36 @@ public:
private: private:
/** /**
* Function boundingRadius * Function calcBoundingRadius
* returns a calculated radius of a bounding circle for this pad. * 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: void buildEffectiveShapes() const;
// Actually computed and cached on demand by the accessor
mutable int m_boundingRadius; ///< radius of the circle containing the pad shape
private:
wxString m_name; ///< pad name (pin number in schematic) wxString m_name; ///< pad name (pin number in schematic)
wxString m_pinFunction; ///< pin function 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 wxPoint m_Pos; ///< pad Position on board
PAD_SHAPE_T m_padShape; ///< Shape: PAD_SHAPE_CIRCLE, PAD_SHAPE_RECT, PAD_SHAPE_T m_padShape; ///< Shape: PAD_SHAPE_CIRCLE, PAD_SHAPE_RECT,
///< PAD_SHAPE_OVAL, PAD_SHAPE_TRAPEZOID, ///< PAD_SHAPE_OVAL, PAD_SHAPE_TRAPEZOID,
///< PAD_SHAPE_ROUNDRECT, PAD_SHAPE_POLYGON ///< 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, /** for free shape pads: a list of basic shapes,
* in local coordinates, orient 0, coordinates relative to m_Pos * in local coordinates, orient 0, coordinates relative to m_Pos
* They are expected to define only one copper area. * They are expected to define only one copper area.
*/ */
std::vector<PAD_CS_PRIMITIVE> m_basicShapes; 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: * How to build the custom shape in zone, to create the clearance area:
* CUST_PAD_SHAPE_IN_ZONE_OUTLINE = use pad shape * CUST_PAD_SHAPE_IN_ZONE_OUTLINE = use pad shape
@ -871,10 +681,10 @@ private: // Private variable members:
///< to corner radius, default 0.25 ///< to corner radius, default 0.25
double m_padChamferRectScale; ///< scaling factor from smallest m_Size coord double m_padChamferRectScale; ///< scaling factor from smallest m_Size coord
///< to chamfer value, default 0.25 ///< 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_T m_anchorPadShape; ///< for custom shaped pads: shape of pad anchor,
///< PAD_SHAPE_RECT, PAD_SHAPE_CIRCLE ///< PAD_SHAPE_RECT, PAD_SHAPE_CIRCLE
/** /**
* m_Offset is useful only for oblong and rect pads (it can be used for other * m_Offset is useful only for oblong and rect pads (it can be used for other

View File

@ -784,10 +784,10 @@ void ZONE_CONTAINER::Mirror( const wxPoint& aMirrorRef, bool aMirrorLeftRight )
ZONE_CONNECTION ZONE_CONTAINER::GetPadConnection( D_PAD* aPad ) const 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; return m_PadConnection;
else 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() ); RotatePoint( pt1, pad->ShapePos(), pad->GetOrientation() );
SHAPE_POLY_SET padPolySet; 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 ); const SHAPE_LINE_CHAIN& padOutline = padPolySet.COutline( 0 );
SHAPE_LINE_CHAIN::INTERSECTIONS intersections; SHAPE_LINE_CHAIN::INTERSECTIONS intersections;

View File

@ -551,7 +551,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
else else
m_SolderPasteMarginRatioCtrl->SetValue( msg ); m_SolderPasteMarginRatioCtrl->SetValue( msg );
switch( m_dummyPad->GetLocalZoneConnection() ) switch( m_dummyPad->GetZoneConnection() )
{ {
default: default:
case ZONE_CONNECTION::INHERITED: m_ZoneConnectionChoice->SetSelection( 0 ); break; 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->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" ) ); 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->SetRoundRectRadiusRatio( m_padMaster->GetRoundRectRadiusRatio() );
m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() ); m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() );
m_currentPad->SetChamferPositions( m_padMaster->GetChamferPositions() ); 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. // rounded rect pads with radius ratio = 0 are in fact rect pads.
// So set the right shape (and perhaps issues with a radius = 0) // So set the right shape (and perhaps issues with a radius = 0)

View File

@ -1084,10 +1084,9 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
// Fast test to detect a pad candidate inside the text bounding box // Fast test to detect a pad candidate inside the text bounding box
// Finer test (time consumming) is made only for pads near the text. // Finer test (time consumming) is made only for pads near the text.
int bb_radius = pad->GetBoundingRadius() + minClearance; 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; continue;
SHAPE_POLY_SET padOutline; 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 ) 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 int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - aRefPad->ShapePos() ) );
wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
int center2center = KiROUND( EuclideanNorm( relativePadPos ) );
// Quick test: Clearance is OK if the bounding circles are further away than aMinClearance // Quick test: Clearance is OK if the bounding circles are further away than aMinClearance
if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance ) if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
return true; return true;
/* Here, pads are near and DRC depends on the pad shapes. We must compare distance using // JEY TODO:
* a fine shape analysis. // TOM TODO: MTV only works as a proxy for actual-distance for convex shapes
* 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;
// swap pads to make comparisons easier VECTOR2I mtv;
// Note also a ROUNDRECT pad with a corner radius = r can be considered as VECTOR2I maxMtv( 0, 0 );
// a smaller RECT (size - 2*r) with a clearance increased by r
// priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other for( const std::shared_ptr<SHAPE>& aShape : aRefPad->GetEffectiveShapes() )
if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
{ {
// pad ref shape is here oval, rect, roundrect, chamfered rect, trapezoid or custom for( const std::shared_ptr<SHAPE>& bShape : aPad->GetEffectiveShapes() )
switch( aPad->GetShape() )
{ {
case PAD_SHAPE_CIRCLE: if( aShape->Collide( bShape.get(), aMinClearance, mtv ) )
swap_pads = true; {
break; if( mtv.SquaredEuclideanNorm() > maxMtv.SquaredEuclideanNorm() )
maxMtv = mtv;
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( swap_pads ) if( maxMtv.x > 0 || maxMtv.y > 0 )
{ {
std::swap( aRefPad, aPad ); *aActual = std::max( 0, aMinClearance - maxMtv.EuclideanNorm() );
relativePadPos = -relativePadPos; return false;
} }
bool diag = true; return 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;
} }

View File

@ -594,8 +594,14 @@ static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
{ {
fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR ); 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]; 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 ) 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 ); 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 ) 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: case PAD_SHAPE_CHAMFERED_RECT:
{ {
SHAPE_POLY_SET polySet; 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(), TransformRoundChamferedRectToPolygon( polySet, wxPoint( 0, 0 ), aPad->GetSize(),
0.0, corner_radius, 0.0, 0, ARC_HIGH_DEF ); 0.0, corner_radius, 0.0, 0, ARC_HIGH_DEF );
std::vector< wxRealPoint > cornerList; std::vector< wxRealPoint > cornerList;

View File

@ -1479,8 +1479,8 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const
if( aPad->GetLocalClearance() != 0 ) if( aPad->GetLocalClearance() != 0 )
StrPrintf( &output, " (clearance %s)", FormatInternalUnits( aPad->GetLocalClearance() ).c_str() ); StrPrintf( &output, " (clearance %s)", FormatInternalUnits( aPad->GetLocalClearance() ).c_str() );
if( aPad->GetZoneConnection() != ZONE_CONNECTION::INHERITED ) if( aPad->GetEffectiveZoneConnection() != ZONE_CONNECTION::INHERITED )
StrPrintf( &output, " (zone_connect %d)", static_cast<int>( aPad->GetZoneConnection() ) ); StrPrintf( &output, " (zone_connect %d)", static_cast<int>( aPad->GetEffectiveZoneConnection() ) );
if( aPad->GetThermalWidth() != 0 ) if( aPad->GetThermalWidth() != 0 )
StrPrintf( &output, " (thermal_width %s)", FormatInternalUnits( aPad->GetThermalWidth() ).c_str() ); 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 * 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; 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++ ) for( auto iter = poly_no_hole.CIterate(); iter; iter++ )
points.emplace_back( iter->x, iter->y ); 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, void D_PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness )
bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_POLYGON ); PAD_CS_PRIMITIVE shape( S_POLYGON );
shape.m_Poly = aPoly; shape.m_Poly = aPoly;
shape.m_Thickness = aThickness; shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape ); m_basicShapes.push_back( shape );
m_shapesDirty = true;
if( aMergePrimitives )
MergePrimitivesAsPolygon();
} }
void D_PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness, void D_PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_SEGMENT ); PAD_CS_PRIMITIVE shape( S_SEGMENT );
shape.m_Start = aStart; shape.m_Start = aStart;
shape.m_End = aEnd; shape.m_End = aEnd;
shape.m_Thickness = aThickness; shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape ); m_basicShapes.push_back( shape );
m_shapesDirty = true;
if( aMergePrimitives )
MergePrimitivesAsPolygon();
} }
void D_PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle, void D_PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
int aThickness, bool aMergePrimitives ) int aThickness )
{ {
PAD_CS_PRIMITIVE shape( S_ARC ); PAD_CS_PRIMITIVE shape( S_ARC );
shape.m_Start = aCenter; 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_ArcAngle = aArcAngle;
shape.m_Thickness = aThickness; shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape ); m_basicShapes.push_back( shape );
m_shapesDirty = true;
if( aMergePrimitives )
MergePrimitivesAsPolygon();
} }
void D_PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1, 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 ); PAD_CS_PRIMITIVE shape( S_CURVE );
shape.m_Start = aStart; 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_Ctrl2 = aCtrl2;
shape.m_Thickness = aThickness; shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape ); m_basicShapes.push_back( shape );
m_shapesDirty = true;
if( aMergePrimitives )
MergePrimitivesAsPolygon();
} }
void D_PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness, void D_PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness )
bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_CIRCLE ); PAD_CS_PRIMITIVE shape( S_CIRCLE );
shape.m_Start = aCenter; shape.m_Start = aCenter;
shape.m_Radius = aRadius; shape.m_Radius = aRadius;
shape.m_Thickness = aThickness; shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape ); m_basicShapes.push_back( shape );
m_shapesDirty = true;
if( aMergePrimitives )
MergePrimitivesAsPolygon();
} }
void D_PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness, void D_PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
bool aMergePrimitives )
{ {
PAD_CS_PRIMITIVE shape( S_RECT ); PAD_CS_PRIMITIVE shape( S_RECT );
shape.m_Start = aStart; shape.m_Start = aStart;
shape.m_End = aEnd; shape.m_End = aEnd;
shape.m_Thickness = aThickness; shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape ); m_basicShapes.push_back( shape );
m_shapesDirty = true;
if( aMergePrimitives )
MergePrimitivesAsPolygon();
} }
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 // clear old list
m_basicShapes.clear(); m_basicShapes.clear();
@ -257,17 +241,16 @@ bool D_PAD::SetPrimitives( const std::vector<PAD_CS_PRIMITIVE>& aPrimitivesList
if( aPrimitivesList.size() ) if( aPrimitivesList.size() )
m_basicShapes = aPrimitivesList; m_basicShapes = aPrimitivesList;
// Only one polygon is expected (pad area = only one copper area) m_shapesDirty = true;
return MergePrimitivesAsPolygon();
} }
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 ) for( const auto& prim : aPrimitivesList )
m_basicShapes.push_back( prim ); 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() void D_PAD::DeletePrimitivesList()
{ {
m_basicShapes.clear(); 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 ) switch( bshape.m_Shape )
{ {
case S_CURVE: 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 ); BEZIER_POLY converter( ctrlPoints );
std::vector< wxPoint> poly; std::vector< wxPoint> poly;
converter.GetPoly( poly, bshape.m_Thickness ); converter.GetPoly( poly, bshape.m_Thickness );
for( unsigned ii = 1; ii < poly.size(); ii++ ) for( unsigned ii = 1; ii < poly.size(); ii++ )
{ {
TransformSegmentToPolygon( TransformSegmentToPolygon( polyset, poly[ ii - 1 ], poly[ ii ], aError,
aux_polyset, poly[ ii - 1 ], poly[ ii ], aError, bshape.m_Thickness ); bshape.m_Thickness );
} }
break; break;
} }
case S_SEGMENT: // usual segment : line with rounded ends 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 ); bshape.m_Thickness );
break; break;
} }
case S_ARC: // Arc with rounded ends 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 ); aError, bshape.m_Thickness );
break; break;
} }
@ -320,80 +303,63 @@ bool D_PAD::buildCustomPadPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError )
case S_CIRCLE: // ring or circle case S_CIRCLE: // ring or circle
{ {
if( bshape.m_Thickness ) // ring 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 ); bshape.m_Thickness );
else // Filled circle else // Filled circle
TransformCircleToPolygon( aux_polyset, bshape.m_Start, bshape.m_Radius, aError ); TransformCircleToPolygon( polyset, bshape.m_Start, bshape.m_Radius, aError );
break; break;
} }
case S_RECT: 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 case S_POLYGON: // polygon
{ {
if( bshape.m_Poly.size() < 2 ) SHAPE_POLY_SET poly;
break; // Malformed polygon. poly.NewOutline();
// Insert the polygon: if( bshape.m_Shape == S_RECT )
const std::vector< wxPoint>& poly = bshape.m_Poly;
aux_polyset.NewOutline();
if( bshape.m_Thickness )
{ {
SHAPE_POLY_SET polyset; poly.Append( bshape.m_Start );
polyset.NewOutline(); poly.Append( bshape.m_End.x, bshape.m_Start.y );
poly.Append( bshape.m_End );
for( const wxPoint& pt : poly ) poly.Append( bshape.m_Start.x, bshape.m_End.y );
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 );
} }
else else
{ {
for( const wxPoint& pt : poly ) for( const wxPoint& pt : bshape.m_Poly )
aux_polyset.Append( pt.x, pt.y ); poly.Append( pt );
} }
if( bshape.m_Shape == S_RECT ) if( bshape.m_Thickness )
bshape.m_Poly.clear(); {
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; break;
default: default:
// un-handled primitive // un-handled primitive
wxASSERT_MSG( false, wxT( "D_PAD::buildCustomPadPolygon not implemented for " wxASSERT_MSG( false, "D_PAD::addCustomPadPrimitivesToPolygon not implemented for "
+ BOARD_ITEM::ShowShape( bshape.m_Shape ) ) ); + BOARD_ITEM::ShowShape( bshape.m_Shape ) );
break; 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 // 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 ); aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
} }
return aMergedPolygon->OutlineCount() <= 1;
} }
/* Merge all basic shapes, converted to a polygon in one polygon, void D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon ) const
* return true if OK, false in there is more than one polygon
* in aMergedPolygon
*/
bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon )
{ {
auto board = GetBoard(); auto board = GetBoard();
int maxError = ARC_HIGH_DEF; int maxError = ARC_HIGH_DEF;
@ -401,58 +367,35 @@ bool D_PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon )
if( board ) if( board )
maxError = board->GetDesignSettings().m_MaxError; maxError = board->GetDesignSettings().m_MaxError;
// if aMergedPolygon == NULL, use m_customShapeAsPolygon as target
if( !aMergedPolygon )
aMergedPolygon = &m_customShapeAsPolygon;
aMergedPolygon->RemoveAllContours(); aMergedPolygon->RemoveAllContours();
// Add the anchor pad shape in aMergedPolygon, others in aux_polyset: // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
// The anchor pad is always at 0,0 // The anchor pad is always at 0,0
switch( GetAnchorPadShape() ) switch( GetAnchorPadShape() )
{ {
default:
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError );
break;
case PAD_SHAPE_RECT: case PAD_SHAPE_RECT:
{ {
SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y ); SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
aMergedPolygon->AddOutline( rect.Outline() ); aMergedPolygon->AddOutline( rect.Outline() );
}
break;
default:
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError );
break; break;
} }
}
if( !buildCustomPadPolygon( aMergedPolygon, maxError ) ) addCustomPadPrimitivesToPolygon( 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 ) );
} }
bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos ) bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos )
{ {
SHAPE_POLY_SET poly; SHAPE_POLY_SET poly;
addCustomPadPrimitivesToPolygon( &poly, ARC_LOW_DEF );
if( !buildCustomPadPolygon( &poly, ARC_LOW_DEF ) ) if( poly.OutlineCount() > 1 )
return false; return false;
const int minSteps = 10; const int minSteps = 10;

View File

@ -44,7 +44,9 @@
#include <gal/graphics_abstraction_layer.h> #include <gal/graphics_abstraction_layer.h>
#include <geometry/geometry_utils.h> #include <geometry/geometry_utils.h>
#include <geometry/shape_line_chain.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; 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 // Choose drawing settings depending on if we are drawing a pad itself or a hole
if( aLayer == LAYER_PADS_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES ) if( aLayer == LAYER_PADS_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
{ {
m_gal->Save(); const std::shared_ptr<SHAPE_SEGMENT>& seg = aPad->GetEffectiveHoleShape();
m_gal->Translate( VECTOR2D( aPad->GetPosition() ) );
m_gal->Rotate( -aPad->GetOrientationRadians() );
// Drawing hole: has same shape as PAD_CIRCLE or PAD_OVAL m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() );
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 ) );
}
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 else
{ {
SHAPE_POLY_SET polySet; wxSize pad_size = aPad->GetSize();
wxSize margin;
switch( aLayer ) switch( aLayer )
{ {
case F_Mask: case F_Mask:
case B_Mask: case B_Mask:
{ margin.x = margin.y = aPad->GetSolderMaskMargin();
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 );
}
}
break; break;
case F_Paste: case F_Paste:
case B_Paste: case B_Paste:
{ margin = aPad->GetSolderPasteMargin();
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 );
}
break; break;
default: default:
if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) margin.x = margin.y = 0;
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 );
}
break; 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 // Clearance outlines
@ -961,31 +861,17 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
m_gal->SetStrokeColor( color ); m_gal->SetStrokeColor( color );
int clearance = aPad->GetClearance(); int clearance = aPad->GetClearance();
if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) const std::vector<std::shared_ptr<SHAPE>>& shapes = aPad->GetEffectiveShapes();
{
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, if( shapes.size() == 1 && shapes[0]->Type() == SH_SEGMENT )
aPad->GetOrientation(), {
wxSize( 0, 0 ) ); const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shapes[0].get();
int seg_width = std::min( aPad->GetSize().x, aPad->GetSize().y ) m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() + 2 * clearance );
+ 2*clearance; }
m_gal->DrawSegment( VECTOR2D( aPad->ShapePos()+seg_start ), else if( shapes.size() == 1 && shapes[0]->Type() == SH_CIRCLE )
VECTOR2D( aPad->ShapePos()+seg_end ), {
seg_width ); const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shapes[0].get();
} m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + clearance );
} }
else else
{ {

View File

@ -3352,33 +3352,33 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent )
case T_gr_arc: case T_gr_arc:
dummysegm = parseDRAWSEGMENT(); dummysegm = parseDRAWSEGMENT();
pad->AddPrimitiveArc( dummysegm->GetCenter(), dummysegm->GetArcStart(), pad->AddPrimitiveArc( dummysegm->GetCenter(), dummysegm->GetArcStart(),
dummysegm->GetAngle(), dummysegm->GetWidth(), false ); dummysegm->GetAngle(), dummysegm->GetWidth() );
break; break;
case T_gr_line: case T_gr_line:
dummysegm = parseDRAWSEGMENT(); dummysegm = parseDRAWSEGMENT();
pad->AddPrimitiveSegment( dummysegm->GetStart(), dummysegm->GetEnd(), pad->AddPrimitiveSegment( dummysegm->GetStart(), dummysegm->GetEnd(),
dummysegm->GetWidth(), false ); dummysegm->GetWidth() );
break; break;
case T_gr_circle: case T_gr_circle:
dummysegm = parseDRAWSEGMENT( true ); // Circles with 0 thickness are allowed dummysegm = parseDRAWSEGMENT( true ); // Circles with 0 thickness are allowed
// ( filled circles ) // ( filled circles )
pad->AddPrimitiveCircle( dummysegm->GetCenter(), dummysegm->GetRadius(), pad->AddPrimitiveCircle( dummysegm->GetCenter(), dummysegm->GetRadius(),
dummysegm->GetWidth(), false ); dummysegm->GetWidth() );
break; break;
case T_gr_rect: case T_gr_rect:
dummysegm = parseDRAWSEGMENT( true ); dummysegm = parseDRAWSEGMENT( true );
pad->AddPrimitiveRect( dummysegm->GetStart(), dummysegm->GetEnd(), pad->AddPrimitiveRect( dummysegm->GetStart(), dummysegm->GetEnd(),
dummysegm->GetWidth(), false ); dummysegm->GetWidth() );
break; break;
case T_gr_poly: case T_gr_poly:
dummysegm = parseDRAWSEGMENT(); dummysegm = parseDRAWSEGMENT();
pad->AddPrimitivePoly( dummysegm->BuildPolyPointsList(), pad->AddPrimitivePoly( dummysegm->BuildPolyPointsList(),
dummysegm->GetWidth(), false ); dummysegm->GetWidth() );
break; break;
case T_gr_curve: case T_gr_curve:
@ -3386,7 +3386,7 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent )
pad->AddPrimitiveCurve( dummysegm->GetStart(), dummysegm->GetEnd(), pad->AddPrimitiveCurve( dummysegm->GetStart(), dummysegm->GetEnd(),
dummysegm->GetBezControl1(), dummysegm->GetBezControl1(),
dummysegm->GetBezControl2(), dummysegm->GetBezControl2(),
dummysegm->GetWidth(), false ); dummysegm->GetWidth() );
break; break;
default: 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(); return pad.release();
} }

View File

@ -35,6 +35,7 @@
#include <base_struct.h> #include <base_struct.h>
#include <gr_text.h> #include <gr_text.h>
#include <geometry/geometry_utils.h> #include <geometry/geometry_utils.h>
#include <geometry/shape_segment.h>
#include <trigo.h> #include <trigo.h>
#include <pcb_base_frame.h> #include <pcb_base_frame.h>
#include <macros.h> #include <macros.h>
@ -249,70 +250,6 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
continue; 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; COLOR4D color = COLOR4D::BLACK;
if( pad->GetLayerSet()[B_Cu] ) if( pad->GetLayerSet()[B_Cu] )
@ -326,8 +263,30 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
else if( sketchPads && aLayerMask[B_Fab] ) else if( sketchPads && aLayerMask[B_Fab] )
color = aPlotOpt.ColorSettings()->GetColor( B_Fab ); color = aPlotOpt.ColorSettings()->GetColor( B_Fab );
// Temporary set the pad size to the required plot size: wxSize margin;
wxSize tmppadsize = pad->GetSize(); 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() ) switch( pad->GetShape() )
{ {
@ -348,14 +307,26 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
if( margin.x > 0 ) if( margin.x > 0 )
{ {
pad->SetShape( PAD_SHAPE_ROUNDRECT ); pad->SetShape( PAD_SHAPE_ROUNDRECT );
pad->SetSize( padPlotsSize );
pad->SetRoundRectCornerRadius( margin.x ); pad->SetRoundRectCornerRadius( margin.x );
} }
KI_FALLTHROUGH;
pad->SetSize( padPlotsSize );
itemplotter.PlotPad( pad, color, padPlotMode );
break;
case PAD_SHAPE_TRAPEZOID: 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_ROUNDRECT:
case PAD_SHAPE_CHAMFERED_RECT: case PAD_SHAPE_CHAMFERED_RECT:
// Chamfer and rounding are stored as a percent and so don't need scaling
pad->SetSize( padPlotsSize ); pad->SetSize( padPlotsSize );
itemplotter.PlotPad( pad, color, padPlotMode ); itemplotter.PlotPad( pad, color, padPlotMode );
break; break;
@ -373,8 +344,7 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
int numSegs = std::max( GetArcToSegmentCount( margin.x, maxError, 360.0 ), 6 ); int numSegs = std::max( GetArcToSegmentCount( margin.x, maxError, 360.0 ), 6 );
shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST ); shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST );
dummy.DeletePrimitivesList(); dummy.DeletePrimitivesList();
dummy.AddPrimitivePoly( shape, 0, false ); dummy.AddPrimitivePoly( shape, 0 );
dummy.MergePrimitivesAsPolygon();
// Be sure the anchor pad is not bigger than the deflated shape because this // 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 // 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 // Restore the pad parameters modified by the plot code
pad->SetSize( tmppadsize ); pad->SetSize( padSize );
pad->SetDelta( deltaSize ); pad->SetDelta( padDelta );
pad->SetShape( padShape ); pad->SetShape( padShape );
pad->SetRoundRectCornerRadius( padCornerRadius ); pad->SetRoundRectCornerRadius( padCornerRadius );
} }
@ -630,7 +600,6 @@ static const PCB_LAYER_ID plot_seq[] = {
void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
const PCB_PLOT_PARAMS& aPlotOpt ) const PCB_PLOT_PARAMS& aPlotOpt )
{ {
BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
itemplotter.SetLayerSet( aLayerMask ); itemplotter.SetLayerSet( aLayerMask );
@ -646,7 +615,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
outlines.Simplify( SHAPE_POLY_SET::PM_FAST ); outlines.Simplify( SHAPE_POLY_SET::PM_FAST );
// Plot outlines // Plot outlines
std::vector< wxPoint > cornerList; std::vector<wxPoint> cornerList;
// Now we have one or more basic polygons: plot each polygon // Now we have one or more basic polygons: plot each polygon
for( int ii = 0; ii < outlines.OutlineCount(); ii++ ) 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) int smallDrill = (aPlotOpt.GetDrillMarksType() == PCB_PLOT_PARAMS::SMALL_DRILL_SHAPE)
? SMALL_DRILL : INT_MAX; ? 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(); wxSize hole = pad->GetDrillSize();
@ -690,19 +659,17 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
else else
{ {
// Note: small drill marks have no significance when applied to slots // Note: small drill marks have no significance when applied to slots
wxPoint drl_start, drl_end; const std::shared_ptr<SHAPE_SEGMENT>& seg = pad->GetEffectiveHoleShape();
int width; aPlotter->ThickSegment( (wxPoint) seg->GetSeg().A,
(wxPoint) seg->GetSeg().B,
pad->GetOblongGeometry( pad->GetDrillSize(), &drl_start, &drl_end, &width ); seg->GetWidth(), SKETCH, NULL );
aPlotter->ThickSegment( pad->GetPosition() + drl_start,
pad->GetPosition() + drl_end, width, SKETCH, NULL );
} }
} }
} }
} }
// Plot vias holes // Plot vias holes
for( auto track : aBoard->Tracks() ) for( TRACK* track : aBoard->Tracks() )
{ {
const VIA* via = dyn_cast<const VIA*>( track ); 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 ); aPad->GetOrientation(), aPlotMode, &gbr_metadata );
break; break;
case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT:
{ m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), aPad->GetOrientation(), aPlotMode,
wxPoint coord[4]; &gbr_metadata );
aPad->BuildPadPolygon( coord, wxSize(0,0), 0 );
m_plotter->FlashPadTrapez( shape_pos, coord,
aPad->GetOrientation(), aPlotMode, &gbr_metadata );
}
break; break;
case PAD_SHAPE_ROUNDRECT: case PAD_SHAPE_ROUNDRECT:
@ -238,39 +234,20 @@ void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, COLOR4D aColor, EDA_DRAW_MODE_T aPl
aPad->GetOrientation(), aPlotMode, &gbr_metadata ); aPad->GetOrientation(), aPlotMode, &gbr_metadata );
break; break;
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 );
if( polygons.OutlineCount() == 0 )
break;
aPad->CustomShapeAsPolygonToBoardPosition( &polygons, shape_pos, aPad->GetOrientation() );
m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), &polygons, aPlotMode, &gbr_metadata );
}
break;
case PAD_SHAPE_RECT:
default: default:
m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), case PAD_SHAPE_TRAPEZOID:
aPad->GetOrientation(), aPlotMode, &gbr_metadata ); case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polygons;
aPad->TransformShapeWithClearanceToPolygon( polygons, 0 );
if( polygons.OutlineCount() == 0 )
break;
m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), &polygons, aPlotMode,
&gbr_metadata );
}
break; break;
} }
} }

View File

@ -617,24 +617,23 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE_BASE::syncPad( D_PAD* aPad )
wxPoint offset = aPad->GetOffset(); wxPoint offset = aPad->GetOffset();
VECTOR2I c( wx_c.x, wx_c.y ); VECTOR2I c( wx_c.x, wx_c.y );
VECTOR2I sz( wx_sz.x, wx_sz.y );
RotatePoint( &offset, aPad->GetOrientation() ); RotatePoint( &offset, aPad->GetOrientation() );
solid->SetPos( VECTOR2I( c.x - offset.x, c.y - offset.y ) ); solid->SetPos( VECTOR2I( c.x - offset.x, c.y - offset.y ) );
solid->SetOffset( VECTOR2I( offset.x, offset.y ) ); solid->SetOffset( VECTOR2I( offset.x, offset.y ) );
double orient = aPad->GetOrientation() / 10.0; if( aPad->GetEffectiveShapes().size() == 1 )
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
{ {
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; SHAPE_POLY_SET outline;
outline.Append( aPad->GetCustomShapeAsPolygon() ); aPad->TransformShapeWithClearanceToPolygon( outline, 0 );
aPad->CustomShapeAsPolygonToBoardPosition( &outline, wx_c, aPad->GetOrientation() );
SHAPE_SIMPLE* shape = new SHAPE_SIMPLE(); 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 ); 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; return solid;
} }

View File

@ -534,15 +534,17 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad )
case PAD_SHAPE_CUSTOM: case PAD_SHAPE_CUSTOM:
{ {
std::vector<wxPoint> polygonal_shape; 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 ); BuildConvexHull( polygonal_shape, pad_shape );
#else #else
const SHAPE_LINE_CHAIN& p_outline = pad_shape.COutline( 0 ); const SHAPE_LINE_CHAIN& p_outline = pad_shape.COutline( 0 );
for( int ii = 0; ii < p_outline.PointCount(); ++ii ) for( int ii = 0; ii < p_outline.PointCount(); ++ii )
polygonal_shape.push_back( wxPoint( p_outline.CPoint( ii ) ) ); polygonal_shape.push_back( wxPoint( p_outline.CPoint( ii ) ) );
#endif #endif
// The polygon must be closed // The polygon must be closed
if( polygonal_shape.front() != polygonal_shape.back() ) 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->SetPosition( wxPoint( anchor->x, anchor->y ) );
pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle );
pad->AddPrimitives( shapes ); pad->AddPrimitives( shapes );
pad->ClearFlags(); pad->ClearFlags();
bool result = pad->MergePrimitivesAsPolygon(); SHAPE_POLY_SET mergedPolygon;
pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle ); pad->MergePrimitivesAsPolygon( &mergedPolygon );
if( !result ) if( mergedPolygon.OutlineCount() > 1 )
{ {
DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n" DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n"
"selected items do not form a single solid shape.") ); "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 ) if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{ {
// the pad shape in zone can be its convex hull or the shape itself SHAPE_POLY_SET poly;
SHAPE_POLY_SET outline( aPad->GetCustomShapeAsPolygon() ); aPad->TransformShapeWithClearanceToPolygon( poly, aGap, m_high_def );
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() );
// the pad shape in zone can be its convex hull or the shape itself
if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ) if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
{ {
std::vector<wxPoint> convex_hull; std::vector<wxPoint> convex_hull;
BuildConvexHull( convex_hull, outline ); BuildConvexHull( convex_hull, poly );
aHoles.NewOutline(); aHoles.NewOutline();
@ -410,7 +406,7 @@ void ZONE_FILLER::addKnockout( D_PAD* aPad, int aGap, SHAPE_POLY_SET& aHoles )
aHoles.Append( pt ); aHoles.Append( pt );
} }
else else
aHoles.Append( outline ); aHoles.Append( poly );
} }
else else
{ {