Add support for custom pad shape. Full support in DRC and PnS.

add option to use shape or convex hull as clearance area in zones.
simplify code to handle clearance area
This commit is contained in:
jean-pierre charras 2017-01-13 18:51:22 +01:00
parent 3d1bebe9f8
commit e2d3fcec02
27 changed files with 8737 additions and 209 deletions

View File

@ -495,8 +495,23 @@ void CINFO3D_VISU::createNewPadWithClearance( const D_PAD* aPad,
}
break;
default:
wxFAIL_MSG( "CINFO3D_VISU::createNewPadWithClearance - a pad shape type is not implemented" );
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates
polyList.Append( aPad->GetCustomShapeAsPolygon() );
aPad->BasicShapesAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() );
if( aClearanceValue )
polyList.Inflate( aClearanceValue, 32 );
// This convert the poly in outline and holes
polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
polyList.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
// Add the PAD polygon
Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits, *aPad );
}
break;
}
}
@ -565,13 +580,10 @@ void CINFO3D_VISU::createNewPad( const D_PAD* aPad,
{
switch( aPad->GetShape() )
{
default:
wxFAIL_MSG( wxT( "CINFO3D_VISU::createNewPad: found a not implemented pad shape (new shape?)" ) );
break;
case PAD_SHAPE_CIRCLE:
case PAD_SHAPE_OVAL:
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_CUSTOM:
createNewPadWithClearance( aPad, aDstContainer, aInflateValue.x );
break;

View File

@ -75,8 +75,13 @@ void CINFO3D_VISU::buildPadShapePolygon( const D_PAD* aPad,
}
break;
default:
wxFAIL_MSG( wxT( "CINFO3D_VISU::buildPadShapePolygon: found a not implemented pad shape (new shape?)" ) );
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polyList; // Will contain the pad outlines in board coordinates
polyList.Append( aPad->GetCustomShapeAsPolygon() );
aPad->BasicShapesAsPolygonToBoardPosition( &polyList, aPad->ShapePos(), aPad->GetOrientation() );
aCornerBuffer.Append( polyList );
}
break;
}
}
@ -96,14 +101,15 @@ void CINFO3D_VISU::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
return;
}
// For other shapes, draw polygon outlines
SHAPE_POLY_SET corners;
unsigned int nr_sides_per_circle = GetNrSegmentsCircle( glm::min( aPad->GetSize().x,
aPad->GetSize().y) );
buildPadShapePolygon( aPad, corners, wxSize( 0, 0 ),
nr_sides_per_circle, GetCircleCorrectionFactor( nr_sides_per_circle ) );
nr_sides_per_circle,
GetCircleCorrectionFactor( nr_sides_per_circle ) );
// Add outlines as thick segments in polygon buffer

View File

@ -325,6 +325,10 @@ if( KICAD_USE_OCE )
add_definitions( -DKICAD_USE_OCE )
endif()
if( KICAD_USE_CUSTOM_PADS )
add_definitions( -DKICAD_USE_CUSTOM_PADS )
endif()
if( USE_WX_GRAPHICS_CONTEXT OR APPLE )
add_definitions( -DUSE_WX_GRAPHICS_CONTEXT )
endif()

View File

@ -379,6 +379,7 @@ set( PCB_COMMON_SRCS
../pcbnew/class_mire.cpp
../pcbnew/class_module.cpp
../pcbnew/class_pad.cpp
../pcbnew/class_pad_custom_shape_functions.cpp
../pcbnew/class_pad_draw_functions.cpp
../pcbnew/class_pcb_text.cpp
../pcbnew/class_text_mod.cpp

View File

@ -25,6 +25,7 @@
add_net
allowed
anchor
angle
arc
arc_segments
@ -52,6 +53,9 @@ connect
connect_pads
copperpour
crossbar
custom
outline
convexhull
date
descr
die_length
@ -117,6 +121,7 @@ none
not_allowed
np_thru_hole
offset
options
oval
pad
pads
@ -135,6 +140,7 @@ placed
plus
polygon
portrait
primitives
priority
pts
radius

View File

@ -35,6 +35,8 @@ enum PAD_SHAPE_T
PAD_SHAPE_OVAL,
PAD_SHAPE_TRAPEZOID,
PAD_SHAPE_ROUNDRECT,
PAD_SHAPE_CUSTOM // A shape defined by user, using a set of basic shapes
// (thick segments, circles, arcs, polygons
};
/**

View File

@ -132,6 +132,7 @@ set( PCBNEW_DIALOGS
dialogs/dialog_non_copper_zones_properties_base.cpp
dialogs/dialog_pad_properties.cpp
dialogs/dialog_pad_properties_base.cpp
dialogs/dialog_pad_basicshapes_properties.cpp
dialogs/dialog_plot_base.cpp
dialogs/dialog_plot.cpp
dialogs/dialog_position_relative.cpp

View File

@ -704,6 +704,18 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
aCornerBuffer.Append( outline );
}
break;
case PAD_SHAPE_CUSTOM:
{
int clearance = KiROUND( aClearanceValue * aCorrectionFactor );
SHAPE_POLY_SET outline; // Will contain the corners in board coordinates
outline.Append( m_customShapeAsPolygon );
BasicShapesAsPolygonToBoardPosition( &outline, GetPosition(), GetOrientation() );
outline.Inflate( clearance, aCircleToSegmentsCount );
aCornerBuffer.Append( outline );
}
break;
}
}
@ -730,6 +742,7 @@ void D_PAD::BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
case PAD_SHAPE_CIRCLE:
case PAD_SHAPE_OVAL:
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_CUSTOM:
TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x,
aSegmentsPerCircle, aCorrectionFactor );
break;

View File

@ -56,7 +56,6 @@ static wxString LayerMaskDescribe( const BOARD* aBoard, LSET aMask );
int D_PAD::m_PadSketchModePenSize = 0; // Pen size used to draw pads in sketch mode
D_PAD::D_PAD( MODULE* parent ) :
BOARD_CONNECTED_ITEM( parent, PCB_PAD_T )
{
@ -72,6 +71,8 @@ D_PAD::D_PAD( MODULE* parent ) :
}
SetShape( PAD_SHAPE_CIRCLE ); // Default pad shape is PAD_CIRCLE.
SetAnchorPadShape( PAD_SHAPE_CIRCLE ); // Default shape for custom shaped pads
// is PAD_CIRCLE.
SetDrillShape( PAD_DRILL_SHAPE_CIRCLE ); // Default pad drill shape is a circle.
m_Attribute = PAD_ATTRIB_STANDARD; // Default pad type is NORMAL (thru hole)
m_LocalClearance = 0;
@ -85,6 +86,8 @@ D_PAD::D_PAD( MODULE* parent ) :
m_ThermalWidth = 0; // Use parent setting by default
m_ThermalGap = 0; // Use parent setting by default
m_customShapeClearanceArea = CUST_PAD_SHAPE_IN_ZONE_OUTLINE;
// Set layers mask to default for a standard thru hole pad.
m_layerMask = StandardMask();
@ -160,6 +163,22 @@ int D_PAD::boundingRadius() const
radius += 1 + KiROUND( EuclideanNorm( wxSize( x - radius, y - radius )));
break;
case PAD_SHAPE_CUSTOM:
radius = 0;
for( int cnt = 0; cnt < m_customShapeAsPolygon.OutlineCount(); ++cnt )
{
const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( cnt );
for( int ii = 0; ii < poly.PointCount(); ++ii )
{
int dist = KiROUND( poly.CPoint( ii ).EuclideanNorm() );
radius = std::max( radius, dist );
}
}
radius += 1;
break;
default:
radius = 0;
}
@ -306,6 +325,32 @@ const EDA_RECT D_PAD::GetBoundingBox() const
area.SetSize( dx-x, dy-y );
break;
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polySet( m_customShapeAsPolygon );
// Move shape to actual position
BasicShapesAsPolygonToBoardPosition( &polySet, GetPosition(), GetOrientation() );
quadrant1 = m_Pos;
quadrant2 = m_Pos;
for( int cnt = 0; cnt < polySet.OutlineCount(); ++cnt )
{
const SHAPE_LINE_CHAIN& poly = polySet.COutline( cnt );
for( int ii = 0; ii < poly.PointCount(); ++ii )
{
quadrant1.x = std::min( quadrant1.x, poly.CPoint( ii ).x );
quadrant1.y = std::min( quadrant1.y, poly.CPoint( ii ).y );
quadrant2.x = std::max( quadrant2.x, poly.CPoint( ii ).x );
quadrant2.y = std::max( quadrant2.y, poly.CPoint( ii ).y );
}
}
area.SetOrigin( quadrant1 );
area.SetEnd( quadrant2 );
}
break;
default:
break;
}
@ -379,10 +424,48 @@ void D_PAD::Flip( const wxPoint& aCentre )
// So the copper layers count is not taken in account
SetLayerSet( FlipLayerMask( m_layerMask ) );
// Flip the basic shapes, in custom pads
FlipBasicShapes();
// m_boundingRadius = -1; the shape has not been changed
}
// Flip the basic shapes, in custom pads
void D_PAD::FlipBasicShapes()
{
// Flip custom shapes
for( unsigned ii = 0; ii < m_basicShapes.size(); ++ii )
{
PAD_CS_PRIMITIVE& primitive = m_basicShapes[ii];
MIRROR( primitive.m_Start.y, 0 );
MIRROR( primitive.m_End.y, 0 );
primitive.m_ArcAngle = -primitive.m_ArcAngle;
switch( primitive.m_Shape )
{
case S_POLYGON: // polygon
for( unsigned jj = 0; jj < primitive.m_Poly.size(); jj++ )
MIRROR( primitive.m_Poly[jj].y, 0 );
break;
default:
break;
}
}
// Flip local coordinates in merged Polygon
for( int cnt = 0; cnt < m_customShapeAsPolygon.OutlineCount(); ++cnt )
{
SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.Outline( cnt );
for( int ii = 0; ii < poly.PointCount(); ++ii )
MIRROR( poly.Point( ii ).y, 0 );
}
}
void D_PAD::AppendConfigs( PARAM_CFG_ARRAY* aResult )
{
// Parameters stored in config are only significant parameters
@ -817,6 +900,17 @@ bool D_PAD::HitTest( const wxPoint& aPosition ) const
return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta );
}
break;
case PAD_SHAPE_CUSTOM:
// Check for hit in polygon
RotatePoint( &delta, -m_Orient );
if( m_customShapeAsPolygon.OutlineCount() )
{
const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( 0 );
return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta );
}
break;
}
return false;
@ -1096,6 +1190,9 @@ wxString D_PAD::ShowPadShape() const
case PAD_SHAPE_ROUNDRECT:
return _( "Roundrect" );
case PAD_SHAPE_CUSTOM:
return _( "CustomShape" );
default:
return wxT( "???" );
}
@ -1334,4 +1431,9 @@ void D_PAD::ImportSettingsFromMaster( const D_PAD& aMasterPad )
default:
;
}
// Add or remove custom pad shapes:
SetBasicShapes( aMasterPad.GetBasicShapes() );
SetAnchorPadShape( aMasterPad.GetAnchorPadShape() );
MergeBasicShapesAsPolygon();
}

View File

@ -38,6 +38,13 @@
#include <config_params.h> // PARAM_CFG_ARRAY
#include "zones.h"
class DRAWSEGMENT;
enum CUST_PAD_SHAPE_IN_ZONE
{
CUST_PAD_SHAPE_IN_ZONE_OUTLINE,
CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL
};
class LINE_READER;
class EDA_3D_CANVAS;
@ -76,6 +83,38 @@ public:
PAD_DRAWINFO();
};
/** Helper class to handle a primitive (basic shape: polygon, segment, circle or arc)
* to build a custom pad full shape from a set of primitives
*/
class PAD_CS_PRIMITIVE
{
public:
STROKE_T m_Shape; /// S_SEGMENT, S_ARC, S_CIRCLE, S_POLYGON only (same as DRAWSEGMENT)
int m_Thickness; /// thickness of segment or outline
/// For filled S_CIRCLE shape, thickness = 0.
// if thickness is not = 0 S_CIRCLE shape is a ring
int m_Radius; /// radius of a circle
double m_ArcAngle; /// angle of an arc, from its starting point, in 0.1 deg
wxPoint m_Start; /// is also the center of the circle and arc
wxPoint m_End; /// is also the start point of the arc
std::vector<wxPoint> m_Poly;
PAD_CS_PRIMITIVE( STROKE_T aShape ):
m_Shape( aShape ), m_Thickness( 0 ), m_Radius( 0 ), m_ArcAngle( 0 )
{
}
// Accessors (helpers for arc and circle shapes)
wxPoint GetCenter() { return m_Start; } /// returns the center of a circle or arc
wxPoint GetArcStart() { return m_End; } /// returns the start point of an arc
/** Export the PAD_CS_PRIMITIVE parameters to a DRAWSEGMENT
* useful to draw a primitive shape
* @param aTarget is the DRAWSEGMENT to initialize
*/
void ExportTo( DRAWSEGMENT* aTarget );
};
class D_PAD : public BOARD_CONNECTED_ITEM
{
@ -169,6 +208,43 @@ public:
void SetPosition( const wxPoint& aPos ) override { m_Pos = aPos; }
const wxPoint& GetPosition() const override { return m_Pos; }
/**
* Function GetAnchorPadShape
* @return the shape of the anchor pad shape, for custom shaped pads.
*/
PAD_SHAPE_T GetAnchorPadShape() const { return m_anchorPadShape; }
/**
* @return the option for the custom pad shape to use as clearance area
* in copper zones
*/
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
{
return m_customShapeClearanceArea;
}
/**
* Set the option for the custom pad shape to use as clearance area
* in copper zones
* @param aOption is the clearance area shape CUST_PAD_SHAPE_IN_ZONE option
*/
void SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE aOption )
{
m_customShapeClearanceArea = aOption;
}
/**
* Function SetAnchorPadShape
* Set the shape of the anchor pad for custm shped pads.
* @param the shape of the anchor pad shape( currently, only
* PAD_SHAPE_RECT or PAD_SHAPE_CIRCLE.
*/
void SetAnchorPadShape( PAD_SHAPE_T aShape )
{
m_anchorPadShape = ( aShape == PAD_SHAPE_RECT ) ? PAD_SHAPE_RECT : PAD_SHAPE_CIRCLE;
m_boundingRadius = -1;
}
void SetY( int y ) { m_Pos.y = y; }
void SetX( int x ) { m_Pos.x = x; }
@ -190,9 +266,80 @@ public:
void SetOffset( const wxPoint& aOffset ) { m_Offset = aOffset; }
const wxPoint& GetOffset() const { return m_Offset; }
/**
* Has meaning only for free shape pads.
* add a free shape to the shape list.
* the shape can be
* a polygon (outline can have a thickness)
* a thick segment
* a filled circle or ring ( if thickness == 0, this is a filled circle, else a ring)
* a arc
*/
void AddBasicShape( std::vector<wxPoint>& aPoly, int aThickness ); ///< add a polygonal basic shape
void AddBasicShape( wxPoint aStart, wxPoint aEnd, int aThickness ); ///< segment basic shape
void AddBasicShape( wxPoint aCenter, int aRadius, int aThickness ); ///< ring or circle basic shape
void AddBasicShape( wxPoint aCenter, wxPoint aStart,
int aArcAngle, int aThickness ); ///< arc basic shape
/**
* Merge all basic shapes, converted to a polygon in one polygon,
* in m_customShapeAsPolygon
* @return true if OK, false in there is more than one polygon
* in m_customShapeAsPolygon
* @param aMergedPolygon = the SHAPE_POLY_SET to fill.
* if NULL, m_customShapeAsPolygon is the target
* @param aCircleToSegmentsCount = number of segment to approximate a circle
* (default = 32)
* Note: The corners coordinates are relative to the pad position, orientation 0,
*/
bool MergeBasicShapesAsPolygon( SHAPE_POLY_SET * aMergedPolygon = NULL,
int aCircleToSegmentsCount = 32 );
/**
* clear the basic shapes list
*/
void DeleteBasicShapesList();
/**
* When created, the corners coordinates are relative to the pad position, orientation 0,
* in m_customShapeAsPolygon
* BasicShapesAsPolygonToBoardPosition 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 BasicShapesAsPolygonToBoardPosition( SHAPE_POLY_SET * aMergedPolygon,
wxPoint aPosition, double aRotation ) const;
/**
* Accessor to the basic shape list
*/
const std::vector<PAD_CS_PRIMITIVE>& GetBasicShapes() 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 ) override;
/**
* Flip the basic shapes, in custom pads
*/
void FlipBasicShapes();
/**
* Import to the basic shape list
* @return true if OK, false if issues
* (more than one polygon to build the polygon shape list)
*/
bool SetBasicShapes( const std::vector<PAD_CS_PRIMITIVE>& aBasicShapesList );
/**
* Function SetOrientation
@ -217,7 +364,6 @@ public:
void SetDrillShape( PAD_DRILL_SHAPE_T aDrillShape )
{ m_drillShape = aDrillShape; }
PAD_DRILL_SHAPE_T GetDrillShape() const { return m_drillShape; }
/**
@ -607,6 +753,25 @@ private: // Private variable members:
///< PAD_SHAPE_OVAL, PAD_SHAPE_TRAPEZOID,
///< PAD_SHAPE_ROUNDRECT, PAD_SHAPE_POLYGON
/** for free shape pads: a list of basic shapes,
* in local coordinates, orient 0, coordinates relative to m_Pos
* They are expected to define only one copper area.
*/
std::vector<PAD_CS_PRIMITIVE> m_basicShapes;
/** for free shape pads: the set of basic shapes, merged as one polygon,
* in local coordinates, orient 0, coordinates relative to m_Pos
*/
SHAPE_POLY_SET m_customShapeAsPolygon;
/**
* How to build the custom shape in zone, to create the clearance area:
* CUST_PAD_SHAPE_IN_ZONE_OUTLINE = use pad shape
* CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL = use the convex hull of the pad shape
* other values are currently reserved
*/
CUST_PAD_SHAPE_IN_ZONE m_customShapeClearanceArea;
int m_SubRatsnest; ///< variable used in rats nest computations
///< handle subnet (block) number in ratsnest connection
@ -621,6 +786,9 @@ private: // Private variable members:
double m_padRoundRectRadiusScale; ///< scaling factor from smallest m_Size coord
///< to corner radius, default 0.25
PAD_SHAPE_T m_anchorPadShape; ///< for custom shaped pads: shape of pad anchor,
///< PAD_SHAPE_RECT, PAD_SHAPE_CIRCLE
/**
* m_Offset is useful only for oblong and rect pads (it can be used for other
* shapes, but without any interest).

View File

@ -0,0 +1,267 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file class_pad_custom_shape_functions.cpp
* class D_PAD functions specific to custom shaped pads.
*/
#include <fctsys.h>
#include <PolyLine.h>
#include <trigo.h>
#include <wxstruct.h>
#include <pcbnew.h>
#include <class_pad.h>
#include <class_drawsegment.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/shape_rect.h>
#include <geometry/convex_hull.h>
void PAD_CS_PRIMITIVE::ExportTo( DRAWSEGMENT* aTarget )
{
aTarget->SetShape( m_Shape );
aTarget->SetWidth( m_Thickness );
aTarget->SetStart( m_Start );
aTarget->SetEnd( m_End );
// in a DRAWSEGMENT the radius of a circle is calculated from the
// center and one point on the circle outline (stored in m_End)
if( m_Shape == S_CIRCLE )
{
wxPoint end = m_Start;
end.x += m_Radius;
aTarget->SetEnd( end );
}
aTarget->SetAngle( m_ArcAngle );
aTarget->SetPolyPoints( m_Poly );
}
/*
* Has meaning only for free shape pads.
* add a free shape to the shape list.
* the shape is a polygon (can be with thick outline), segment, circle or arc
*/
void D_PAD::AddBasicShape( std::vector<wxPoint>& aPoly, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_POLYGON );
shape.m_Poly = aPoly;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
MergeBasicShapesAsPolygon();
}
void D_PAD::AddBasicShape( wxPoint aStart, wxPoint aEnd, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_SEGMENT );
shape.m_Start = aStart;
shape.m_End = aEnd;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
MergeBasicShapesAsPolygon();
}
void D_PAD::AddBasicShape( wxPoint aCenter, wxPoint aStart, int aArcAngle, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_ARC );
shape.m_Start = aCenter;
shape.m_End = aStart;
shape.m_ArcAngle = aArcAngle;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
MergeBasicShapesAsPolygon();
}
void D_PAD::AddBasicShape( wxPoint aCenter, int aRadius, int aThickness )
{
PAD_CS_PRIMITIVE shape( S_CIRCLE );
shape.m_Start = aCenter;
shape.m_Radius = aRadius;
shape.m_Thickness = aThickness;
m_basicShapes.push_back( shape );
MergeBasicShapesAsPolygon();
}
bool D_PAD::SetBasicShapes( const std::vector<PAD_CS_PRIMITIVE>& aBasicShapesList )
{
// clear old list
m_basicShapes.clear();
// Import to the basic shape list
if( aBasicShapesList.size() )
m_basicShapes = aBasicShapesList;
// Only one polygon is expected (pad area = only one copper area)
return MergeBasicShapesAsPolygon();
}
// clear the basic shapes list and associated data
void D_PAD::DeleteBasicShapesList()
{
m_basicShapes.clear();
m_customShapeAsPolygon.RemoveAllContours();
}
/* Merge all basic shapes, converted to a polygon in one polygon,
* return true if OK, false in there is more than one polygon
* in aMergedPolygon
*/
bool D_PAD::MergeBasicShapesAsPolygon( SHAPE_POLY_SET* aMergedPolygon,
int aCircleToSegmentsCount )
{
// if aMergedPolygon == NULL, use m_customShapeAsPolygon as target
if( !aMergedPolygon )
aMergedPolygon = &m_customShapeAsPolygon;
aMergedPolygon->RemoveAllContours();
// Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
// The anchor pad is always at 0,0
switch( GetAnchorPadShape() )
{
default:
case PAD_SHAPE_CIRCLE:
TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0,0 ), GetSize().x/2,
aCircleToSegmentsCount );
break;
case PAD_SHAPE_RECT:
{
SHAPE_RECT rect( -GetSize().x/2, -GetSize().y/2, GetSize().x, GetSize().y );
aMergedPolygon->AddOutline( rect.Outline() );
}
break;
}
SHAPE_POLY_SET aux_polyset;
for( unsigned cnt = 0; cnt < m_basicShapes.size(); ++cnt )
{
const PAD_CS_PRIMITIVE& bshape = m_basicShapes[cnt];
switch( bshape.m_Shape )
{
case S_SEGMENT: // usual segment : line with rounded ends
TransformRoundedEndsSegmentToPolygon( aux_polyset,
bshape.m_Start, bshape.m_End, aCircleToSegmentsCount, bshape.m_Thickness );
break;
case S_ARC: // Arc with rounded ends
TransformArcToPolygon( aux_polyset,
bshape.m_Start, bshape.m_End, bshape.m_ArcAngle,
aCircleToSegmentsCount, bshape.m_Thickness );
break;
case S_CIRCLE: // ring or circle
if( bshape.m_Thickness ) // ring
TransformRingToPolygon( aux_polyset,
bshape.m_Start, bshape.m_Radius,
aCircleToSegmentsCount, bshape.m_Thickness ) ;
else // Filled circle
TransformCircleToPolygon( aux_polyset,
bshape.m_Start, bshape.m_Radius,
aCircleToSegmentsCount ) ;
break;
case S_POLYGON: // polygon
if( bshape.m_Poly.size() < 2 )
break; // Malformed polygon.
{
// Insert the polygon:
const std::vector< wxPoint>& poly = bshape.m_Poly;
aux_polyset.NewOutline();
if( bshape.m_Thickness )
{
SHAPE_POLY_SET polyset;
polyset.NewOutline();
for( unsigned ii = 0; ii < poly.size(); ii++ )
polyset.Append( poly[ii].x, poly[ii].y );
polyset.Inflate( bshape.m_Thickness/2, 32 );
aux_polyset.Append( polyset );
}
else
for( unsigned ii = 0; ii < poly.size(); ii++ )
aux_polyset.Append( poly[ii].x, poly[ii].y );
}
break;
default:
break;
}
}
// Merge all polygons:
if( aux_polyset.OutlineCount() )
{
aMergedPolygon->BooleanAdd( aux_polyset, SHAPE_POLY_SET::PM_FAST );
aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_FAST );
}
m_boundingRadius = -1; // The current bouding radius is no more valid.
return aMergedPolygon->OutlineCount() <= 1;
}
void D_PAD::BasicShapesAsPolygonToBoardPosition( 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
for( int cnt = 0; cnt < aMergedPolygon->OutlineCount(); ++cnt )
{
SHAPE_LINE_CHAIN& poly = aMergedPolygon->Outline( cnt );
for( int ii = 0; ii < poly.PointCount(); ++ii )
{
wxPoint corner( poly.Point( ii ).x, poly.Point( ii ).y );
RotatePoint( &corner, aRotation );
corner += aPosition;
poly.Point( ii ).x = corner.x;
poly.Point( ii ).y = corner.y;
}
}
}

View File

@ -460,6 +460,93 @@ void D_PAD::DrawShape( EDA_RECT* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
}
break;
case PAD_SHAPE_CUSTOM:
{
// The full shape has 2 items
// 1- The anchor pad: a round or rect pad located at pad position
// 2- The custom complex shape
// Note: The anchor pad shape is containing by the custom complex shape polygon
// The anchor pad is shown to help user to see where is the anchor, only in sketch mode
// (In filled mode, it is merged with the basic shapes)
wxPoint pad_pos = GetPosition() - aDrawInfo.m_Offset;
// In sketch mode only: Draw the anchor pad: a round or rect pad
if( !aDrawInfo.m_ShowPadFilled )
{
if( GetAnchorPadShape() == PAD_SHAPE_RECT )
{
wxPoint poly[4];
poly[0] = wxPoint( - halfsize.x, - halfsize.y );
poly[1] = wxPoint( - halfsize.x, + halfsize.y );
poly[2] = wxPoint( + halfsize.x, + halfsize.y );
poly[3] = wxPoint( + halfsize.x, - halfsize.y );
for( int ii = 0; ii < 4; ++ii )
{
RotatePoint( &poly[ii], m_Orient );
poly[ii] += pad_pos;
}
GRClosedPoly( aClipBox, aDC, 4, poly, false, 0,
aDrawInfo.m_Color, aDrawInfo.m_Color );
}
else
{
GRCircle( aClipBox, aDC, pad_pos.x, pad_pos.y,
halfsize.x,
m_PadSketchModePenSize, aDrawInfo.m_Color );
}
}
SHAPE_POLY_SET outline; // Will contain the corners in board coordinates
outline.Append( m_customShapeAsPolygon );
BasicShapesAsPolygonToBoardPosition( &outline, pad_pos, GetOrientation() );
SHAPE_LINE_CHAIN* poly;
const int segmentToCircleCount = 32;
if( aDrawInfo.m_Mask_margin.x )
{
SHAPE_POLY_SET clearance_outline;
clearance_outline.Append( outline );
clearance_outline.Inflate( aDrawInfo.m_Mask_margin.x, segmentToCircleCount );
poly = &clearance_outline.Outline( 0 );
}
else
{
// Draw the polygon: only one polygon is expected
// However we provide a multi polygon shape drawing
// ( for the future or to show a non expected shape )
for( int jj = 0; jj < outline.OutlineCount(); ++jj )
{
poly = &outline.Outline( jj );
GRClosedPoly( aClipBox, aDC, poly->PointCount(),
(wxPoint*)&poly->Point( 0 ), aDrawInfo.m_ShowPadFilled, 0,
aDrawInfo.m_Color, aDrawInfo.m_Color );
}
}
if( aDrawInfo.m_PadClearance )
{
SHAPE_POLY_SET clearance_outline;
clearance_outline.Append( outline );
clearance_outline.Inflate( aDrawInfo.m_PadClearance, segmentToCircleCount );
for( int jj = 0; jj < clearance_outline.OutlineCount(); ++jj )
{
poly = &clearance_outline.Outline( jj );
GRClosedPoly( aClipBox, aDC, poly->PointCount(),
(wxPoint*)&poly->Point( 0 ), false, 0,
aDrawInfo.m_Color, aDrawInfo.m_Color );
}
}
break;
}
default:
break;
}

View File

@ -0,0 +1,587 @@
/**
* @file dialog_pad_basicshapes_properties.cpp
* @brief basic shapes for pads crude editor.
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <algorithm>
#include <fctsys.h>
#include <common.h>
#include <confirm.h>
#include <pcbnew.h>
#include <trigo.h>
#include <macros.h>
#include <wxBasePcbFrame.h>
#include <base_units.h>
#include <gr_basic.h>
#include <class_board.h>
#include <class_module.h>
#include <dialog_pad_properties.h>
DIALOG_PAD_BASICSHAPES_PROPERTIES::DIALOG_PAD_BASICSHAPES_PROPERTIES(
wxWindow* aParent, PAD_CS_PRIMITIVE * aShape )
: DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE( aParent )
{
m_shape = aShape;
TransferDataToWindow();
m_sdbSizerOK->SetDefault();
GetSizer()->SetSizeHints( this );
}
bool DIALOG_PAD_BASICSHAPES_PROPERTIES::TransferDataToWindow()
{
if( m_shape == NULL )
return false;
// Shows the text info about circle or ring only for S_CIRCLE shape
if( m_shape->m_Shape != S_CIRCLE )
m_staticTextInfo->Show( false );
PutValueInLocalUnits( *m_textCtrlThickness, m_shape->m_Thickness );
// Update units and parameters names according to the shape to edit:
wxString unit = GetAbbreviatedUnitsLabel();
m_staticTextPosUnit->SetLabel( unit );
m_staticTextEndUnit->SetLabel( unit );
m_staticTextThicknessUnit->SetLabel( unit );
m_staticTextAngleUnit->SetLabel( wxEmptyString );
m_staticTextAngle->SetLabel( wxEmptyString );
switch( m_shape->m_Shape )
{
case S_SEGMENT: // Segment with rounded ends
SetTitle( _( "Segment" ) );
PutValueInLocalUnits( *m_textCtrPosX, m_shape->m_Start.x );
PutValueInLocalUnits( *m_textCtrPosY, m_shape->m_Start.y );
PutValueInLocalUnits( *m_textCtrEndX, m_shape->m_End.x );
PutValueInLocalUnits( *m_textCtrEndY, m_shape->m_End.y );
m_textCtrAngle->Show( false );
m_staticTextAngleUnit->Show( false );
m_staticTextAngle->Show( false );
break;
case S_ARC: // Arc with rounded ends
SetTitle( _( "Arc" ) );
m_staticTextPosEnd->SetLabel( _( "Center" ) );
PutValueInLocalUnits( *m_textCtrEndX, m_shape->m_Start.x ); // Start point of arc
PutValueInLocalUnits( *m_textCtrEndY, m_shape->m_Start.y );
PutValueInLocalUnits( *m_textCtrPosX, m_shape->m_End.x ); // arc center
PutValueInLocalUnits( *m_textCtrPosY, m_shape->m_End.y );
m_textCtrAngle->SetValue( FMT_ANGLE( m_shape->m_ArcAngle ) );
m_staticTextAngle->SetLabel( _( "Angle" ) );
m_staticTextAngleUnit->SetLabel( _( "degree" ) );
break;
case S_CIRCLE: // ring or circle
if( m_shape->m_Thickness )
SetTitle( _( "Ring" ) );
else
SetTitle( _( "Circle" ) );
// End point does not exist for a circle or ring:
m_textCtrEndX->Show( false );
m_textCtrEndY->Show( false );
m_staticTextPosEnd->Show( false );
m_staticTextEndUnit->Show( false );
m_staticTextEndX->Show( false );
m_staticTextEndY->Show( false );
// Circle center uses position controls:
m_staticTextPosStart->SetLabel( _( "Center" ) );
PutValueInLocalUnits( *m_textCtrPosX, m_shape->m_Start.x );
PutValueInLocalUnits( *m_textCtrPosY, m_shape->m_Start.y );
PutValueInLocalUnits( *m_textCtrAngle, m_shape->m_Radius );
m_staticTextAngleUnit->SetLabel( unit );
m_staticTextAngle->SetLabel( _( "Radius" ) );
break;
case S_POLYGON: // polygon
SetTitle( "Polygon" );
m_staticTextPosStart->SetLabel( wxEmptyString );
m_staticTextPosEnd->SetLabel( wxEmptyString );
m_staticTextAngle->SetLabel( _( "corners count" ) );
m_textCtrAngle->SetValue( wxString::Format( "%d", m_shape->m_Poly.size() ) );
break;
default:
SetTitle( "Unknown basic shape" );
break;
}
return true;
}
bool DIALOG_PAD_BASICSHAPES_PROPERTIES::TransferDataFromWindow()
{
// Transfer data out of the GUI.
m_shape->m_Thickness = ValueFromString( g_UserUnit, m_textCtrlThickness->GetValue() );
switch( m_shape->m_Shape )
{
case S_SEGMENT: // Segment with rounded ends
m_shape->m_Start.x = ValueFromString( g_UserUnit, m_textCtrPosX->GetValue() );
m_shape->m_Start.y = ValueFromString( g_UserUnit, m_textCtrPosY->GetValue() );
m_shape->m_End.x = ValueFromString( g_UserUnit, m_textCtrEndX->GetValue() );
m_shape->m_End.y = ValueFromString( g_UserUnit, m_textCtrEndY->GetValue() );
break;
case S_ARC: // Arc with rounded ends
// Start point of arc
m_shape->m_Start.x = ValueFromString( g_UserUnit, m_textCtrEndX->GetValue() );
m_shape->m_Start.y = ValueFromString( g_UserUnit, m_textCtrEndY->GetValue() );
// arc center
m_shape->m_End.x = ValueFromString( g_UserUnit, m_textCtrPosX->GetValue() );
m_shape->m_End.y = ValueFromString( g_UserUnit, m_textCtrPosY->GetValue() );
m_shape->m_ArcAngle = ValueFromString( DEGREES, m_textCtrAngle->GetValue() );
break;
case S_CIRCLE: // ring or circle
m_shape->m_Start.x = ValueFromString( g_UserUnit, m_textCtrPosX->GetValue() );
m_shape->m_Start.y = ValueFromString( g_UserUnit, m_textCtrPosY->GetValue() );
//radius
m_shape->m_Radius = ValueFromString( g_UserUnit, m_textCtrAngle->GetValue() );
break;
case S_POLYGON: // polygon
// polygon has a specific dialog editor. No nothing here
break;
default:
SetTitle( "Unknown basic shape" );
break;
}
return true;
}
DIALOG_PAD_BASIC_SHP_POLY_PROPS::DIALOG_PAD_BASIC_SHP_POLY_PROPS(
wxWindow* aParent, PAD_CS_PRIMITIVE * aShape )
: DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE( aParent ),
m_shape( aShape ), m_currshape( *m_shape )
{
TransferDataToWindow();
m_sdbSizerOK->SetDefault();
GetSizer()->SetSizeHints( this );
// TODO: move wxEVT_GRID_CELL_CHANGING in wxFormbuilder, when it support it
m_gridCornersList->Connect( wxEVT_GRID_CELL_CHANGING,
wxGridEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS::onCellChanging ), NULL, this );
}
DIALOG_PAD_BASIC_SHP_POLY_PROPS::~DIALOG_PAD_BASIC_SHP_POLY_PROPS()
{
m_gridCornersList->Disconnect( wxEVT_GRID_CELL_CHANGING,
wxGridEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS::onCellChanging ), NULL, this );
}
bool DIALOG_PAD_BASIC_SHP_POLY_PROPS::TransferDataToWindow()
{
if( m_shape == NULL )
return false;
// Update units and parameters names according to the shape to edit:
wxString unit = GetAbbreviatedUnitsLabel();
m_staticTextThicknessUnit->SetLabel( unit );
PutValueInLocalUnits( *m_textCtrlThickness, m_currshape.m_Thickness );
// Test for acceptable polygon (more than 2 corners, and not self-intersecting)
// A warning message is displayed if not OK
Validate();
// If the number of corners is < 2 (Happens for a new shape, prepare 2 dummy corners
while( m_currshape.m_Poly.size() < 2 )
m_currshape.m_Poly.push_back( wxPoint( 0, 0 ) );
// Populates the list of corners
int extra_rows = m_currshape.m_Poly.size() - m_gridCornersList->GetNumberRows();
if( extra_rows > 0 )
{
m_gridCornersList->AppendRows( extra_rows );
}
else if( extra_rows < 0 )
{
extra_rows = -extra_rows;
m_gridCornersList->DeleteRows( 0, extra_rows );
}
// enter others corner coordinates
wxString msg;
for( unsigned row = 0; row < m_currshape.m_Poly.size(); ++row )
{
// Row label is "Corner x"
msg.Printf( "Corner %d", row+1 );
m_gridCornersList->SetRowLabelValue( row, msg );
msg = StringFromValue( g_UserUnit, m_currshape.m_Poly[row].x );
m_gridCornersList->SetCellValue( row, 0, msg );
msg = StringFromValue( g_UserUnit, m_currshape.m_Poly[row].y );
m_gridCornersList->SetCellValue( row, 1, msg );
}
return true;
}
bool DIALOG_PAD_BASIC_SHP_POLY_PROPS::TransferDataFromWindow()
{
if( !Validate() )
return false;
// Transfer data out of the GUI.
m_currshape.m_Thickness = ValueFromString( g_UserUnit, m_textCtrlThickness->GetValue() );
*m_shape = m_currshape;
return true;
}
// test for a valid polygon (a not self intersectiong polygon)
bool DIALOG_PAD_BASIC_SHP_POLY_PROPS::Validate()
{
if( m_currshape.m_Poly.size() < 3 )
{
m_staticTextValidate->SetLabel( _(" Incorrect polygon: less than 3 corners" ) );
m_staticTextValidate->Show( true );
return false;
}
bool valid = true;
SHAPE_LINE_CHAIN polyline;
for( unsigned ii = 0; ii < m_currshape.m_Poly.size(); ++ii )
polyline.Append( m_currshape.m_Poly[ii].x, m_currshape.m_Poly[ii].y );
// The polyline describes a polygon: close it.
polyline.SetClosed( true );
// Remove redundant corners:
polyline.Simplify();
if( polyline.PointCount() < 3 )
{
m_staticTextValidate->SetLabel( _("Incorrect polygon: too few corners after simplification" ) );
valid = false;
}
if( valid && polyline.SelfIntersecting() )
{
m_staticTextValidate->SetLabel( _("Incorrect polygon: self intersecting" ) );
valid = false;
}
if( valid )
m_staticTextValidate->SetLabel( _("Polygon:" ) );
if( polyline.PointCount() != (int)m_currshape.m_Poly.size() )
{ // Happens after simplification
m_currshape.m_Poly.clear();
for( int ii = 0; ii < polyline.PointCount(); ++ii )
m_currshape.m_Poly.push_back( wxPoint( polyline.CPoint( ii ).x, polyline.CPoint( ii ).y ) );
m_staticTextValidate->SetLabel( _("Polygon: redundant corners removed" ) );
}
return valid;
}
void DIALOG_PAD_BASIC_SHP_POLY_PROPS::onButtonAdd( wxCommandEvent& event )
{
// Insert a new corner after the currently selected:
int row = -1;
if( m_gridCornersList->GetNumberRows() )
{
wxArrayInt selections = m_gridCornersList->GetSelectedRows();
if( selections.size() > 0 )
{
std::sort( selections.begin(), selections.end() );
row = selections[0];
}
else
{
row = m_gridCornersList->GetGridCursorRow();
}
if( row < 0 )
{
wxMessageBox( _( "Select a corner before adding a new corner" ) );
return;
}
}
else
row = 0;
m_gridCornersList->SelectRow( row, false );
if( m_currshape.m_Poly.size() == 0 )
m_currshape.m_Poly.push_back( wxPoint(0,0) );
else
m_currshape.m_Poly.insert( m_currshape.m_Poly.begin() + row + 1, wxPoint(0,0) );
TransferDataToWindow();
m_panelPoly->Refresh();
}
void DIALOG_PAD_BASIC_SHP_POLY_PROPS::OnButtonDelete( wxCommandEvent& event )
{
wxArrayInt selections = m_gridCornersList->GetSelectedRows();
std::sort( selections.begin(), selections.end() );
// remove corners:
for( int ii = selections.size()-1; ii >= 0 ; --ii )
{
m_currshape.m_Poly.erase( m_currshape.m_Poly.begin() + selections[ii] );
}
// Unselect all raws:
m_gridCornersList->SelectRow( -1, false );
TransferDataToWindow();
m_panelPoly->Refresh();
}
void DIALOG_PAD_BASIC_SHP_POLY_PROPS::onPaintPolyPanel( wxPaintEvent& event )
{
wxPaintDC dc( m_panelPoly );
wxSize dc_size = dc.GetSize();
dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );
// Calculate a suitable scale to fit the available draw area
wxSize minsize;
for( unsigned ii = 0; ii < m_currshape.m_Poly.size(); ++ii )
{
minsize.x = std::max( minsize.x, std::abs( m_currshape.m_Poly[ii].x ) );
minsize.y = std::max( minsize.y, std::abs( m_currshape.m_Poly[ii].y ) );
}
// The draw origin is the center of the window.
// Therefore the window size is twice the minsize just calculated
minsize.x *= 2;
minsize.y *= 2;
minsize.x += m_currshape.m_Thickness;
minsize.y += m_currshape.m_Thickness;
// Avoid null or too small size:
int mindim = Millimeter2iu( 0.5 );
if( minsize.x < mindim )
minsize.x = mindim;
if( minsize.y < mindim )
minsize.y = mindim;
double scale = std::min( (double) dc_size.x / minsize.x, (double) dc_size.y / minsize.y );
// Give a margin
scale *= 0.9;
dc.SetUserScale( scale, scale );
GRResetPenAndBrush( &dc );
// Draw X and Y axis. This is particularly useful to show the
// reference position of basic shape
// Axis are drawn before the polygon to avoid masking segments on axis
GRLine( NULL, &dc, -int( dc_size.x/scale ), 0, int( dc_size.x/scale ), 0, 0, LIGHTBLUE ); // X axis
GRLine( NULL, &dc, 0, -int( dc_size.y/scale ), 0, int( dc_size.y/scale ), 0, LIGHTBLUE ); // Y axis
// Draw polygon.
// The selected edge(s) are shown in selectcolor, the others in normalcolor.
EDA_COLOR_T normalcolor = WHITE;
EDA_COLOR_T selectcolor = RED;
for( unsigned ii = 0; ii < m_currshape.m_Poly.size(); ++ii )
{
EDA_COLOR_T color = normalcolor;
if( m_gridCornersList->IsInSelection (ii, 0) ||
m_gridCornersList->IsInSelection (ii, 1) ||
m_gridCornersList->GetGridCursorRow() == (int)ii )
color = selectcolor;
unsigned jj = ii + 1;
if( jj >= m_currshape.m_Poly.size() )
jj = 0;
GRLine( NULL, &dc, m_currshape.m_Poly[ii], m_currshape.m_Poly[jj], m_currshape.m_Thickness, color );
}
event.Skip();
}
void DIALOG_PAD_BASIC_SHP_POLY_PROPS::onPolyPanelResize( wxSizeEvent& event )
{
m_panelPoly->Refresh();
event.Skip();
}
void DIALOG_PAD_BASIC_SHP_POLY_PROPS::onGridSelect( wxGridRangeSelectEvent& event )
{
m_panelPoly->Refresh();
}
void DIALOG_PAD_BASIC_SHP_POLY_PROPS::onCellChanging( wxGridEvent& event )
{
int row = event.GetRow();
int col = event.GetCol();
wxString msg = event.GetString();
if( msg.IsEmpty() )
return;
int value = ValueFromString( g_UserUnit, msg );
if( col == 0 ) // Set the X value
m_currshape.m_Poly[row].x = value;
else // Set the Y value
m_currshape.m_Poly[row].y = value;
m_currshape.m_Thickness = ValueFromString( g_UserUnit, m_textCtrlThickness->GetValue() );
Validate();
m_panelPoly->Refresh();
}
// A dialog to apply geometry transforms to a shape or set of shapes
// (move, rotate around origin, scaling factor, duplication).
DIALOG_PAD_BASICSHAPES_TRANSFORM::DIALOG_PAD_BASICSHAPES_TRANSFORM(
wxWindow* aParent,
std::vector<PAD_CS_PRIMITIVE*>& aList, bool aShowDuplicate )
:DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE( aParent ), m_list( aList )
{
wxString unit = GetAbbreviatedUnitsLabel();
m_staticTextMoveUnit->SetLabel( unit );
if( !aShowDuplicate ) // means no duplicate transform
{
m_staticTextDupCnt->Enable( false );
m_spinCtrlDuplicateCount->Enable( false );
}
m_sdbSizerOK->SetDefault();
GetSizer()->SetSizeHints( this );
}
// A helper function in geometry transform
inline void geom_transf( wxPoint& aCoord, wxPoint& aMove, double aScale, double aRotation )
{
aCoord.x = KiROUND( aCoord.x * aScale );
aCoord.y = KiROUND( aCoord.y * aScale );
aCoord += aMove;
RotatePoint( &aCoord, aRotation );
}
void DIALOG_PAD_BASICSHAPES_TRANSFORM::Transform( std::vector<PAD_CS_PRIMITIVE>* aList, int aDuplicateCount )
{
// Get parameters from dlg:
wxPoint moveVect;
moveVect.x = ValueFromString( g_UserUnit, m_textCtrMoveX->GetValue() );
moveVect.y = ValueFromString( g_UserUnit, m_textCtrMoveY->GetValue() );
double rotation = DoubleValueFromString( DEGREES, m_textCtrAngle->GetValue() );
double scale = DoubleValueFromString( UNSCALED_UNITS, m_textCtrlScalingFactor->GetValue() );
// Avoid too small /too large scale, which could create issues:
if( scale < 0.01 )
scale = 0.01;
if( scale > 100.0 )
scale = 100.0;
// Transform shapes
// shapes are scaled, then moved then rotated.
// if aList != NULL, the initial shape will be duplicated, and transform
// applied to the duplicated shape
do {
for( unsigned idx = 0; idx < m_list.size(); ++idx )
{
PAD_CS_PRIMITIVE* shape;
if( aList == NULL )
shape = m_list[idx];
else
{
PAD_CS_PRIMITIVE new_shape( *m_list[idx] );
aList->push_back( new_shape );
shape = &aList->back();
}
// Transform parameters common to all shape types (some can be unused)
shape->m_Thickness = KiROUND( shape->m_Thickness * scale );
geom_transf( shape->m_Start, moveVect, scale, rotation );
geom_transf( shape->m_End, moveVect, scale, rotation );
// specific parameters:
switch( shape->m_Shape )
{
case S_SEGMENT: // Segment with rounded ends
break;
case S_ARC: // Arc with rounded ends
break;
case S_CIRCLE: // ring or circle
shape->m_Radius = KiROUND( shape->m_Radius * scale );
break;
case S_POLYGON: // polygon
for( unsigned ii = 0; ii < shape->m_Poly.size(); ++ii )
geom_transf( shape->m_Poly[ii], moveVect, scale, rotation );
break;
default:
break;
}
}
// Prepare new transform on duplication:
// Each new item is rotated (or moved) by the transform from the last duplication
rotation += rotation;
moveVect += moveVect;
} while( aList && --aDuplicateCount > 0 );
}

View File

@ -6,10 +6,10 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2013 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2008-2013 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -32,6 +32,7 @@
#include <fctsys.h>
#include <common.h>
#include <gr_basic.h>
#include <trigo.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <pcbnew.h>
@ -53,6 +54,8 @@ static PAD_SHAPE_T code_shape[] = {
PAD_SHAPE_RECT,
PAD_SHAPE_TRAPEZOID,
PAD_SHAPE_ROUNDRECT,
PAD_SHAPE_CUSTOM, // choice = CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR
PAD_SHAPE_CUSTOM // choice = PAD_SHAPE_CUSTOM_RECT_ANCHOR
};
// the ordered index of the pad shape wxChoice in dialog.
@ -63,6 +66,8 @@ enum CODE_CHOICE {
CHOICE_SHAPE_RECT,
CHOICE_SHAPE_TRAPEZOID,
CHOICE_SHAPE_ROUNDRECT,
CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR,
CHOICE_SHAPE_CUSTOM_RECT_ANCHOR
};
@ -121,33 +126,14 @@ DIALOG_PAD_PROPERTIES::DIALOG_PAD_PROPERTIES( PCB_BASE_FRAME* aParent, D_PAD* aP
else // We are editing a "master" pad, i.e. a template to create new pads
*m_dummyPad = *m_padMaster;
// Show the X and Y axis. It is usefull because pad shape can have an offset
// or be a complex shape.
m_axisOrigin = new KIGFX::ORIGIN_VIEWITEM( KIGFX::COLOR4D(0.0, 0.0, 0.8, 1.0),
KIGFX::ORIGIN_VIEWITEM::CROSS,
Millimeter2iu( 0.2 ),
VECTOR2D( m_dummyPad->GetPosition() ) );
m_axisOrigin->SetDrawAtZero( true );
if( m_parent->IsGalCanvasActive() )
{
m_panelShowPadGal->UseColorScheme( &m_parent->Settings().Colors() );
m_panelShowPadGal->SwitchBackend( m_parent->GetGalCanvas()->GetBackend() );
m_panelShowPadGal->Show();
m_panelShowPad->Hide();
m_panelShowPadGal->GetView()->Add( m_dummyPad );
m_panelShowPadGal->GetView()->Add( m_axisOrigin );
m_panelShowPadGal->StartDrawing();
Connect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_PAD_PROPERTIES::OnResize ) );
}
else
{
m_panelShowPad->Show();
m_panelShowPadGal->Hide();
}
// Initialize canvas to be able to display the dummy pad:
prepareCanvas();
initValues();
// Usually, TransferDataToWindow is called by OnInitDialog
// calling it here fixes all widgets sizes, and FinishDialogSettings can
// safely fix minsizes
TransferDataToWindow();
m_sdbSizerOK->SetDefault();
@ -157,12 +143,48 @@ DIALOG_PAD_PROPERTIES::DIALOG_PAD_PROPERTIES( PCB_BASE_FRAME* aParent, D_PAD* aP
FinishDialogSettings();
}
void DIALOG_PAD_PROPERTIES::OnInitDialog( wxInitDialogEvent& event )
{
m_PadNumCtrl->SetFocus();
m_PadNumCtrl->SetSelection( -1, -1 );
}
void DIALOG_PAD_PROPERTIES::prepareCanvas()
{
// Initialize the canvases (legacy or gal) to display the pad
// Enable the suitable canvas and make some inits
// Show the X and Y axis. It is usefull because pad shape can have an offset
// or be a complex shape.
KIGFX::COLOR4D axis_color = LIGHTBLUE;
m_axisOrigin = new KIGFX::ORIGIN_VIEWITEM( axis_color,
KIGFX::ORIGIN_VIEWITEM::CROSS,
Millimeter2iu( 0.2 ),
VECTOR2D( m_dummyPad->GetPosition() ) );
m_axisOrigin->SetDrawAtZero( true );
if( m_parent->IsGalCanvasActive() )
{
m_panelShowPadGal->UseColorScheme( &m_parent->Settings().Colors() );
m_panelShowPadGal->SwitchBackend( m_parent->GetGalCanvas()->GetBackend() );
m_panelShowPadGal->Show();
m_panelShowPad->Hide();
m_panelShowPadGal->GetView()->Add( m_dummyPad );
m_panelShowPadGal->GetView()->Add( m_axisOrigin );
m_panelShowPadGal->StartDrawing();
Connect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_PAD_PROPERTIES::OnResize ) );
}
else
{
m_panelShowPad->Show();
m_panelShowPadGal->Hide();
}
}
void DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event )
{
wxPaintDC dc( m_panelShowPad );
@ -221,6 +243,7 @@ void DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event )
// If the pad is a circle, use the x size here instead.
int ysize;
if( m_dummyPad->GetShape() == PAD_SHAPE_CIRCLE )
ysize = m_dummyPad->GetSize().x;
else
@ -249,7 +272,63 @@ void DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event )
GRResetPenAndBrush( &dc );
m_dummyPad->DrawShape( NULL, &dc, drawInfo );
// Draw X and Y axis. Hhis is particularly useful to show the
// draw selected primitives:
COLOR4D hcolor = CYAN;
long select = m_listCtrlShapes->GetFirstSelected();
wxPoint start, end, center;
while( select >= 0 )
{
PAD_CS_PRIMITIVE& primitive = m_primitives[select];
// The best way to calculate parameters to draw a primitive is to
// use a dummy DRAWSEGMENT and use its methods
// Note: in legacy canvas, the pad has the 0,0 position
DRAWSEGMENT dummySegment;
primitive.ExportTo( &dummySegment );
dummySegment.Rotate( wxPoint( 0, 0), m_dummyPad->GetOrientation() );
switch( primitive.m_Shape )
{
case S_SEGMENT: // usual segment : line with rounded ends
GRFilledSegment( NULL, &dc, dummySegment.GetStart(), dummySegment.GetEnd(),
primitive.m_Thickness, hcolor );
break;
case S_ARC: // Arc with rounded ends
GRArc1( NULL, &dc, dummySegment.GetArcEnd(), dummySegment.GetArcStart(),
dummySegment.GetCenter(), primitive.m_Thickness, hcolor );
break;
case S_CIRCLE: // ring or circle
if( primitive.m_Thickness )
{
GRCircle( NULL, &dc, dummySegment.GetCenter(), primitive.m_Radius,
primitive.m_Thickness, hcolor );
}
else
{
GRFilledCircle( NULL, &dc, dummySegment.GetCenter(),
primitive.m_Radius, hcolor );
}
break;
case S_POLYGON: // polygon
{
std::vector<wxPoint>& poly = dummySegment.GetPolyPoints();
GRClosedPoly( NULL, &dc, poly.size(), &poly[0], /* filled */ true,
primitive.m_Thickness, hcolor, hcolor );
}
break;
default:
break;
}
select = m_listCtrlShapes->GetNextSelected( select );
}
// Draw X and Y axis. This is particularly useful to show the
// reference position of pads with offset and no hole, or custom pad shapes
const int linethickness = 0;
GRLine( NULL, &dc, -int( dc_size.x/scale ), 0, int( dc_size.x/scale ), 0,
@ -376,8 +455,13 @@ void DIALOG_PAD_PROPERTIES::initValues()
// flip pad's layers
m_dummyPad->SetLayerSet( FlipLayerMask( m_dummyPad->GetLayerSet() ) );
// flip custom pad shapes
m_dummyPad->FlipBasicShapes();
}
m_primitives = m_dummyPad->GetBasicShapes();
m_staticTextWarningPadFlipped->Show(m_isFlipped);
m_PadNumCtrl->SetValue( m_dummyPad->GetPadName() );
@ -463,6 +547,11 @@ void DIALOG_PAD_PROPERTIES::initValues()
break;
}
if( m_dummyPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
m_ZoneCustomPadShape->SetSelection( 1 );
else
m_ZoneCustomPadShape->SetSelection( 0 );
if( m_currentPad )
{
angle = m_currentPad->GetOrientation();
@ -531,6 +620,13 @@ void DIALOG_PAD_PROPERTIES::initValues()
case PAD_SHAPE_ROUNDRECT:
m_PadShape->SetSelection( CHOICE_SHAPE_ROUNDRECT );
break;
case PAD_SHAPE_CUSTOM:
if( m_dummyPad->GetAnchorPadShape() == PAD_SHAPE_RECT )
m_PadShape->SetSelection( CHOICE_SHAPE_CUSTOM_RECT_ANCHOR );
else
m_PadShape->SetSelection( CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR );
break;
}
m_OrientValue = angle / 10.0;
@ -566,8 +662,90 @@ void DIALOG_PAD_PROPERTIES::initValues()
OnDrillShapeSelected( cmd_event );
OnPadShapeSelection( cmd_event );
updateRoundRectCornerValues();
// Update basic shapes list
displayBasicShapesList();
}
// A small helper function, to display coordinates:
static wxString formatCoord( wxPoint aCoord )
{
return wxString::Format( "(X:%s Y:%s)",
CoordinateToString( aCoord.x, true ),
CoordinateToString( aCoord.y, true ) );
}
void DIALOG_PAD_PROPERTIES::displayBasicShapesList()
{
m_listCtrlShapes->ClearAll();
wxListItem itemCol;
itemCol.SetImage(-1);
for( int ii = 0; ii < 5; ++ii )
m_listCtrlShapes->InsertColumn(ii, itemCol);
wxString bs_info[5];
for( unsigned ii = 0; ii < m_primitives.size(); ++ii )
{
const PAD_CS_PRIMITIVE& primitive = m_primitives[ii];
for( unsigned jj = 0; jj < 5; ++jj )
bs_info[jj].Empty();
bs_info[4] = wxString::Format( _( "width %s" ),
CoordinateToString( primitive.m_Thickness, true ) );
switch( primitive.m_Shape )
{
case S_SEGMENT: // usual segment : line with rounded ends
bs_info[0] = _( "Segment" );
bs_info[1] = _( "from " ) + formatCoord( primitive.m_Start );
bs_info[2] = _( "to " ) + formatCoord( primitive.m_End );
break;
case S_ARC: // Arc with rounded ends
bs_info[0] = _( "Arc" );
bs_info[1] = _( "center " ) + formatCoord( primitive.m_Start ); // Center
bs_info[2] = _( "start " ) + formatCoord( primitive.m_End ); // Start point
bs_info[3] = wxString::Format( _( "angle %s" ), FMT_ANGLE( primitive.m_ArcAngle ) );
break;
case S_CIRCLE: // ring or circle
if( primitive.m_Thickness )
bs_info[0] = _( "ring" );
else
bs_info[0] = _( "circle" );
bs_info[1] = formatCoord( primitive.m_Start );
bs_info[2] = wxString::Format( _( "radius %s" ),
CoordinateToString( primitive.m_Radius, true ) );
break;
case S_POLYGON: // polygon
bs_info[0] = "Polygon";
bs_info[1] = wxString::Format( _( "corners count %d" ), primitive.m_Poly.size() );
break;
default:
bs_info[0] = "Unknown primitive";
break;
}
long tmp = m_listCtrlShapes->InsertItem(ii, bs_info[0]);
m_listCtrlShapes->SetItemData(tmp, ii);
for( int jj = 0, col = 0; jj < 5; ++jj )
{
m_listCtrlShapes->SetItem(tmp, col++, bs_info[jj]);
}
}
// Now columns are filled, ensure correct width of columns
for( unsigned ii = 0; ii < 5; ++ii )
m_listCtrlShapes->SetColumnWidth( ii, wxLIST_AUTOSIZE );
}
void DIALOG_PAD_PROPERTIES::OnResize( wxSizeEvent& event )
{
@ -578,6 +756,8 @@ void DIALOG_PAD_PROPERTIES::OnResize( wxSizeEvent& event )
void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
{
bool is_custom = false;
switch( m_PadShape->GetSelection() )
{
case CHOICE_SHAPE_CIRCLE:
@ -622,11 +802,30 @@ void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
m_tcCornerSizeRatio->ChangeValue( wxString::Format( "%.1f",
m_dummyPad->GetRoundRectRadiusRatio()*100 ) );
break;
case CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR: // PAD_SHAPE_CUSTOM, circular anchor
case CHOICE_SHAPE_CUSTOM_RECT_ANCHOR: // PAD_SHAPE_CUSTOM, rect anchor
is_custom = true;
m_ShapeDelta_Ctrl->Enable( false );
m_trapDeltaDirChoice->Enable( false );
m_ShapeSize_Y_Ctrl->Enable(
m_PadShape->GetSelection() == CHOICE_SHAPE_CUSTOM_RECT_ANCHOR );
m_ShapeOffset_X_Ctrl->Enable( false );
m_ShapeOffset_Y_Ctrl->Enable( false );
break;
}
// A few widgets are enabled only for rounded rect pads:
m_tcCornerSizeRatio->Enable( m_PadShape->GetSelection() == CHOICE_SHAPE_ROUNDRECT );
// PAD_SHAPE_CUSTOM type has constraints for zone connection and thermal shape:
// only not connected is allowed to avoid destroying the shape.
// Enable/disable options
m_ZoneConnectionChoice->Enable( !is_custom );
m_ThermalWidthCtrl->Enable( !is_custom );
m_ThermalGapCtrl->Enable( !is_custom );
m_ZoneCustomPadShape->Enable( is_custom );
transferDataToPad( m_dummyPad );
updateRoundRectCornerValues();
@ -872,6 +1071,14 @@ bool DIALOG_PAD_PROPERTIES::padValuesOK()
}
}
if( m_dummyPad->GetShape() == PAD_SHAPE_CUSTOM )
{
if( !m_dummyPad->MergeBasicShapesAsPolygon( ) )
error_msgs.Add(
_( "Incorrect pad shape: the shape must be equivalent to only one polygon" ) );
}
if( error_msgs.GetCount() )
{
HTML_MESSAGE_BOX dlg( this, _("Pad setup errors list" ) );
@ -887,7 +1094,77 @@ void DIALOG_PAD_PROPERTIES::redraw()
{
if( m_parent->IsGalCanvasActive() )
{
m_parent->GetGalCanvas()->GetView()->Update( m_dummyPad );
auto view = m_panelShowPadGal->GetView();
m_panelShowPadGal->StopDrawing();
view->SetTopLayer( F_SilkS );
view->Update( m_dummyPad );
// delete previous items if highlight list
while( m_highligth.size() )
{
delete m_highligth.back(); // the dtor also removes item from view
m_highligth.pop_back();
}
// highlight selected primitives:
long select = m_listCtrlShapes->GetFirstSelected();
while( select >= 0 )
{
PAD_CS_PRIMITIVE& primitive = m_primitives[select];
DRAWSEGMENT* dummySegment = new DRAWSEGMENT;
dummySegment->SetLayer( F_SilkS );
primitive.ExportTo( dummySegment );
dummySegment->Rotate( wxPoint( 0, 0), m_dummyPad->GetOrientation() );
dummySegment->Move( m_dummyPad->GetPosition() );
// Update selected primitive (highligth selected)
switch( primitive.m_Shape )
{
case S_SEGMENT:
case S_ARC:
break;
case S_CIRCLE: // ring or circle
if( primitive.m_Thickness == 0 ) // filled circle
{ // the filled circle option does not exist in a DRAWSEGMENT
// but it is easy to create it with a circle having the
// right radius and outline width
wxPoint end = dummySegment->GetCenter();
end.x += primitive.m_Radius/2;
dummySegment->SetEnd( end );
dummySegment->SetWidth( primitive.m_Radius );
}
break;
case S_POLYGON: // polygon
{
std::vector<wxPoint>& poly = dummySegment->GetPolyPoints();
for( unsigned ii = 0; ii < poly.size(); ii++ )
{
poly[ii] += m_dummyPad->GetPosition();
}
}
break;
default:
delete dummySegment;
dummySegment = nullptr;
break;
}
if( dummySegment )
{
view->Add( dummySegment );
m_highligth.push_back( dummySegment );
}
select = m_listCtrlShapes->GetNextSelected( select );
}
BOX2I bbox = m_dummyPad->ViewBBox();
@ -897,14 +1174,15 @@ void DIALOG_PAD_PROPERTIES::redraw()
BOX2I drawbox;
drawbox.Move( m_dummyPad->GetPosition() );
drawbox.Inflate( bbox.GetSize().x*2, bbox.GetSize().y*2 );
m_panelShowPadGal->GetView()->SetBoundary( drawbox );
view->SetBoundary( drawbox );
// Autozoom
m_panelShowPadGal->GetView()->SetViewport( BOX2D( bbox.GetOrigin(), bbox.GetSize() ) );
view->SetViewport( BOX2D( bbox.GetOrigin(), bbox.GetSize() ) );
// Add a margin
m_panelShowPadGal->GetView()->SetScale( m_panelShowPadGal->GetView()->GetScale() * 0.7 );
view->SetScale( m_panelShowPadGal->GetView()->GetScale() * 0.7 );
m_panelShowPadGal->StartDrawing();
m_panelShowPadGal->Refresh();
}
}
@ -1004,6 +1282,19 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
m_currentPad->SetPadToDieLength( m_padMaster->GetPadToDieLength() );
if( m_padMaster->GetShape() != PAD_SHAPE_CUSTOM )
m_padMaster->DeleteBasicShapesList();
m_currentPad->SetAnchorPadShape( m_padMaster->GetAnchorPadShape() );
m_currentPad->SetBasicShapes( m_padMaster->GetBasicShapes() );
if( m_isFlipped )
{
m_currentPad->SetLayerSet( FlipLayerMask( m_currentPad->GetLayerSet() ) );
m_currentPad->FlipBasicShapes();
}
if( m_currentPad->GetLayerSet() != m_padMaster->GetLayerSet() )
{
rastnestIsChanged = true;
@ -1055,6 +1346,9 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
m_currentPad->SetShape( PAD_SHAPE_RECT );
}
// define the way the clearnce area is defined in zones
m_currentPad->SetCustomShapeInZoneOpt( m_padMaster->GetCustomShapeInZoneOpt() );
if( footprint )
footprint->CalculateBoundingBox();
@ -1088,7 +1382,11 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( D_PAD* aPad )
aPad->SetAttribute( code_type[m_PadType->GetSelection()] );
aPad->SetShape( code_shape[m_PadShape->GetSelection()] );
aPad->SetAnchorPadShape( m_PadShape->GetSelection() == CHOICE_SHAPE_CUSTOM_RECT_ANCHOR ?
PAD_SHAPE_RECT : PAD_SHAPE_CIRCLE );
if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
aPad->SetBasicShapes( m_primitives );
// Read pad clearances values:
aPad->SetLocalClearance( ValueFromTextCtrl( *m_NetClearanceValueCtrl ) );
@ -1158,6 +1456,10 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( D_PAD* aPad )
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
y = x;
// for custom shped pads, the pad size is the anchor pad size:
if( aPad->GetShape() == PAD_SHAPE_CUSTOM && aPad->GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
y = x;
aPad->SetSize( wxSize( x, y ) );
// Read pad length die
@ -1248,6 +1550,27 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( D_PAD* aPad )
aPad->SetDelta( wxSize( 0, 0 ) );
break;
case PAD_SHAPE_CUSTOM:
aPad->SetOffset( wxPoint( 0, 0 ) );
aPad->SetDelta( wxSize( 0, 0 ) );
// The pad custom has a "anchor pad" (a basic shape: round or rect pad)
// that is the minimal area of this pad, and is usefull to ensure a hole
// diameter is acceptable, and is used in Gerber files as flashed area
// reference
if( aPad->GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
{
x = aPad->GetSize().x;
aPad->SetSize( wxSize( x, x ) );
}
// define the way the clearance area is defined in zones
aPad->SetCustomShapeInZoneOpt( m_ZoneCustomPadShape->GetSelection() == 0 ?
CUST_PAD_SHAPE_IN_ZONE_OUTLINE :
CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL );
break;
default:
;
}
@ -1361,3 +1684,227 @@ void DIALOG_PAD_PROPERTIES::OnValuesChanged( wxCommandEvent& event )
redraw();
}
}
void DIALOG_PAD_PROPERTIES::editBasicShape()
{
long select = m_listCtrlShapes->GetFirstSelected();
if( select < 0 )
{
wxMessageBox( _( "No shape selected" ) );
return;
}
PAD_CS_PRIMITIVE& shape = m_primitives[select];
if( shape.m_Shape == S_POLYGON )
{
DIALOG_PAD_BASIC_SHP_POLY_PROPS dlg( this, &shape );
if( dlg.ShowModal() != wxID_OK )
return;
dlg.TransferDataFromWindow();
}
else
{
DIALOG_PAD_BASICSHAPES_PROPERTIES dlg( this, &shape );
if( dlg.ShowModal() != wxID_OK )
return;
dlg.TransferDataFromWindow();
}
displayBasicShapesList();
if( m_canUpdate )
{
transferDataToPad( m_dummyPad );
redraw();
}
}
void DIALOG_PAD_PROPERTIES::OnPrimitiveSelection( wxListEvent& event )
{
// Called on a double click on the basic shapes list
// To Do: highligth the primitive(s) currently selected.
redraw();
}
/// Called on a double click on the basic shapes list
void DIALOG_PAD_PROPERTIES::onCustomShapeDClick( wxMouseEvent& event )
{
editBasicShape();
}
// Called on a click on basic shapes list panel button
void DIALOG_PAD_PROPERTIES::onEditShape( wxCommandEvent& event )
{
editBasicShape();
}
// Called on a click on basic shapes list panel button
void DIALOG_PAD_PROPERTIES::onDeleteShape( wxCommandEvent& event )
{
long select = m_listCtrlShapes->GetFirstSelected();
if( select < 0 )
return;
// Multiple selections are allowed. get them and remove corresponding shapes
std::vector<long> indexes;
indexes.push_back( select );
while( ( select = m_listCtrlShapes->GetNextSelected( select ) ) >= 0 )
indexes.push_back( select );
// Erase all select shapes
for( unsigned ii = indexes.size(); ii > 0; --ii )
m_primitives.erase( m_primitives.begin() + indexes[ii-1] );
displayBasicShapesList();
if( m_canUpdate )
{
transferDataToPad( m_dummyPad );
redraw();
}
}
void DIALOG_PAD_PROPERTIES::onAddShape( wxCommandEvent& event )
{
// Ask user for shape type
wxString shapelist[] =
{
_( "Segment" ), _( "Arc" ), _( "ring/circle" ), _( "polygon" )
};
int type = wxGetSingleChoiceIndex( wxEmptyString, _( " Select shape type:" ),
DIM( shapelist ), shapelist, 0 );
STROKE_T listtype[] =
{
S_SEGMENT, S_ARC, S_CIRCLE, S_POLYGON
};
PAD_CS_PRIMITIVE basicShape( listtype[type] );
if( listtype[type] == S_POLYGON )
{
DIALOG_PAD_BASIC_SHP_POLY_PROPS dlg( this, &basicShape );
if( dlg.ShowModal() != wxID_OK )
return;
}
else
{
DIALOG_PAD_BASICSHAPES_PROPERTIES dlg( this, &basicShape );
if( dlg.ShowModal() != wxID_OK )
return;
}
m_primitives.push_back( basicShape );
displayBasicShapesList();
if( m_canUpdate )
{
transferDataToPad( m_dummyPad );
redraw();
}
}
void DIALOG_PAD_PROPERTIES::onImportShape( wxCommandEvent& event )
{
wxMessageBox( "Not yet available" );
}
void DIALOG_PAD_PROPERTIES::onGeometryTransform( wxCommandEvent& event )
{
long select = m_listCtrlShapes->GetFirstSelected();
if( select < 0 )
{
wxMessageBox( _( "No shape selected" ) );
return;
}
// Multiple selections are allowed. Build selected shapes list
std::vector<long> indexes;
indexes.push_back( select );
std::vector<PAD_CS_PRIMITIVE*> shapeList;
shapeList.push_back( &m_primitives[select] );
while( ( select = m_listCtrlShapes->GetNextSelected( select ) ) >= 0 )
{
indexes.push_back( select );
shapeList.push_back( &m_primitives[select] );
}
DIALOG_PAD_BASICSHAPES_TRANSFORM dlg( this, shapeList );
if( dlg.ShowModal() != wxID_OK )
return;
// Transfert new settings:
dlg.Transform();
displayBasicShapesList();
if( m_canUpdate )
{
transferDataToPad( m_dummyPad );
redraw();
}
}
void DIALOG_PAD_PROPERTIES::onDuplicateShape( wxCommandEvent& event )
{
long select = m_listCtrlShapes->GetFirstSelected();
if( select < 0 )
{
wxMessageBox( _( "No shape selected" ) );
return;
}
// Multiple selections are allowed. Build selected shapes list
std::vector<long> indexes;
indexes.push_back( select );
std::vector<PAD_CS_PRIMITIVE*> shapeList;
shapeList.push_back( &m_primitives[select] );
while( ( select = m_listCtrlShapes->GetNextSelected( select ) ) >= 0 )
{
indexes.push_back( select );
shapeList.push_back( &m_primitives[select] );
}
DIALOG_PAD_BASICSHAPES_TRANSFORM dlg( this, shapeList, true );
if( dlg.ShowModal() != wxID_OK )
return;
// Transfert new settings:
dlg.Transform( &m_primitives, dlg.GetDuplicateCount() );
displayBasicShapesList();
if( m_canUpdate )
{
transferDataToPad( m_dummyPad );
redraw();
}
}

View File

@ -40,6 +40,7 @@
#include <class_board.h>
#include <class_module.h>
#include <class_drawsegment.h>
#include <origin_viewitem.h>
@ -56,7 +57,6 @@ class DIALOG_PAD_PROPERTIES : public DIALOG_PAD_PROPERTIES_BASE
{
public:
DIALOG_PAD_PROPERTIES( PCB_BASE_FRAME* aParent, D_PAD* aPad );
void OnInitDialog( wxInitDialogEvent& event ) override;
~DIALOG_PAD_PROPERTIES()
{
delete m_dummyPad;
@ -80,13 +80,23 @@ private:
bool m_canUpdate;
bool m_canEditNetName; // true only if the caller is the board editor
// for free shape pads: the list of primitives (basic shapes),
// in local coordinates, orient 0, coordinates relative to m_Pos
// They are expected to define only one copper area.
std::vector<PAD_CS_PRIMITIVE> m_primitives;
std::vector<DRAWSEGMENT*> m_highligth; // shapes highlighted in GAL mode
wxFloatingPointValidator<double> m_OrientValidator;
double m_OrientValue;
private:
void prepareCanvas(); // Initialize the canvases (legacy or gal) to display the pad
void initValues();
void displayBasicShapesList();
bool padValuesOK(); ///< test if all values are acceptable for the pad
void redraw();
void editBasicShape();
void updateRoundRectCornerValues();
/**
@ -100,6 +110,7 @@ private:
bool transferDataToPad( D_PAD* aPad );
// event handlers:
void OnInitDialog( wxInitDialogEvent& event ) override;
void OnResize( wxSizeEvent& event );
void OnPadShapeSelection( wxCommandEvent& event ) override;
@ -120,8 +131,128 @@ private:
/// Updates the different parameters for the component being edited.
/// Automatically fired from the OK button click.
bool TransferDataFromWindow() override;
bool TransferDataToWindow() override;
/// Event handlers of basic shapes list panel
void onDeleteShape( wxCommandEvent& event ) override;
void onEditShape( wxCommandEvent& event ) override;
void onAddShape( wxCommandEvent& event ) override;
void onImportShape( wxCommandEvent& event ) override;
void onGeometryTransform( wxCommandEvent& event ) override;
void onDuplicateShape( wxCommandEvent& event ) override;
/// Called on a double click on the basic shapes list
void onCustomShapeDClick( wxMouseEvent& event ) override;
/// Called on selection/deselection of a basic shape
void OnPrimitiveSelection( wxListEvent& event ) override;
};
/**
* a dialog to edit basics shapes parameters.
* Polygonal shape is not handles by this dialog
*/
class DIALOG_PAD_BASICSHAPES_PROPERTIES: public DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE
{
public:
DIALOG_PAD_BASICSHAPES_PROPERTIES( wxWindow* aParent, PAD_CS_PRIMITIVE * aShape );
/**
* Function TransferDataFromWindow
* Transfer data out of the GUI.
*/
bool TransferDataFromWindow() override;
private:
/**
* Function TransferDataToWindow
* Transfer data into the GUI.
*/
bool TransferDataToWindow() override;
// The basic shape currently edited
PAD_CS_PRIMITIVE * m_shape;
};
/**
* a dialog to edit basic polygonal shape parameters
*/
class DIALOG_PAD_BASIC_SHP_POLY_PROPS: public DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE
{
// The basic shape currently edited
PAD_CS_PRIMITIVE * m_shape;
// The working copy of the basic shape currently edited
PAD_CS_PRIMITIVE m_currshape;
public:
DIALOG_PAD_BASIC_SHP_POLY_PROPS( wxWindow* aParent, PAD_CS_PRIMITIVE * aShape );
~DIALOG_PAD_BASIC_SHP_POLY_PROPS();
/**
* Function TransferDataFromWindow
* Transfer data out of the GUI.
*/
bool TransferDataFromWindow() override;
private:
/**
* Function TransferDataToWindow
* Transfer data into the GUI.
*/
bool TransferDataToWindow() override;
/**
* test for a valid polygon (a not self intersectiong polygon)
*/
bool Validate() override;
// Events handlers:
void OnValidateButton( wxCommandEvent& event );
void onButtonAdd( wxCommandEvent& event ) override;
void OnButtonDelete( wxCommandEvent& event ) override;
void onPaintPolyPanel( wxPaintEvent& event ) override;
void onPolyPanelResize( wxSizeEvent& event ) override;
void onGridSelect( wxGridRangeSelectEvent& event ) override;
void onCellChanging( wxGridEvent& event );
void onCellSelect( wxGridEvent& event ) override
{
event.Skip();
}
};
/** A dialog to apply geometry transforms to a shape or set of shapes
* (move, rotate around origin, scaling factor, duplication).
* shapes are scaled, then moved then rotated.
* aList is a list of shapes to transform or duplicate
* if aShowDuplicate == false, the parameter "Duplicate count" is disabled
*/
class DIALOG_PAD_BASICSHAPES_TRANSFORM : public DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE
{
public:
DIALOG_PAD_BASICSHAPES_TRANSFORM( wxWindow* aParent,
std::vector<PAD_CS_PRIMITIVE*>& aList, bool aShowDuplicate = false );
/**
* Apply geometric transform (rotation, move, scale) defined in dialog
* aDuplicate = 1 .. n to duplicate the list of shapes
* aDuplicate = 0 to transform the list of shapes
* The duplicated items are transformed, but the initial shpes are not modified.
* The duplicated items are added to aList
*/
void Transform( std::vector<PAD_CS_PRIMITIVE>* aList = NULL, int aDuplicateCount = 0 );
/**
* @return the number of duplicate, chosen by user
*/
int GetDuplicateCount() { return m_spinCtrlDuplicateCount->GetValue(); }
private:
std::vector<PAD_CS_PRIMITIVE*>& m_list;
};
#endif // #ifndef _DIALOG_PAD_PROPERTIES_H_

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jul 17 2016)
// C++ code generated with wxFormBuilder (version Jul 2 2017)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@ -63,10 +63,10 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_staticText45->Wrap( -1 );
fgSizerPadType->Add( m_staticText45, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxTOP, 5 );
wxString m_PadShapeChoices[] = { _("Circular"), _("Oval"), _("Rectangular"), _("Trapezoidal"), _("Rounded Rectangle") };
wxString m_PadShapeChoices[] = { _("Circular"), _("Oval"), _("Rectangular"), _("Trapezoidal"), _("Rounded Rectangle"), _("Custom Shape with Circular Anchor"), _("Custom Shape with Rectangular Anchor") };
int m_PadShapeNChoices = sizeof( m_PadShapeChoices ) / sizeof( wxString );
m_PadShape = new wxChoice( m_panelGeneral, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_PadShapeNChoices, m_PadShapeChoices, 0 );
m_PadShape->SetSelection( 0 );
m_PadShape->SetSelection( 6 );
fgSizerPadType->Add( m_PadShape, 0, wxALL|wxEXPAND, 5 );
@ -223,7 +223,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_staticline9 = new wxStaticLine( m_panelGeneral, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
fgSizerShapeType->Add( m_staticline9, 0, wxEXPAND | wxALL, 5 );
m_staticTextCornerSizeRatio = new wxStaticText( m_panelGeneral, wxID_ANY, _("Corner size ( percent of width):"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextCornerSizeRatio = new wxStaticText( m_panelGeneral, wxID_ANY, _("Corner size (% of width):"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextCornerSizeRatio->Wrap( -1 );
m_staticTextCornerSizeRatio->SetToolTip( _("Corner radius in percent of the pad width.\nThe width is the smaller value between size X and size Y\nThe max value is 50 percent ") );
@ -486,56 +486,67 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
bSizerClearance->Add( sbClearancesSizer, 0, wxALL|wxEXPAND, 5 );
wxStaticBoxSizer* sbSizerZonesSettings;
sbSizerZonesSettings = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Copper Zones") ), wxVERTICAL );
m_sbSizerZonesSettings = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Copper Zones") ), wxVERTICAL );
wxFlexGridSizer* fgSizer41;
fgSizer41 = new wxFlexGridSizer( 3, 3, 0, 0 );
fgSizer41->AddGrowableCol( 1 );
fgSizer41->SetFlexibleDirection( wxBOTH );
fgSizer41->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
wxFlexGridSizer* fgSizerCopperZonesOpts;
fgSizerCopperZonesOpts = new wxFlexGridSizer( 3, 3, 0, 0 );
fgSizerCopperZonesOpts->AddGrowableCol( 1 );
fgSizerCopperZonesOpts->SetFlexibleDirection( wxBOTH );
fgSizerCopperZonesOpts->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticText40 = new wxStaticText( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Pad connection:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText40 = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Pad connection:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText40->Wrap( -1 );
fgSizer41->Add( m_staticText40, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 );
fgSizerCopperZonesOpts->Add( m_staticText40, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 );
wxString m_ZoneConnectionChoiceChoices[] = { _("From parent footprint"), _("Solid"), _("Thermal relief"), _("None") };
int m_ZoneConnectionChoiceNChoices = sizeof( m_ZoneConnectionChoiceChoices ) / sizeof( wxString );
m_ZoneConnectionChoice = new wxChoice( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneConnectionChoiceNChoices, m_ZoneConnectionChoiceChoices, 0 );
m_ZoneConnectionChoice = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneConnectionChoiceNChoices, m_ZoneConnectionChoiceChoices, 0 );
m_ZoneConnectionChoice->SetSelection( 0 );
fgSizer41->Add( m_ZoneConnectionChoice, 0, wxLEFT|wxTOP|wxEXPAND, 5 );
fgSizerCopperZonesOpts->Add( m_ZoneConnectionChoice, 0, wxLEFT|wxTOP|wxEXPAND, 5 );
m_staticText53 = new wxStaticText( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_staticText53->Wrap( -1 );
fgSizer41->Add( m_staticText53, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
m_staticText49 = new wxStaticText( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Thermal relief width:"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerCopperZonesOpts->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticText49 = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Thermal relief width:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText49->Wrap( -1 );
fgSizer41->Add( m_staticText49, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 );
fgSizerCopperZonesOpts->Add( m_staticText49, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxTOP, 5 );
m_ThermalWidthCtrl = new wxTextCtrl( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer41->Add( m_ThermalWidthCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 );
m_ThermalWidthCtrl = new wxTextCtrl( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerCopperZonesOpts->Add( m_ThermalWidthCtrl, 0, wxEXPAND|wxLEFT|wxTOP, 5 );
m_ThermalWidthUnits = new wxStaticText( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 );
m_ThermalWidthUnits = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 );
m_ThermalWidthUnits->Wrap( -1 );
fgSizer41->Add( m_ThermalWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
fgSizerCopperZonesOpts->Add( m_ThermalWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
m_staticText52 = new wxStaticText( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Thermal relief gap:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText52 = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Thermal relief gap:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText52->Wrap( -1 );
fgSizer41->Add( m_staticText52, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxTOP, 5 );
fgSizerCopperZonesOpts->Add( m_staticText52, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxTOP, 5 );
m_ThermalGapCtrl = new wxTextCtrl( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizer41->Add( m_ThermalGapCtrl, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxTOP, 5 );
m_ThermalGapCtrl = new wxTextCtrl( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerCopperZonesOpts->Add( m_ThermalGapCtrl, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxTOP, 5 );
m_ThermalGapUnits = new wxStaticText( sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 );
m_ThermalGapUnits = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Inch"), wxDefaultPosition, wxDefaultSize, 0 );
m_ThermalGapUnits->Wrap( -1 );
fgSizer41->Add( m_ThermalGapUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
fgSizerCopperZonesOpts->Add( m_ThermalGapUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticTextcps = new wxStaticText( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, _("Custom pad shape in zone:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextcps->Wrap( -1 );
fgSizerCopperZonesOpts->Add( m_staticTextcps, 0, wxALL, 5 );
wxString m_ZoneCustomPadShapeChoices[] = { _("Use pad shape"), _("Use pad convex hull") };
int m_ZoneCustomPadShapeNChoices = sizeof( m_ZoneCustomPadShapeChoices ) / sizeof( wxString );
m_ZoneCustomPadShape = new wxChoice( m_sbSizerZonesSettings->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_ZoneCustomPadShapeNChoices, m_ZoneCustomPadShapeChoices, 0 );
m_ZoneCustomPadShape->SetSelection( 0 );
fgSizerCopperZonesOpts->Add( m_ZoneCustomPadShape, 0, wxALL|wxEXPAND, 5 );
sbSizerZonesSettings->Add( fgSizer41, 0, wxEXPAND, 5 );
fgSizerCopperZonesOpts->Add( 0, 0, 1, wxEXPAND, 5 );
bSizerClearance->Add( sbSizerZonesSettings, 0, wxALL|wxEXPAND, 5 );
m_sbSizerZonesSettings->Add( fgSizerCopperZonesOpts, 0, wxEXPAND, 5 );
bSizerClearance->Add( m_sbSizerZonesSettings, 0, wxALL|wxEXPAND, 5 );
m_staticTextWarning = new wxStaticText( m_localSettingsPanel, wxID_ANY, _("Set fields to 0 to use parent or global values"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextWarning->Wrap( -1 );
@ -551,6 +562,65 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_localSettingsPanel->Layout();
bSizerPanelClearance->Fit( m_localSettingsPanel );
m_notebook->AddPage( m_localSettingsPanel, _("Local Clearance and Settings"), false );
m_panelCustomShape = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_bSizerPanelShapes = new wxBoxSizer( wxVERTICAL );
m_staticTextShapesList = new wxStaticText( m_panelCustomShape, wxID_ANY, _("Primitives list"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextShapesList->Wrap( -1 );
m_staticTextShapesList->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_bSizerPanelShapes->Add( m_staticTextShapesList, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
m_staticTextShapesListWraning = new wxStaticText( m_panelCustomShape, wxID_ANY, _(" Coordinates are relative to anchor pad, orientation 0"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextShapesListWraning->Wrap( -1 );
m_staticTextShapesListWraning->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
m_bSizerPanelShapes->Add( m_staticTextShapesListWraning, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
m_listCtrlShapes = new wxListView( m_panelCustomShape, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_NO_HEADER|wxLC_REPORT );
m_bSizerPanelShapes->Add( m_listCtrlShapes, 1, wxALL|wxEXPAND, 5 );
wxBoxSizer* bSizerButtons;
bSizerButtons = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bSizerButtonsUpper;
bSizerButtonsUpper = new wxBoxSizer( wxHORIZONTAL );
m_buttonDel = new wxButton( m_panelCustomShape, wxID_ANY, _("Delete Shape"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerButtonsUpper->Add( m_buttonDel, 0, wxALL, 5 );
m_buttonEditShape = new wxButton( m_panelCustomShape, wxID_ANY, _("Edit Shape"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerButtonsUpper->Add( m_buttonEditShape, 0, wxALL, 5 );
m_buttonAddShape = new wxButton( m_panelCustomShape, wxID_ANY, _("Add Shape"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerButtonsUpper->Add( m_buttonAddShape, 0, wxALL, 5 );
m_buttonDup = new wxButton( m_panelCustomShape, wxID_ANY, _("Duplicate Shape"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerButtonsUpper->Add( m_buttonDup, 0, wxALL, 5 );
bSizerButtons->Add( bSizerButtonsUpper, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
wxBoxSizer* bSizerButtonsLower;
bSizerButtonsLower = new wxBoxSizer( wxHORIZONTAL );
m_buttonGeometry = new wxButton( m_panelCustomShape, wxID_ANY, _("Geometry Transform"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerButtonsLower->Add( m_buttonGeometry, 0, wxALL, 5 );
m_buttonImport = new wxButton( m_panelCustomShape, wxID_ANY, _("Import Shapes"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerButtonsLower->Add( m_buttonImport, 0, wxALL, 5 );
bSizerButtons->Add( bSizerButtonsLower, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
m_bSizerPanelShapes->Add( bSizerButtons, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
m_panelCustomShape->SetSizer( m_bSizerPanelShapes );
m_panelCustomShape->Layout();
m_bSizerPanelShapes->Fit( m_panelCustomShape );
m_notebook->AddPage( m_panelCustomShape, _("Custom Shape"), false );
bSizerUpper->Add( m_notebook, 0, wxALL|wxEXPAND, 5 );
@ -624,6 +694,15 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
m_PadLayerECO1->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnSetLayers ), NULL, this );
m_PadLayerECO2->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnSetLayers ), NULL, this );
m_NetClearanceValueCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnValuesChanged ), NULL, this );
m_listCtrlShapes->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_PAD_PROPERTIES_BASE::onCustomShapeDClick ), NULL, this );
m_listCtrlShapes->Connect( wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnPrimitiveSelection ), NULL, this );
m_listCtrlShapes->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnPrimitiveSelection ), NULL, this );
m_buttonDel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onDeleteShape ), NULL, this );
m_buttonEditShape->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onEditShape ), NULL, this );
m_buttonAddShape->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onAddShape ), NULL, this );
m_buttonDup->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onDuplicateShape ), NULL, this );
m_buttonGeometry->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onGeometryTransform ), NULL, this );
m_buttonImport->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onImportShape ), NULL, this );
m_panelShowPad->Connect( wxEVT_PAINT, wxPaintEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnPaintShowPanel ), NULL, this );
}
@ -660,6 +739,415 @@ DIALOG_PAD_PROPERTIES_BASE::~DIALOG_PAD_PROPERTIES_BASE()
m_PadLayerECO1->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnSetLayers ), NULL, this );
m_PadLayerECO2->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnSetLayers ), NULL, this );
m_NetClearanceValueCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnValuesChanged ), NULL, this );
m_listCtrlShapes->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_PAD_PROPERTIES_BASE::onCustomShapeDClick ), NULL, this );
m_listCtrlShapes->Disconnect( wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnPrimitiveSelection ), NULL, this );
m_listCtrlShapes->Disconnect( wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnPrimitiveSelection ), NULL, this );
m_buttonDel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onDeleteShape ), NULL, this );
m_buttonEditShape->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onEditShape ), NULL, this );
m_buttonAddShape->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onAddShape ), NULL, this );
m_buttonDup->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onDuplicateShape ), NULL, this );
m_buttonGeometry->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onGeometryTransform ), NULL, this );
m_buttonImport->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onImportShape ), NULL, this );
m_panelShowPad->Disconnect( wxEVT_PAINT, wxPaintEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnPaintShowPanel ), NULL, this );
}
DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE::DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bSizermain;
bSizermain = new wxBoxSizer( wxVERTICAL );
m_staticTextInfo = new wxStaticText( this, wxID_ANY, _("Filled circle: set thickness to 0\nRing: set thickness to the width of the ring"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextInfo->Wrap( -1 );
m_staticTextInfo->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizermain->Add( m_staticTextInfo, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
wxFlexGridSizer* fgSizerShapeProperties;
fgSizerShapeProperties = new wxFlexGridSizer( 0, 6, 0, 0 );
fgSizerShapeProperties->AddGrowableCol( 2 );
fgSizerShapeProperties->AddGrowableCol( 4 );
fgSizerShapeProperties->SetFlexibleDirection( wxBOTH );
fgSizerShapeProperties->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticTextPosStart = new wxStaticText( this, wxID_ANY, _("Start point"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextPosStart->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextPosStart, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextStartX = new wxStaticText( this, wxID_ANY, _("X"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextStartX->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextStartX, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_textCtrPosX = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrPosX, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_staticTextStartY = new wxStaticText( this, wxID_ANY, _("Y"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextStartY->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextStartY, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_textCtrPosY = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrPosY, 0, wxALL|wxEXPAND, 5 );
m_staticTextPosUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextPosUnit->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextPosUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextPosEnd = new wxStaticText( this, wxID_ANY, _("End point"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextPosEnd->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextPosEnd, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextEndX = new wxStaticText( this, wxID_ANY, _("X"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextEndX->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextEndX, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_textCtrEndX = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrEndX, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextEndY = new wxStaticText( this, wxID_ANY, _("Y"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextEndY->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextEndY, 0, wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_textCtrEndY = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrEndY, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_staticTextEndUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextEndUnit->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextEndUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextAngle = new wxStaticText( this, wxID_ANY, _("Angle"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextAngle->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextAngle, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_textCtrAngle = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrAngle, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticTextAngleUnit = new wxStaticText( this, wxID_ANY, _("degree"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextAngleUnit->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextAngleUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextThickness = new wxStaticText( this, wxID_ANY, _("Thickness"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextThickness->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextThickness, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_textCtrlThickness = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrlThickness, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticTextThicknessUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextThicknessUnit->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextThicknessUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
bSizermain->Add( fgSizerShapeProperties, 1, wxEXPAND, 5 );
m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizermain->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 );
m_sdbSizer = new wxStdDialogButtonSizer();
m_sdbSizerOK = new wxButton( this, wxID_OK );
m_sdbSizer->AddButton( m_sdbSizerOK );
m_sdbSizerCancel = new wxButton( this, wxID_CANCEL );
m_sdbSizer->AddButton( m_sdbSizerCancel );
m_sdbSizer->Realize();
bSizermain->Add( m_sdbSizer, 0, wxALIGN_RIGHT|wxALL, 5 );
this->SetSizer( bSizermain );
this->Layout();
this->Centre( wxBOTH );
}
DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE::~DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE()
{
}
DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE::DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bSizermain;
bSizermain = new wxBoxSizer( wxVERTICAL );
wxFlexGridSizer* fgSizerShapeProperties;
fgSizerShapeProperties = new wxFlexGridSizer( 0, 6, 0, 0 );
fgSizerShapeProperties->AddGrowableCol( 2 );
fgSizerShapeProperties->AddGrowableCol( 4 );
fgSizerShapeProperties->SetFlexibleDirection( wxBOTH );
fgSizerShapeProperties->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticTextMove = new wxStaticText( this, wxID_ANY, _("Move vector"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextMove->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextMove, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextMoveX = new wxStaticText( this, wxID_ANY, _("X"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextMoveX->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextMoveX, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_textCtrMoveX = new wxTextCtrl( this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrMoveX, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_staticTextMoveY = new wxStaticText( this, wxID_ANY, _("Y"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextMoveY->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextMoveY, 0, wxALL, 5 );
m_textCtrMoveY = new wxTextCtrl( this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrMoveY, 0, wxALL|wxEXPAND, 5 );
m_staticTextMoveUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextMoveUnit->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextMoveUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextAngle = new wxStaticText( this, wxID_ANY, _("Rotation"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextAngle->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextAngle, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_textCtrAngle = new wxTextCtrl( this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrAngle, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticTextAngleUnit = new wxStaticText( this, wxID_ANY, _("degree"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextAngleUnit->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextAngleUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextSF = new wxStaticText( this, wxID_ANY, _("Scaling factor"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextSF->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextSF, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_textCtrlScalingFactor = new wxTextCtrl( this, wxID_ANY, _("1.0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerShapeProperties->Add( m_textCtrlScalingFactor, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_staticTextDupCnt = new wxStaticText( this, wxID_ANY, _("Duplicate count"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextDupCnt->Wrap( -1 );
fgSizerShapeProperties->Add( m_staticTextDupCnt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
m_spinCtrlDuplicateCount = new wxSpinCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 100, 1 );
fgSizerShapeProperties->Add( m_spinCtrlDuplicateCount, 0, wxALL|wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
fgSizerShapeProperties->Add( 0, 0, 1, wxEXPAND, 5 );
bSizermain->Add( fgSizerShapeProperties, 1, wxEXPAND, 5 );
m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizermain->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 );
m_sdbSizer = new wxStdDialogButtonSizer();
m_sdbSizerOK = new wxButton( this, wxID_OK );
m_sdbSizer->AddButton( m_sdbSizerOK );
m_sdbSizerCancel = new wxButton( this, wxID_CANCEL );
m_sdbSizer->AddButton( m_sdbSizerCancel );
m_sdbSizer->Realize();
bSizermain->Add( m_sdbSizer, 0, wxALIGN_RIGHT|wxALL, 5 );
this->SetSizer( bSizermain );
this->Layout();
this->Centre( wxBOTH );
}
DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE::~DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE()
{
}
DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bSizerMain;
bSizerMain = new wxBoxSizer( wxVERTICAL );
m_staticTextShapesListWarning = new wxStaticText( this, wxID_ANY, _(" Coordinates are relative to anchor pad, orientation 0"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextShapesListWarning->Wrap( -1 );
m_staticTextShapesListWarning->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerMain->Add( m_staticTextShapesListWarning, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
m_staticTextValidate = new wxStaticText( this, wxID_ANY, _("Incorrect polygon"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextValidate->Wrap( -1 );
m_staticTextValidate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
bSizerMain->Add( m_staticTextValidate, 0, wxALL, 5 );
wxBoxSizer* bSizerUpper;
bSizerUpper = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bSizer23;
bSizer23 = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bSizerCornerlist;
bSizerCornerlist = new wxBoxSizer( wxVERTICAL );
m_gridCornersList = new wxGrid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_gridCornersList->CreateGrid( 1, 2 );
m_gridCornersList->EnableEditing( true );
m_gridCornersList->EnableGridLines( true );
m_gridCornersList->EnableDragGridSize( false );
m_gridCornersList->SetMargins( 0, 0 );
// Columns
m_gridCornersList->EnableDragColMove( false );
m_gridCornersList->EnableDragColSize( true );
m_gridCornersList->SetColLabelSize( 30 );
m_gridCornersList->SetColLabelValue( 0, _("Pos X") );
m_gridCornersList->SetColLabelValue( 1, _("Pos Y") );
m_gridCornersList->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
// Rows
m_gridCornersList->AutoSizeRows();
m_gridCornersList->EnableDragRowSize( false );
m_gridCornersList->SetRowLabelSize( 80 );
m_gridCornersList->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE );
// Label Appearance
// Cell Defaults
m_gridCornersList->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP );
bSizerCornerlist->Add( m_gridCornersList, 1, wxALL, 5 );
wxBoxSizer* bSizerRightButts;
bSizerRightButts = new wxBoxSizer( wxHORIZONTAL );
m_buttonAdd = new wxButton( this, wxID_ANY, _("Add"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerRightButts->Add( m_buttonAdd, 0, wxALL, 5 );
m_buttonDelete = new wxButton( this, wxID_ANY, _("Delete"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerRightButts->Add( m_buttonDelete, 0, wxALL, 5 );
bSizerCornerlist->Add( bSizerRightButts, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
bSizer23->Add( bSizerCornerlist, 0, wxEXPAND, 5 );
m_panelPoly = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL );
m_panelPoly->SetBackgroundColour( wxColour( 0, 0, 0 ) );
m_panelPoly->SetMinSize( wxSize( 300,300 ) );
bSizer23->Add( m_panelPoly, 1, wxEXPAND | wxALL, 5 );
bSizerUpper->Add( bSizer23, 1, wxEXPAND, 5 );
wxFlexGridSizer* fgSizerThickness;
fgSizerThickness = new wxFlexGridSizer( 0, 5, 0, 0 );
fgSizerThickness->AddGrowableCol( 1 );
fgSizerThickness->SetFlexibleDirection( wxBOTH );
fgSizerThickness->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_staticTextThickness = new wxStaticText( this, wxID_ANY, _("Outline thickness"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextThickness->Wrap( -1 );
fgSizerThickness->Add( m_staticTextThickness, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_textCtrlThickness = new wxTextCtrl( this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerThickness->Add( m_textCtrlThickness, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextThicknessUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextThicknessUnit->Wrap( -1 );
fgSizerThickness->Add( m_staticTextThicknessUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
fgSizerThickness->Add( 10, 10, 0, 0, 5 );
m_staticTextInfo = new wxStaticText( this, wxID_ANY, _("(Thickness outline is usually set to 0)"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextInfo->Wrap( -1 );
fgSizerThickness->Add( m_staticTextInfo, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
bSizerUpper->Add( fgSizerThickness, 0, 0, 5 );
bSizerMain->Add( bSizerUpper, 1, wxEXPAND, 5 );
m_staticline3 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizerMain->Add( m_staticline3, 0, wxEXPAND | wxALL, 5 );
m_sdbSizer = new wxStdDialogButtonSizer();
m_sdbSizerOK = new wxButton( this, wxID_OK );
m_sdbSizer->AddButton( m_sdbSizerOK );
m_sdbSizerCancel = new wxButton( this, wxID_CANCEL );
m_sdbSizer->AddButton( m_sdbSizerCancel );
m_sdbSizer->Realize();
bSizerMain->Add( m_sdbSizer, 0, wxEXPAND|wxALL, 5 );
this->SetSizer( bSizerMain );
this->Layout();
this->Centre( wxBOTH );
// Connect Events
m_gridCornersList->Connect( wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onGridSelect ), NULL, this );
m_gridCornersList->Connect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onCellSelect ), NULL, this );
m_buttonAdd->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onButtonAdd ), NULL, this );
m_buttonDelete->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::OnButtonDelete ), NULL, this );
m_panelPoly->Connect( wxEVT_PAINT, wxPaintEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onPaintPolyPanel ), NULL, this );
m_panelPoly->Connect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onPolyPanelResize ), NULL, this );
}
DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::~DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE()
{
// Disconnect Events
m_gridCornersList->Disconnect( wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onGridSelect ), NULL, this );
m_gridCornersList->Disconnect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onCellSelect ), NULL, this );
m_buttonAdd->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onButtonAdd ), NULL, this );
m_buttonDelete->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::OnButtonDelete ), NULL, this );
m_panelPoly->Disconnect( wxEVT_PAINT, wxPaintEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onPaintPolyPanel ), NULL, this );
m_panelPoly->Disconnect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE::onPolyPanelResize ), NULL, this );
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jul 17 2016)
// C++ code generated with wxFormBuilder (version Jul 2 2017)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@ -12,6 +12,7 @@
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class DIALOG_SHIM;
class wxListView;
#include "dialog_shim.h"
#include <wx/string.h>
@ -30,11 +31,14 @@ class DIALOG_SHIM;
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/listctrl.h>
#include <wx/button.h>
#include <wx/notebook.h>
#include <wxBasePcbFrame.h>
#include <pcb_draw_panel_gal.h>
#include <wx/button.h>
#include <wx/dialog.h>
#include <wx/spinctrl.h>
#include <wx/grid.h>
///////////////////////////////////////////////////////////////////////////
@ -145,16 +149,29 @@ class DIALOG_PAD_PROPERTIES_BASE : public DIALOG_SHIM
wxStaticText* m_staticTextRatio;
wxTextCtrl* m_SolderPasteMarginRatioCtrl;
wxStaticText* m_SolderPasteRatioMarginUnits;
wxStaticBoxSizer* m_sbSizerZonesSettings;
wxStaticText* m_staticText40;
wxChoice* m_ZoneConnectionChoice;
wxStaticText* m_staticText53;
wxStaticText* m_staticText49;
wxTextCtrl* m_ThermalWidthCtrl;
wxStaticText* m_ThermalWidthUnits;
wxStaticText* m_staticText52;
wxTextCtrl* m_ThermalGapCtrl;
wxStaticText* m_ThermalGapUnits;
wxStaticText* m_staticTextcps;
wxChoice* m_ZoneCustomPadShape;
wxStaticText* m_staticTextWarning;
wxPanel* m_panelCustomShape;
wxBoxSizer* m_bSizerPanelShapes;
wxStaticText* m_staticTextShapesList;
wxStaticText* m_staticTextShapesListWraning;
wxListView* m_listCtrlShapes;
wxButton* m_buttonDel;
wxButton* m_buttonEditShape;
wxButton* m_buttonAddShape;
wxButton* m_buttonDup;
wxButton* m_buttonGeometry;
wxButton* m_buttonImport;
wxPanel* m_panelShowPad;
PCB_DRAW_PANEL_GAL* m_panelShowPadGal;
KIGFX::GAL_DISPLAY_OPTIONS m_galOptions;
@ -172,6 +189,14 @@ class DIALOG_PAD_PROPERTIES_BASE : public DIALOG_SHIM
virtual void OnSetLayers( wxCommandEvent& event ) { event.Skip(); }
virtual void onCornerSizePercentChange( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDrillShapeSelected( wxCommandEvent& event ) { event.Skip(); }
virtual void onCustomShapeDClick( wxMouseEvent& event ) { event.Skip(); }
virtual void OnPrimitiveSelection( wxListEvent& event ) { event.Skip(); }
virtual void onDeleteShape( wxCommandEvent& event ) { event.Skip(); }
virtual void onEditShape( wxCommandEvent& event ) { event.Skip(); }
virtual void onAddShape( wxCommandEvent& event ) { event.Skip(); }
virtual void onDuplicateShape( wxCommandEvent& event ) { event.Skip(); }
virtual void onGeometryTransform( wxCommandEvent& event ) { event.Skip(); }
virtual void onImportShape( wxCommandEvent& event ) { event.Skip(); }
virtual void OnPaintShowPanel( wxPaintEvent& event ) { event.Skip(); }
@ -182,4 +207,115 @@ class DIALOG_PAD_PROPERTIES_BASE : public DIALOG_SHIM
};
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE : public DIALOG_SHIM
{
private:
protected:
wxStaticText* m_staticTextInfo;
wxStaticText* m_staticTextPosStart;
wxStaticText* m_staticTextStartX;
wxTextCtrl* m_textCtrPosX;
wxStaticText* m_staticTextStartY;
wxTextCtrl* m_textCtrPosY;
wxStaticText* m_staticTextPosUnit;
wxStaticText* m_staticTextPosEnd;
wxStaticText* m_staticTextEndX;
wxTextCtrl* m_textCtrEndX;
wxStaticText* m_staticTextEndY;
wxTextCtrl* m_textCtrEndY;
wxStaticText* m_staticTextEndUnit;
wxStaticText* m_staticTextAngle;
wxTextCtrl* m_textCtrAngle;
wxStaticText* m_staticTextAngleUnit;
wxStaticText* m_staticTextThickness;
wxTextCtrl* m_textCtrlThickness;
wxStaticText* m_staticTextThicknessUnit;
wxStaticLine* m_staticline1;
wxStdDialogButtonSizer* m_sdbSizer;
wxButton* m_sdbSizerOK;
wxButton* m_sdbSizerCancel;
public:
DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 561,243 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_PAD_BASICSHAPES_PROPERTIES_BASE();
};
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE : public DIALOG_SHIM
{
private:
protected:
wxStaticText* m_staticTextMove;
wxStaticText* m_staticTextMoveX;
wxTextCtrl* m_textCtrMoveX;
wxStaticText* m_staticTextMoveY;
wxTextCtrl* m_textCtrMoveY;
wxStaticText* m_staticTextMoveUnit;
wxStaticText* m_staticTextAngle;
wxTextCtrl* m_textCtrAngle;
wxStaticText* m_staticTextAngleUnit;
wxStaticText* m_staticTextSF;
wxTextCtrl* m_textCtrlScalingFactor;
wxStaticText* m_staticTextDupCnt;
wxSpinCtrl* m_spinCtrlDuplicateCount;
wxStaticLine* m_staticline1;
wxStdDialogButtonSizer* m_sdbSizer;
wxButton* m_sdbSizerOK;
wxButton* m_sdbSizerCancel;
public:
DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Pad Custom Shape Geometry Transform"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 472,208 ), long style = wxDEFAULT_DIALOG_STYLE );
~DIALOG_PAD_BASICSHAPES_TRANSFORM_BASE();
};
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE : public DIALOG_SHIM
{
private:
protected:
wxStaticText* m_staticTextShapesListWarning;
wxStaticText* m_staticTextValidate;
wxGrid* m_gridCornersList;
wxButton* m_buttonAdd;
wxButton* m_buttonDelete;
wxPanel* m_panelPoly;
wxStaticText* m_staticTextThickness;
wxTextCtrl* m_textCtrlThickness;
wxStaticText* m_staticTextThicknessUnit;
wxStaticText* m_staticTextInfo;
wxStaticLine* m_staticline3;
wxStdDialogButtonSizer* m_sdbSizer;
wxButton* m_sdbSizerOK;
wxButton* m_sdbSizerCancel;
// Virtual event handlers, overide them in your derived class
virtual void onGridSelect( wxGridRangeSelectEvent& event ) { event.Skip(); }
virtual void onCellSelect( wxGridEvent& event ) { event.Skip(); }
virtual void onButtonAdd( wxCommandEvent& event ) { event.Skip(); }
virtual void OnButtonDelete( wxCommandEvent& event ) { event.Skip(); }
virtual void onPaintPolyPanel( wxPaintEvent& event ) { event.Skip(); }
virtual void onPolyPanelResize( wxSizeEvent& event ) { event.Skip(); }
public:
DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Basic Shape Polygon"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 503,396 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_PAD_BASIC_SHP_POLY_PROPS_BASE();
};
#endif //__DIALOG_PAD_PROPERTIES_BASE_H__

View File

@ -5,9 +5,9 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2016 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
* Copyright (C) 2004-2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2016 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 2017 KiCad Developers, see change_log.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -603,7 +603,9 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
dist = KiROUND( EuclideanNorm( relativePadPos ) );
// Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
if( (dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius()) >= dist_min )
int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius();
if( delta >= dist_min )
return true;
/* Here, pads are near and DRC depend on the pad shapes
@ -639,7 +641,8 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
swap_pads = true;
break;
default:
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CUSTOM:
break;
}
}
@ -686,6 +689,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_RECT:
case PAD_SHAPE_CUSTOM:
// pad_angle = pad orient relative to the aRefPad orient
pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
NORMALIZE_ANGLE_POS( pad_angle );
@ -697,14 +701,29 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ),
aRefPad->GetSize(), aRefPad->GetOrientation() );
}
else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
// The reference pad can be rotated. calculate the rotated
// coordiantes ( note, the ref pad position is the origin of
// coordinates for this drc test)
aRefPad->BasicShapesAsPolygonToBoardPosition( &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() );
}
switch( aPad->GetShape() )
{
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_RECT:
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CUSTOM:
if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
int padRadius = aPad->GetRoundRectCornerRadius();
@ -712,6 +731,16 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos,
aPad->GetSize(), aPad->GetOrientation() );
}
else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
// The pad to compare can be rotated. calculate the rotated
// coordinattes ( note, the pad to compare position
// is the relativePadPos for this drc test
aPad->BasicShapesAsPolygonToBoardPosition( &polysetcompare,
relativePadPos, aPad->GetOrientation() );
}
else
{
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
@ -720,9 +749,11 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
for( int ii = 0; ii < 4; ii++ )
polycompare[ii] += relativePadPos;
}
// And now test polygons:
if( polysetref.OutlineCount() )
// 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:
@ -730,6 +761,24 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
polycompare, 4, dist_min ) )
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, dist_min ) )
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(), dist_min ) )
diag = false;
}
else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) )
diag = false;
break;
@ -783,7 +832,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
}
default:
wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
wxMessageBox( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
break;
}
@ -798,14 +847,33 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
*/
bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
{
wxSize padHalfsize; // half dimension of the pad
int r;
// Note:
// we are using a horizontal segment for test, because we know here
// only the length and orientation+ of the segment
// Therefore the coordinates of the shape of pad to compare
// must be calculated in a axis system rotated by m_segmAngle
// and centered to the segment origin, before they can be tested
// against the segment
// We are using:
// m_padToTestPos the position of the pad shape in this axis system
// m_segmAngle the axis system rotation
int segmHalfWidth = aSegmentWidth / 2;
int distToLine = segmHalfWidth + aMinDist;
wxSize padHalfsize; // half dimension of the pad
if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
// For a custom pad, the pad size has no meaning, we only can
// use the bounding radius
padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius();
}
else
{
padHalfsize.x = aPad->GetSize().x >> 1;
padHalfsize.y = aPad->GetSize().y >> 1;
}
if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
{
@ -848,7 +916,10 @@ bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMi
*/
switch( aPad->GetShape() )
{
default:
case PAD_SHAPE_CIRCLE:
// This case was already tested, so it cannot be found here.
// it is here just to avoid compil warning, and to remember
// it is already tested.
return false;
case PAD_SHAPE_OVAL:
@ -912,11 +983,13 @@ bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMi
break;
case PAD_SHAPE_ROUNDRECT:
{
// a round rect is a smaller rect, with a clearance augmented by the corners radius
r = aPad->GetRoundRectCornerRadius();
int r = aPad->GetRoundRectCornerRadius();
padHalfsize.x -= r;
padHalfsize.y -= r;
distToLine += r;
}
// Fall through
case PAD_SHAPE_RECT:
// the area to test is a rounded rectangle.
@ -992,11 +1065,41 @@ bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMi
RotatePoint( &poly[ii], m_segmAngle );
}
if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ), wxPoint(m_segmLength,0), distToLine ) )
if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ),
wxPoint(m_segmLength,0), distToLine ) )
return false;
break;
}
break;
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polyset;
polyset.Append( aPad->GetCustomShapeAsPolygon() );
// The pad can be rotated. calculate the coordinates
// relatives to the segment being tested
// Note, the pad position relative to the segment origin
// is m_padToTestPos
aPad->BasicShapesAsPolygonToBoardPosition( &polyset,
m_padToTestPos, orient );
// Rotate all coordinates by m_segmAngle, because the segment orient
// is m_segmAngle
// we are using a horizontal segment for test, because we know here
// only the lenght and orientation+ of the segment
// therefore all coordinates of the pad to test must be rotated by
// m_segmAngle (they are already relative to the segment origin)
aPad->BasicShapesAsPolygonToBoardPosition( &polyset,
wxPoint( 0, 0 ), m_segmAngle );
const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
refpoly.PointCount(),
wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
distToLine ) )
return false;
}
break;
}
return true;

View File

@ -1243,6 +1243,7 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const
case PAD_SHAPE_OVAL: shape = "oval"; break;
case PAD_SHAPE_TRAPEZOID: shape = "trapezoid"; break;
case PAD_SHAPE_ROUNDRECT: shape = "roundrect"; break;
case PAD_SHAPE_CUSTOM: shape = "custom"; break;
default:
THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"), aPad->GetShape() ) );
@ -1346,6 +1347,107 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const
m_out->Print( aNestLevel+1, "%s", output.c_str()+1 ); // +1 skips 1st space on 1st element
}
if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
m_out->Print( 0, "\n");
m_out->Print( aNestLevel+1, "(options" );
if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
m_out->Print( 0, " (clearance convexhull)" );
#if 1 // Set to 1 to output the default option
else
m_out->Print( 0, " (clearance outline)" );
#endif
// Output the anchor pad shape (circle/rect)
if( aPad->GetAnchorPadShape() == PAD_SHAPE_RECT )
shape = "rect";
else
shape = "circle";
m_out->Print( 0, " (anchor %s)", shape );
m_out->Print( 0, ")"); // end of (options ...
// Output graphic primitive of the pad shape
m_out->Print( 0, "\n");
m_out->Print( aNestLevel+1, "(primitives" );
int nested_level = aNestLevel+2;
// Output all basic shapes
for( unsigned icnt = 0; icnt < aPad->GetBasicShapes().size(); ++icnt )
{
m_out->Print( 0, "\n");
const PAD_CS_PRIMITIVE& primitive = aPad->GetBasicShapes()[icnt];
switch( primitive.m_Shape )
{
case S_SEGMENT: // usual segment : line with rounded ends
m_out->Print( nested_level, "(gr_line (start %s) (end %s) (width %s))",
FMT_IU( primitive.m_Start ).c_str(),
FMT_IU( primitive.m_End ).c_str(),
FMT_IU( primitive.m_Thickness ).c_str() );
break;
case S_ARC: // Arc with rounded ends
m_out->Print( nested_level, "(gr_arc (start %s) (end %s) (angle %s) (width %s))",
FMT_IU( primitive.m_Start ).c_str(),
FMT_IU( primitive.m_End ).c_str(),
FMT_ANGLE( primitive.m_ArcAngle ).c_str(),
FMT_IU( primitive.m_Thickness ).c_str() );
break;
case S_CIRCLE: // ring or circle (circle if width == 0
m_out->Print( nested_level, "(gr_circle (center %s) (end %s %s) (width %s))",
FMT_IU( primitive.m_Start ).c_str(),
FMT_IU( primitive.m_Start.x + primitive.m_Radius ).c_str(),
FMT_IU( primitive.m_Start.y ).c_str(),
FMT_IU( primitive.m_Thickness ).c_str() );
break;
case S_POLYGON: // polygon
if( primitive.m_Poly.size() < 2 )
break; // Malformed polygon.
{
m_out->Print( nested_level, "(gr_poly (pts\n");
// Write the polygon corners coordinates:
const std::vector< wxPoint>& poly = primitive.m_Poly;
int newLine = 0;
for( unsigned ii = 0; ii < poly.size(); ii++ )
{
if( newLine == 0 )
m_out->Print( nested_level+1, " (xy %s)", FMT_IU( wxPoint( poly[ii].x, poly[ii].y ) ).c_str() );
else
m_out->Print( 0, " (xy %s)", FMT_IU( wxPoint( poly[ii].x, poly[ii].y ) ).c_str() );
if( ++newLine > 4 )
{
newLine = 0;
m_out->Print( 0, "\n" );
}
}
if( primitive.m_Thickness != 0 )
m_out->Print( 0, ") (width %s))", FMT_IU( primitive.m_Thickness ).c_str() );
else
m_out->Print( 0, "))");
}
break;
default:
break;
}
}
m_out->Print( 0, "\n");
m_out->Print( aNestLevel+1, ")" ); // end of (basic_shapes
}
m_out->Print( 0, ")\n" );
}

View File

@ -73,6 +73,10 @@ void PCB_BASE_FRAME::Import_Pad_Settings( D_PAD* aPad, bool aDraw )
aPad->ImportSettingsFromMaster( mp );
aPad->SetBasicShapes( mp.GetBasicShapes() );
aPad->SetAnchorPadShape( mp.GetAnchorPadShape() );
aPad->MergeBasicShapesAsPolygon();
if( aDraw )
m_canvas->RefreshDrawingRect( aPad->GetBoundingBox() );

View File

@ -681,6 +681,8 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
m_gal->Translate( VECTOR2D( aPad->GetPosition() ) );
m_gal->Rotate( -aPad->GetOrientationRadians() );
int custom_margin = 0; // a clearance/margin for custom shape, for solder paste/mask
// Choose drawing settings depending on if we are drawing a pad itself or a hole
if( aLayer == LAYER_PADS_HOLES || aLayer == LAYER_NON_PLATED )
{
@ -692,6 +694,7 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
{
// Drawing soldermask
int soldermaskMargin = aPad->GetSolderMaskMargin();
custom_margin = soldermaskMargin;
m_gal->Translate( VECTOR2D( aPad->GetOffset() ) );
size = VECTOR2D( aPad->GetSize().x / 2.0 + soldermaskMargin,
@ -702,6 +705,8 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
{
// Drawing solderpaste
wxSize solderpasteMargin = aPad->GetSolderPasteMargin();
// try to find a clearance which can be used for custom shapes
custom_margin = solderpasteMargin.x;
m_gal->Translate( VECTOR2D( aPad->GetOffset() ) );
size = VECTOR2D( aPad->GetSize().x / 2.0 + solderpasteMargin.x,
@ -788,6 +793,55 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
break;
}
case PAD_SHAPE_CUSTOM:
{ // Draw the complex custom shape
std::deque<VECTOR2D> pointList;
// Use solder[Paste/Mask]size or pad size to build pad shape
// however, solder[Paste/Mask] size has no actual meaning for a
// custom shape, because it is a set of basic shapes
// We use the custom_margin (good for solder mask, but approximative
// for solder paste).
if( custom_margin )
{
SHAPE_POLY_SET outline;
outline.Append( aPad->GetCustomShapeAsPolygon() );
const int segmentToCircleCount = 32;
outline.Inflate( custom_margin, segmentToCircleCount );
// Draw the polygon: only one polygon is expected
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
for( int ii = 0; ii < poly.PointCount(); ii++ )
pointList.push_back( poly.Point( ii ) );
}
else
{
// Draw the polygon: only one polygon is expected
// However we provide a multi polygon shape drawing
// ( for the future or to show even an incorrect shape
const SHAPE_POLY_SET& outline = aPad->GetCustomShapeAsPolygon();
for( int jj = 0; jj < outline.OutlineCount(); ++jj )
{
const SHAPE_LINE_CHAIN& poly = outline.COutline( jj );
for( int ii = 0; ii < poly.PointCount(); ii++ )
pointList.push_back( poly.CPoint( ii ) );
}
}
if( m_pcbSettings.m_sketchMode[LAYER_PADS] )
{
// Add the beginning point to close the outline
pointList.push_back( pointList.front() );
m_gal->DrawPolyline( pointList );
}
else
m_gal->DrawPolygon( pointList );
}
break;
case PAD_SHAPE_TRAPEZOID:
{
std::deque<VECTOR2D> pointList;

View File

@ -1910,7 +1910,6 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
Expecting( "smd and/or virtual" );
}
}
break;
case T_fp_text:
@ -2307,6 +2306,10 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent )
pad->SetShape( PAD_SHAPE_ROUNDRECT );
break;
case T_custom:
pad->SetShape( PAD_SHAPE_CUSTOM );
break;
default:
Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
}
@ -2481,17 +2484,141 @@ D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent )
NeedRIGHT();
break;
case T_options:
parseD_PAD_option( pad.get() );
break;
case T_primitives:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_LEFT )
token = NextTok();
// Currently, I am using parseDRAWSEGMENT() to read basic shapes parameters,
// because they are the same as a DRAWSEGMENT.
// However it could be better to write a specific parser, to avoid possible issues
// if the DRAWSEGMENT parser is modified.
DRAWSEGMENT* dummysegm = NULL;
switch( token )
{
case T_gr_arc:
dummysegm = parseDRAWSEGMENT();
pad->AddBasicShape( dummysegm->GetCenter(), dummysegm->GetArcStart(),
dummysegm->GetAngle(), dummysegm->GetWidth() );
break;
case T_gr_line:
dummysegm = parseDRAWSEGMENT();
pad->AddBasicShape( dummysegm->GetStart(), dummysegm->GetEnd(),
dummysegm->GetWidth() );
break;
case T_gr_circle:
dummysegm = parseDRAWSEGMENT();
pad->AddBasicShape( dummysegm->GetCenter(), dummysegm->GetRadius(),
dummysegm->GetWidth() );
break;
case T_gr_poly:
dummysegm = parseDRAWSEGMENT();
pad->AddBasicShape( dummysegm->GetPolyPoints(), dummysegm->GetWidth() );
break;
default:
Expecting( "at, drill, layers, net, die_length, solder_mask_margin, roundrect_rratio,"
"solder_paste_margin, solder_paste_margin_ratio, clearance, "
"zone_connect, fp_poly, basic_shapes, thermal_width, or thermal_gap" );
Expecting( "gr_line, gr_arc, gr_circle or gr_poly" );
break;
}
delete dummysegm;
}
break;
default:
Expecting( "at, drill, layers, net, die_length, solder_mask_margin, roundrect_rratio,\n"
"solder_paste_margin, solder_paste_margin_ratio, clearance,\n"
"zone_connect, fp_poly, primitives, thermal_width, or thermal_gap" );
}
}
// Be sure the custom shape polygon is built:
if( pad->GetShape() == PAD_SHAPE_CUSTOM )
pad->MergeBasicShapesAsPolygon();
return pad.release();
}
bool PCB_PARSER::parseD_PAD_option( D_PAD* aPad )
{
// Parse only the (option ...) inside a pad description
for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_anchor:
token = NextTok();
// Custom shaped pads have a "anchor pad", which is the reference
// for connection calculations.
// Because this is an anchor, only the 2 very basic shapes are managed:
// circle and rect. The default is circle
switch( token )
{
case T_circle: // default
break;
case T_rect:
aPad->SetAnchorPadShape( PAD_SHAPE_RECT );
break;
default:
// Currently, because pad options is a moving target
// just skip unknown keywords
break;
}
NeedRIGHT();
break;
case T_clearance:
token = NextTok();
// Custom shaped pads have a clearance area that is the pad shape
// (like usual pads) or the convew hull of the pad shape.
switch( token )
{
case T_outline:
aPad->SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE_OUTLINE );
break;
case T_convexhull:
aPad->SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL );
break;
default:
// Currently, because pad options is a moving target
// just skip unknown keywords
break;
}
NeedRIGHT();
break;
default:
// Currently, because pad options is a moving target
// just skip unknown keywords
while( (token = NextTok() ) != T_RIGHT )
{}
break;
}
}
return true;
}
TRACK* PCB_PARSER::parseTRACK()
{
wxCHECK_MSG( CurTok() == T_segment, NULL,

View File

@ -134,6 +134,8 @@ class PCB_PARSER : public PCB_LEXER
TEXTE_MODULE* parseTEXTE_MODULE();
EDGE_MODULE* parseEDGE_MODULE();
D_PAD* parseD_PAD( MODULE* aParent = NULL );
// Parse only the (option ...) inside a pad description
bool parseD_PAD_option( D_PAD* aPad );
TRACK* parseTRACK();
VIA* parseVIA();
ZONE_CONTAINER* parseZONE_CONTAINER();

View File

@ -182,6 +182,19 @@ void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, COLOR4D aColor, EDA_DRAW_MODE_T aPl
aPad->GetOrientation(), aPlotMode, &gbr_metadata );
break;
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET polygons;
aPad->MergeBasicShapesAsPolygon(&polygons, 64 );
if( polygons.OutlineCount() == 0 )
break;
aPad->BasicShapesAsPolygonToBoardPosition( &polygons, shape_pos, aPad->GetOrientation() );
m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), &polygons, aPlotMode, &gbr_metadata );
}
break;
case PAD_SHAPE_RECT:
default:
m_plotter->FlashPadRect( shape_pos, aPad->GetSize(),

View File

@ -626,6 +626,26 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE::syncPad( D_PAD* aPad )
}
break;
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET outline;
outline.Append( aPad->GetCustomShapeAsPolygon() );
aPad->BasicShapesAsPolygonToBoardPosition( &outline, wx_c, aPad->GetOrientation() );
for( int jj = 0; jj < outline.OutlineCount(); ++jj )
{
SHAPE_CONVEX* shape = new SHAPE_CONVEX();
const SHAPE_LINE_CHAIN& poly = outline.COutline( jj );
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;
@ -722,6 +742,26 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE::syncPad( D_PAD* aPad )
break;
}
case PAD_SHAPE_CUSTOM:
{
SHAPE_POLY_SET outline;
outline.Append( aPad->GetCustomShapeAsPolygon() );
aPad->BasicShapesAsPolygonToBoardPosition( &outline, wx_c, aPad->GetOrientation() );
for( int jj = 0; jj < outline.OutlineCount(); ++jj )
{
SHAPE_CONVEX* shape = new SHAPE_CONVEX();
const SHAPE_LINE_CHAIN& poly = outline.COutline( jj );
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;

View File

@ -69,6 +69,7 @@
#include <geometry/shape_poly_set.h>
#include <geometry/shape_file_io.h>
#include <geometry/convex_hull.h>
/* DEBUG OPTION:
* To emit zone data to a file when filling zones for the debugging purposes,
@ -194,6 +195,31 @@ void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeature
if( item_boundingbox.Intersects( zone_boundingbox ) )
{
int clearance = std::max( zone_clearance, item_clearance );
// PAD_SHAPE_CUSTOM can have a specific keepout, to avoid to break the shape
if( pad->GetShape() == PAD_SHAPE_CUSTOM &&
pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
{
// the pad shape in zone can be its convex hull or
// the shape itself
SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
outline.Inflate( KiROUND( clearance*correctionFactor) , segsPerCircle );
pad->BasicShapesAsPolygonToBoardPosition( &outline,
pad->GetPosition(), pad->GetOrientation() );
if( pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
{
std::vector<wxPoint> convex_hull;
BuildConvexHull( convex_hull, outline );
aFeatures.NewOutline();
for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
aFeatures.Append( convex_hull[ii] );
}
else
aFeatures.Append( outline );
}
else
pad->TransformShapeWithClearanceToPolygon( aFeatures,
clearance,
segsPerCircle,
@ -204,7 +230,10 @@ void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeature
}
// Pads are removed from zone if the setup is PAD_ZONE_CONN_NONE
if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE )
// or if they have a custom shape, because a thermal relief will break
// the shape
if( GetPadConnection( pad ) == PAD_ZONE_CONN_NONE ||
pad->GetShape() == PAD_SHAPE_CUSTOM )
{
int gap = zone_clearance;
int thermalGap = GetThermalReliefGap( pad );
@ -214,10 +243,28 @@ void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeature
if( item_boundingbox.Intersects( zone_boundingbox ) )
{
// PAD_SHAPE_CUSTOM has a specific keepout, to avoid to break the shape
// the pad shape in zone can be its convex hull or the shape itself
if( pad->GetShape() == PAD_SHAPE_CUSTOM &&
pad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
{
// the pad shape in zone can be its convex hull or
// the shape itself
SHAPE_POLY_SET outline( pad->GetCustomShapeAsPolygon() );
outline.Inflate( KiROUND( gap*correctionFactor) , segsPerCircle );
pad->BasicShapesAsPolygonToBoardPosition( &outline,
pad->GetPosition(), pad->GetOrientation() );
std::vector<wxPoint> convex_hull;
BuildConvexHull( convex_hull, outline );
aFeatures.NewOutline();
for( unsigned ii = 0; ii < convex_hull.size(); ++ii )
aFeatures.Append( convex_hull[ii] );
}
else
pad->TransformShapeWithClearanceToPolygon( aFeatures,
gap,
segsPerCircle,
correctionFactor );
gap, segsPerCircle, correctionFactor );
}
}
}