Thermal shapes modification for round and oblong pads.
This is a workaround for a bug (i believe) of kbool.
This commit is contained in:
parent
1eb6220ae7
commit
134c07f94b
|
@ -5,6 +5,18 @@ Started 2007-June-11
|
|||
Please add newer entries at the top, list the date and your name with
|
||||
email address.
|
||||
|
||||
2008-Nov-8 UPDATE Jean-Pierre Charras <jean-pierre.charras@inpg.fr>
|
||||
================================================================================
|
||||
++pcbnew
|
||||
Thermal shapes modification for round and oblong pads.
|
||||
This is a workaround for a bug (i believe) of kbool.
|
||||
New thermal shapes have NO angle < 90 degrees between 2 adjacent segments.
|
||||
For shapes that have angle < 90 degrees between 2 adjacent segments,
|
||||
under certains circumstances kbool drops some holes and creates a bad filled area.
|
||||
(see zones_convert_brd_items_to_polygons.cpp)
|
||||
|
||||
|
||||
|
||||
2008-Nov-8 UPDATE Dick Hollenbeck <dick@softplc.com>
|
||||
================================================================================
|
||||
+gerview
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
/*******************************************/
|
||||
/* zones_convert_brd_items_to_polygons.cpp */
|
||||
/*******************************************/
|
||||
|
||||
/* Functions to convert some board items to polygons
|
||||
(pads, tracks ..)
|
||||
This is used to calculate filled areas in copper zones.
|
||||
Filled areas are the full zone area sub all polygons calculated from these items and the clearance area
|
||||
*/
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <math.h>
|
||||
|
@ -40,6 +50,7 @@ double s_Correction; /* mult coeff used to enlarge rounded and oval pads (and
|
|||
* because the segment approximation for arcs and circles
|
||||
* create a smaller gap than a true circle
|
||||
*/
|
||||
|
||||
/** function AddClearanceAreasPolygonsToPolysList
|
||||
* Add non copper areas polygons (pads and tracks with clearence)
|
||||
* to a filled copper area
|
||||
|
@ -109,6 +120,7 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb )
|
|||
EDA_Rect item_boundingbox;
|
||||
EDA_Rect zone_boundingbox = GetBoundingBox();
|
||||
zone_boundingbox.Inflate( m_ZoneClearance, clearance );
|
||||
|
||||
/*
|
||||
* First : Add pads
|
||||
*/
|
||||
|
@ -353,6 +365,24 @@ void AddPadWithClearancePolygon( Bool_Engine* aBooleng,
|
|||
* each corner of a polygon if calculated for a pad at position 0, 0, orient 0,
|
||||
* and then moved and rotated acroding to the pad position and orientation
|
||||
*/
|
||||
|
||||
/* WARNING:
|
||||
* When Kbool calculates the filled areas :
|
||||
* i.e when substarcting holes (thermal shapes) to the full zone area
|
||||
* under certains circumstances kboll drop some holes.
|
||||
* These circumstances are:
|
||||
* some identical holes (same thermal shape and size) are *exactly* on the same vertical line
|
||||
* And
|
||||
* nothing else between holes
|
||||
* And
|
||||
* angles less than 90 deg between 2 consecutive lines in hole outline
|
||||
* And a hole above the identical holes
|
||||
*
|
||||
* In fact, it is easy to find these conditions in pad arrays.
|
||||
* So to avoid this, the workaround is do not use holes outlines that include
|
||||
* angles less than 90 deg between 2 consecutive lines
|
||||
* this is made in round and oblong thermal reliefs
|
||||
*/
|
||||
void AddThermalReliefPadPolygon( Bool_Engine* aBooleng,
|
||||
D_PAD& aPad,
|
||||
int aThermalGap,
|
||||
|
@ -388,44 +418,61 @@ void AddThermalReliefPadPolygon( Bool_Engine* aBooleng,
|
|||
*/
|
||||
|
||||
// Build the hole pattern, for the hole in the X >0, Y > 0 plane:
|
||||
std::vector <int> corners_buffer;
|
||||
// The pattern roughtly is a 90 deg arc pie
|
||||
std::vector <wxPoint> corners_buffer;
|
||||
|
||||
// Crosspoint of thermal spoke sides, the first point of polygon buffer
|
||||
corners_buffer.push_back( wxPoint( copper_tickness.x / 2, copper_tickness.y / 2 ) );
|
||||
|
||||
// Add an intermediate point on spoke sides, to allow a > 90 deg angle between side and first seg of arc approx
|
||||
corner.x = copper_tickness.x / 2;
|
||||
int y = dx + aThermalGap - (aThermalGap / 3);
|
||||
corner.y = (int) sqrt( ( ( (double) y * y ) - (double) corner.x * corner.x ) );
|
||||
corners_buffer.push_back( corner );
|
||||
|
||||
// calculate the starting point of the outter arc
|
||||
dx += aThermalGap; // The radius of the outter arc is dx = pad radius + aThermalGap
|
||||
corner.x = copper_tickness.x / 2;
|
||||
double dtmp = ( (double) dx * dx ) - ( (double) corner.x * corner.x );
|
||||
corner.y = (int) sqrt( dtmp );
|
||||
double dtmp = sqrt( ( (double) dx * dx ) - ( (double) corner.x * corner.x ) );
|
||||
corner.y = (int) dtmp;
|
||||
|
||||
// calculates the position of the first point of the arc section
|
||||
RotatePoint( &corner, delta );
|
||||
|
||||
// calculate the ending point of the outter arc
|
||||
corner_end.x = corner.y;
|
||||
corner_end.y = copper_tickness.y / 2;
|
||||
corner_end.y = corner.x;
|
||||
|
||||
// calculate intermediate points (y coordinate from corner.y to corner_end.y
|
||||
while( (corner.y > corner_end.y) && (corner.x < corner_end.x) )
|
||||
{
|
||||
corners_buffer.push_back( corner.x );
|
||||
corners_buffer.push_back( corner.y );
|
||||
corners_buffer.push_back( corner );
|
||||
RotatePoint( &corner, delta );
|
||||
}
|
||||
|
||||
corners_buffer.push_back( corner_end.x );
|
||||
corners_buffer.push_back( corner_end.y );
|
||||
|
||||
/* add the radius lines */
|
||||
corners_buffer.push_back( copper_tickness.x / 2 );
|
||||
corners_buffer.push_back( copper_tickness.y / 2 );
|
||||
corners_buffer.push_back( corner_end );
|
||||
|
||||
/* add an intermediate point, to avoid angles < 90 deg between last arc approx line and radius line
|
||||
*/
|
||||
corner.x = corners_buffer[1].y;
|
||||
corner.y = corners_buffer[1].x;
|
||||
corners_buffer.push_back( corner );
|
||||
|
||||
// Now, add the 4 holes ( each is the pattern, rotated by 0, 90, 180 and 270 deg
|
||||
angle = 450; // TODO: problems with kbool if angle = 0 (bad filled polygon on some pads, but not alls)
|
||||
// WARNING: problems with kbool if angle = 0 (in fact when angle < 200):
|
||||
// bad filled polygon on some cases, when pads are on a same vertical line
|
||||
// this seems a bug in kbool polygon
|
||||
// angle = 450 (45.0 degrees orientation) seems work fine.
|
||||
// angle = 0 with thermal shapes without angle < 90 deg seems works fine also
|
||||
angle = 450;
|
||||
int angle_pad = aPad.m_Orient; // Pad orientation
|
||||
for( unsigned ihole = 0; ihole < 4; ihole++ )
|
||||
{
|
||||
if( aBooleng->StartPolygonAdd( GROUP_B ) )
|
||||
{
|
||||
for( unsigned ii = 0; ii < corners_buffer.size(); ii += 2 )
|
||||
for( unsigned ii = 0; ii < corners_buffer.size(); ii++ )
|
||||
{
|
||||
corner = wxPoint( corners_buffer[ii], corners_buffer[ii + 1] );
|
||||
corner = corners_buffer[ii];
|
||||
RotatePoint( &corner, angle + angle_pad ); // Rotate by segment angle and pad orientation
|
||||
corner += PadShapePos;
|
||||
aBooleng->AddPoint( corner.x, corner.y );
|
||||
|
@ -442,7 +489,6 @@ void AddThermalReliefPadPolygon( Bool_Engine* aBooleng,
|
|||
case PAD_OVAL:
|
||||
{
|
||||
// Oval pad support along the lines of round and rectangular pads
|
||||
|
||||
std::vector <wxPoint> corners_buffer; // Polygon buffer as vector
|
||||
|
||||
int dx = (aPad.m_Size.x / 2) + aThermalGap; // Cutout radius x
|
||||
|
@ -450,85 +496,72 @@ void AddThermalReliefPadPolygon( Bool_Engine* aBooleng,
|
|||
|
||||
wxPoint shape_offset;
|
||||
|
||||
if( dx > dy ) // Some coordinate fiddling, depending on the shape offset direction
|
||||
// We want to calculate an oval shape with dx > dy.
|
||||
// if this is not the case, exchange dx and dy, and rotate the shape 90 deg.
|
||||
int supp_angle = 0;
|
||||
if( dx < dy )
|
||||
{
|
||||
shape_offset = wxPoint( (dx - dy), 0 );
|
||||
EXCHG( dx, dy );
|
||||
supp_angle = 900;
|
||||
EXCHG( copper_tickness.x, copper_tickness.y);
|
||||
}
|
||||
int deltasize = dx - dy;
|
||||
// here we have dx > dy
|
||||
// Some coordinate fiddling, depending on the shape offset direction
|
||||
shape_offset = wxPoint( deltasize, 0 );
|
||||
|
||||
// Crosspoint of thermal spoke sides, the first point of polygon buffer
|
||||
corners_buffer.push_back( wxPoint( copper_tickness.x / 2, copper_tickness.y / 2 ) );
|
||||
|
||||
// Arc start point calculation, the intersecting point of cutout arc and thermal spoke edge
|
||||
if( copper_tickness.x > dx - dy ) // If copper thickness is more than shape offset, we need to calculate arc intercept point.
|
||||
if( copper_tickness.x > deltasize ) // If copper thickness is more than shape offset, we need to calculate arc intercept point.
|
||||
{
|
||||
corner.x = copper_tickness.x / 2;
|
||||
corner.y =
|
||||
(int) sqrt( (double) ( dy * dy ) -
|
||||
( ( corner.x - (dx - dy) ) * ( corner.x - (dx - dy) ) ) );
|
||||
corner.x -= (dx - dy);
|
||||
( ( corner.x - delta ) * ( corner.x - deltasize ) ) );
|
||||
corner.x -= deltasize;
|
||||
|
||||
/* creates an intermediate point, to have a > 90 deg angle
|
||||
* between the side and the first segment of arc approximation
|
||||
*/
|
||||
wxPoint intpoint = corner;
|
||||
intpoint.y -= aThermalGap/3;
|
||||
corners_buffer.push_back( intpoint + shape_offset );
|
||||
RotatePoint( &corner, delta );
|
||||
}
|
||||
else
|
||||
{
|
||||
corner.x = copper_tickness.x / 2;
|
||||
corner.y = dy;
|
||||
corners_buffer.push_back( corner );
|
||||
corner.x = ( (dx - dy) - copper_tickness.x ) / 2;
|
||||
corner.x = ( deltasize - copper_tickness.x ) / 2;
|
||||
}
|
||||
|
||||
// Arc stop point calculation, the intersecting point of cutout arc and thermal spoke edge
|
||||
corner_end.y = copper_tickness.y / 2;
|
||||
corner_end.x = (int) sqrt( (double) ( ( dx * dx ) - ( corner_end.y * corner_end.y ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
shape_offset = wxPoint( 0, (dx - dy) );
|
||||
corners_buffer.push_back( wxPoint( copper_tickness.x / 2, -copper_tickness.y / 2 ) );
|
||||
|
||||
if( copper_tickness.y > dy - dx )
|
||||
{
|
||||
corner.y = copper_tickness.y / 2;
|
||||
corner.x =
|
||||
(int) sqrt( (double) ( dx *
|
||||
dx ) -
|
||||
( ( corner.y - (dy - dx) ) * ( corner.y - (dy - dx) ) ) );
|
||||
corner.y = ( -copper_tickness.y / 2 ) + (dy - dx);
|
||||
}
|
||||
else
|
||||
{
|
||||
corner.y = -copper_tickness.y / 2;
|
||||
corner.x = dx;
|
||||
corners_buffer.push_back( corner );
|
||||
corner.y = ( (dy - dx) - copper_tickness.y ) / 2;
|
||||
}
|
||||
corner_end.x = copper_tickness.x / 2;
|
||||
corner_end.y = -(int) sqrt( (double) ( ( dy * dy ) - ( corner_end.x * corner_end.x ) ) );
|
||||
}
|
||||
|
||||
|
||||
// calculate intermediate points till limit is reached
|
||||
if( dx > dy )
|
||||
{
|
||||
while( (corner.y > corner_end.y) && (corner.x < corner_end.x) )
|
||||
{
|
||||
corners_buffer.push_back( corner + shape_offset );
|
||||
RotatePoint( &corner, delta );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while( (corner.y > corner_end.y) && (corner.x > corner_end.x) )
|
||||
{
|
||||
corners_buffer.push_back( corner + shape_offset );
|
||||
RotatePoint( &corner, delta );
|
||||
}
|
||||
}
|
||||
|
||||
//corners_buffer.push_back(corner + shape_offset); // TODO: about one mil geometry error forms somewhere.
|
||||
/* Moves the last point, to have a > 90 deg angle
|
||||
* between the side and the last segment of arc approximation
|
||||
*/
|
||||
// TODO: calculate a better point, in order to have to have
|
||||
// a best shape.
|
||||
corner_end.x -= aThermalGap/3;
|
||||
corners_buffer.pop_back();
|
||||
corners_buffer.push_back( corner_end ); // Enabling the line above shows intersection point.
|
||||
|
||||
|
||||
/* Create 2 holes, rotated by pad rotation.
|
||||
*/
|
||||
angle = aPad.m_Orient;
|
||||
angle = aPad.m_Orient + supp_angle;
|
||||
for( int irect = 0; irect < 2; irect++ )
|
||||
{
|
||||
if( aBooleng->StartPolygonAdd( GROUP_B ) )
|
||||
|
@ -552,12 +585,12 @@ void AddThermalReliefPadPolygon( Bool_Engine* aBooleng,
|
|||
for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
|
||||
{
|
||||
wxPoint swap = corners_buffer[ic];
|
||||
swap = wxPoint( -swap.x, swap.y );
|
||||
swap.x = -swap.x;
|
||||
corners_buffer[ic] = swap;
|
||||
}
|
||||
|
||||
// Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
|
||||
angle = aPad.m_Orient;
|
||||
angle = aPad.m_Orient + supp_angle;
|
||||
for( int irect = 0; irect < 2; irect++ )
|
||||
{
|
||||
if( aBooleng->StartPolygonAdd( GROUP_B ) )
|
||||
|
|
|
@ -522,11 +522,29 @@ int CPolyLine::MakeKboolPoly( int aStart_contour, int aEnd_contour, std::vector<
|
|||
void ArmBoolEng( Bool_Engine* aBooleng, bool aConvertHoles )
|
||||
{
|
||||
// set some global vals to arm the boolean engine
|
||||
double DGRID = 1000; // round coordinate X or Y value in calculations to this
|
||||
double MARGE = 0.001; // snap with in this range points to lines in the intersection routines
|
||||
// should always be > DGRID a MARGE >= 10*DGRID is oke
|
||||
|
||||
// input points are scaled up with GetDGrid() * GetGrid()
|
||||
|
||||
// DGRID is only meant to make fractional parts of input data which
|
||||
/*
|
||||
The input data scaled up with DGrid is related to the accuracy the user has in his input data.
|
||||
User data with a minimum accuracy of 0.001, means set the DGrid to 1000.
|
||||
The input data may contain data with a minimum accuracy much smaller, but by setting the DGrid
|
||||
everything smaller than 1/DGrid is rounded.
|
||||
|
||||
DGRID is only meant to make fractional parts of input data which can be
|
||||
doubles, part of the integers used in vertexes within the boolean algorithm.
|
||||
And therefore DGRID bigger than 1 is not usefull, you would only loose accuracy.
|
||||
Within the algorithm all input data is multiplied with DGRID, and the result
|
||||
is rounded to an integer.
|
||||
*/
|
||||
double DGRID = 1.0; // round coordinate X or Y value in calculations to this (initial value = 1000.0 in kbool example)
|
||||
// Note: in kicad, coordinates are already integer so DGRID can be set to 1
|
||||
|
||||
double MARGE = 2.0; // snap with in this range points to lines in the intersection routines
|
||||
// should always be > DGRID a MARGE >= 10*DGRID is ok
|
||||
// this is also used to remove small segments and to decide when
|
||||
// two segments are in line.
|
||||
// two segments are in line. ( initial value = 0.001 )
|
||||
double CORRECTIONFACTOR = 500.0; // correct the polygons by this number
|
||||
double CORRECTIONABER = 1.0; // the accuracy for the rounded shapes used in correction
|
||||
double ROUNDFACTOR = 1.5; // when will we round the correction shape to a circle
|
||||
|
@ -534,13 +552,14 @@ void ArmBoolEng( Bool_Engine* aBooleng, bool aConvertHoles )
|
|||
double MAXLINEMERGE = 1000.0; // leave as is, segments of this length in smoothen
|
||||
|
||||
|
||||
// DGRID is only meant to make fractional parts of input data which
|
||||
// are doubles, part of the integers used in vertexes within the boolean algorithm.
|
||||
// Within the algorithm all input data is multiplied with DGRID
|
||||
|
||||
// space for extra intersection inside the boolean algorithms
|
||||
// only change this if there are problems
|
||||
int GRID = 10000;
|
||||
/*
|
||||
Grid makes sure that the integer data used within the algorithm has room for extra intersections
|
||||
smaller than the smallest number within the input data.
|
||||
The input data scaled up with DGrid is related to the accuracy the user has in his input data.
|
||||
Another scaling with Grid is applied on top of it to create space in the integer number for
|
||||
even smaller numbers.
|
||||
*/
|
||||
int GRID = 100; // initial value = 10000 in kbool example
|
||||
|
||||
aBooleng->SetMarge( MARGE );
|
||||
aBooleng->SetGrid( GRID );
|
||||
|
|
Loading…
Reference in New Issue