Reverted commits that remove boost::polygon dependency (need more testing).
This commit is contained in:
parent
9f18e5a98f
commit
d2ebf688f9
|
@ -49,7 +49,7 @@
|
|||
|
||||
class BOARD_DESIGN_SETTINGS;
|
||||
class EDA_3D_FRAME;
|
||||
class SHAPE_POLY_SET;
|
||||
class CPOLYGONS_LIST;
|
||||
class REPORTER;
|
||||
|
||||
class VIA;
|
||||
|
@ -306,7 +306,7 @@ private:
|
|||
* Used only to draw pads outlines on silkscreen layers.
|
||||
*/
|
||||
void buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aWidth,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor );
|
||||
|
|
|
@ -637,7 +637,9 @@ void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* a
|
|||
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
|
||||
BOARD* pcb = GetBoard();
|
||||
|
||||
SHAPE_POLY_SET bufferPolys;
|
||||
CPOLYGONS_LIST bufferPolys;
|
||||
|
||||
bufferPolys.reserve( 5000 ); // Reserve for items not on board
|
||||
|
||||
static const LAYER_ID sequence[] = {
|
||||
Dwgs_User,
|
||||
|
@ -699,10 +701,12 @@ void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* a
|
|||
|
||||
// bufferPolys contains polygons to merge. Many overlaps .
|
||||
// Calculate merged polygons and remove pads and vias holes
|
||||
if( bufferPolys.IsEmpty() )
|
||||
if( bufferPolys.GetCornersCount() == 0 )
|
||||
continue;
|
||||
|
||||
bufferPolys.Fracture();
|
||||
KI_POLYGON_SET currLayerPolyset;
|
||||
KI_POLYGON_SET polyset;
|
||||
bufferPolys.ExportTo( polyset );
|
||||
currLayerPolyset += polyset;
|
||||
|
||||
int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
|
||||
int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );
|
||||
|
@ -715,6 +719,9 @@ void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* a
|
|||
else
|
||||
zpos -= thickness/2 ;
|
||||
|
||||
bufferPolys.RemoveAllContours();
|
||||
bufferPolys.ImportFrom( currLayerPolyset );
|
||||
|
||||
float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)
|
||||
|
||||
// If we are not using thickness, then the znormal must face the layer direction
|
||||
|
|
|
@ -62,7 +62,7 @@ void TransfertToGLlist( std::vector< S3D_VERTEX >& aVertices, double aBiuTo3DUni
|
|||
* from Z position = aZpos to aZpos + aHeight
|
||||
* Used to create the vertical sides of 3D horizontal shapes with thickness.
|
||||
*/
|
||||
static void Draw3D_VerticalPolygonalCylinder( const SHAPE_POLY_SET& aPolysList,
|
||||
static void Draw3D_VerticalPolygonalCylinder( const CPOLYGONS_LIST& aPolysList,
|
||||
int aHeight, int aZpos,
|
||||
bool aInside, double aBiuTo3DUnits )
|
||||
{
|
||||
|
@ -87,28 +87,29 @@ static void Draw3D_VerticalPolygonalCylinder( const SHAPE_POLY_SET& aPolysList,
|
|||
coords[3].z = coords[0].z;
|
||||
|
||||
// Draw the vertical polygonal side
|
||||
for( int ii = 0; ii < aPolysList.OutlineCount(); ii++ )
|
||||
int startContour = 0;
|
||||
for( unsigned ii = 0; ii < aPolysList.GetCornersCount(); ii++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& path = aPolysList.COutline( ii );
|
||||
unsigned jj = ii + 1;
|
||||
|
||||
for( int jj = 0; jj < path.PointCount(); jj++ )
|
||||
if( aPolysList.IsEndContour( ii ) || jj >= aPolysList.GetCornersCount() )
|
||||
{
|
||||
const VECTOR2I& a = path.CPoint( jj );
|
||||
const VECTOR2I& b = path.CPoint( jj + 1 );
|
||||
|
||||
// Build the 4 vertices of each GL_QUAD
|
||||
coords[0].x = a.x;
|
||||
coords[0].y = -a.y;
|
||||
coords[1].x = coords[0].x;
|
||||
coords[1].y = coords[0].y; // only z change
|
||||
coords[2].x = b.x;
|
||||
coords[2].y = -b.y;
|
||||
coords[3].x = coords[2].x;
|
||||
coords[3].y = coords[2].y; // only z change
|
||||
|
||||
// Creates the GL_QUAD
|
||||
TransfertToGLlist( coords, aBiuTo3DUnits );
|
||||
jj = startContour;
|
||||
startContour = ii + 1;
|
||||
}
|
||||
|
||||
// Build the 4 vertices of each GL_QUAD
|
||||
coords[0].x = aPolysList.GetX( ii );
|
||||
coords[0].y = -aPolysList.GetY( ii );
|
||||
coords[1].x = coords[0].x;
|
||||
coords[1].y = coords[0].y; // only z change
|
||||
coords[2].x = aPolysList.GetX( jj );
|
||||
coords[2].y = -aPolysList.GetY( jj );
|
||||
coords[3].x = coords[2].x;
|
||||
coords[3].y = coords[2].y; // only z change
|
||||
|
||||
// Creates the GL_QUAD
|
||||
TransfertToGLlist( coords, aBiuTo3DUnits );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +147,7 @@ void SetGLTexture( GLuint text_id, float scale )
|
|||
* The top side is located at aZpos + aThickness / 2
|
||||
* The bottom side is located at aZpos - aThickness / 2
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList,
|
||||
void Draw3D_SolidHorizontalPolyPolygons( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness, double aBiuTo3DUnits,
|
||||
bool aUseTextures,
|
||||
float aNormal_Z_Orientation )
|
||||
|
@ -175,7 +176,7 @@ void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList,
|
|||
glNormal3f( 0.0, 0.0, aNormal_Z_Orientation );
|
||||
|
||||
// Draw solid areas contained in this list
|
||||
SHAPE_POLY_SET polylist = aPolysList; // temporary copy for gluTessVertex
|
||||
CPOLYGONS_LIST polylist = aPolysList; // temporary copy for gluTessVertex
|
||||
|
||||
int startContour;
|
||||
|
||||
|
@ -183,7 +184,7 @@ void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList,
|
|||
{
|
||||
startContour = 1;
|
||||
|
||||
for ( SHAPE_POLY_SET::ITERATOR ii = polylist.Iterate(); ii; ++ii )
|
||||
for( unsigned ii = 0; ii < polylist.GetCornersCount(); ii++ )
|
||||
{
|
||||
if( startContour == 1 )
|
||||
{
|
||||
|
@ -192,16 +193,16 @@ void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList,
|
|||
startContour = 0;
|
||||
}
|
||||
|
||||
v_data[0] = ii->x * aBiuTo3DUnits;
|
||||
v_data[1] = -ii->y * aBiuTo3DUnits;
|
||||
v_data[0] = polylist.GetX( ii ) * aBiuTo3DUnits;
|
||||
v_data[1] = -polylist.GetY( ii ) * aBiuTo3DUnits;
|
||||
// gluTessVertex store pointers on data, not data, so do not store
|
||||
// different corners values in a temporary variable
|
||||
// but send pointer on each CPolyPt value in polylist
|
||||
// before calling gluDeleteTess
|
||||
gluTessVertex( tess, v_data, &ii.Get() );
|
||||
gluTessVertex( tess, v_data, &polylist[ii] );
|
||||
|
||||
|
||||
if( ii.IsEndContour() )
|
||||
if( polylist.IsEndContour( ii ) )
|
||||
{
|
||||
gluTessEndContour( tess );
|
||||
gluTessEndPolygon( tess );
|
||||
|
@ -241,15 +242,14 @@ void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList,
|
|||
* The first polygon is the main polygon, others are holes
|
||||
* See Draw3D_SolidHorizontalPolyPolygons for more info
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolygonWithHoles( const SHAPE_POLY_SET& aPolysList,
|
||||
void Draw3D_SolidHorizontalPolygonWithHoles( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness,
|
||||
double aBiuTo3DUnits, bool aUseTextures,
|
||||
float aNormal_Z_Orientation )
|
||||
{
|
||||
SHAPE_POLY_SET polygon( aPolysList );
|
||||
|
||||
polygon.Fracture();
|
||||
CPOLYGONS_LIST polygon;
|
||||
|
||||
ConvertPolysListWithHolesToOnePolygon( aPolysList, polygon );
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, aThickness, aBiuTo3DUnits, aUseTextures,
|
||||
aNormal_Z_Orientation );
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius,
|
|||
int aZpos, double aBiuTo3DUnits )
|
||||
{
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
SHAPE_POLY_SET outer_cornerBuffer;
|
||||
CPOLYGONS_LIST outer_cornerBuffer;
|
||||
|
||||
TransformCircleToPolygon( outer_cornerBuffer, aCenterPos,
|
||||
aRadius + (aThickness / 2), slice );
|
||||
|
@ -273,7 +273,7 @@ void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius,
|
|||
std::vector<S3D_VERTEX> coords;
|
||||
coords.resize( 4 );
|
||||
|
||||
SHAPE_POLY_SET inner_cornerBuffer;
|
||||
CPOLYGONS_LIST inner_cornerBuffer;
|
||||
if( aThickness ) // build the the vertical inner polygon (hole)
|
||||
TransformCircleToPolygon( inner_cornerBuffer, aCenterPos,
|
||||
aRadius - (aThickness / 2), slice );
|
||||
|
@ -294,11 +294,10 @@ void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius,
|
|||
if( aThickness )
|
||||
{
|
||||
// draw top (front) and bottom (back) horizontal sides (rings)
|
||||
outer_cornerBuffer.AddHole( inner_cornerBuffer.COutline( 0 ) );
|
||||
SHAPE_POLY_SET polygon = outer_cornerBuffer;
|
||||
|
||||
polygon.Fracture();
|
||||
outer_cornerBuffer.Append( inner_cornerBuffer );
|
||||
CPOLYGONS_LIST polygon;
|
||||
|
||||
ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );
|
||||
// draw top (front) horizontal ring
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits, false,
|
||||
1.0f );
|
||||
|
@ -327,7 +326,7 @@ void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
|
|||
const int slice = SEGM_PER_CIRCLE;
|
||||
|
||||
// Build the points to approximate oblong cylinder by segments
|
||||
SHAPE_POLY_SET outer_cornerBuffer;
|
||||
CPOLYGONS_LIST outer_cornerBuffer;
|
||||
|
||||
int segm_width = (aRadius * 2) + aThickness;
|
||||
TransformRoundedEndsSegmentToPolygon( outer_cornerBuffer, aAxis1Pos,
|
||||
|
@ -340,7 +339,7 @@ void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
|
|||
|
||||
if( aThickness )
|
||||
{
|
||||
SHAPE_POLY_SET inner_cornerBuffer;
|
||||
CPOLYGONS_LIST inner_cornerBuffer;
|
||||
segm_width = aRadius * 2;
|
||||
TransformRoundedEndsSegmentToPolygon( inner_cornerBuffer, aAxis1Pos,
|
||||
aAxis2Pos, slice, segm_width );
|
||||
|
@ -352,10 +351,10 @@ void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
|
|||
|
||||
// Build the horizontal full polygon shape
|
||||
// (outer polygon shape - inner polygon shape)
|
||||
outer_cornerBuffer.AddHole( inner_cornerBuffer.COutline( 0 ) );
|
||||
outer_cornerBuffer.Append( inner_cornerBuffer );
|
||||
|
||||
SHAPE_POLY_SET polygon( outer_cornerBuffer );
|
||||
polygon.Fracture();
|
||||
CPOLYGONS_LIST polygon;
|
||||
ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );
|
||||
|
||||
// draw top (front) horizontal side (ring)
|
||||
Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits, false,
|
||||
|
@ -380,8 +379,8 @@ void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
|
|||
void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
|
||||
int aWidth, int aThickness, int aZpos, double aBiuTo3DUnits )
|
||||
{
|
||||
SHAPE_POLY_SET cornerBuffer;
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
CPOLYGONS_LIST cornerBuffer;
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
|
||||
TransformRoundedEndsSegmentToPolygon( cornerBuffer, aStart, aEnd, slice, aWidth );
|
||||
|
||||
|
@ -395,7 +394,7 @@ void Draw3D_ArcSegment( const wxPoint& aCenterPos, const wxPoint& aStartPoint,
|
|||
{
|
||||
const int slice = SEGM_PER_CIRCLE;
|
||||
|
||||
SHAPE_POLY_SET cornerBuffer;
|
||||
CPOLYGONS_LIST cornerBuffer;
|
||||
TransformArcToPolygon( cornerBuffer, aCenterPos, aStartPoint, aArcAngle,
|
||||
slice, aWidth );
|
||||
|
||||
|
@ -422,7 +421,7 @@ void CALLBACK tessEndCB()
|
|||
void CALLBACK tessCPolyPt2Vertex( const GLvoid* data )
|
||||
{
|
||||
// cast back to double type
|
||||
const VECTOR2I* ptr = (const VECTOR2I*) data;
|
||||
const CPolyPt* ptr = (const CPolyPt*) data;
|
||||
|
||||
if( s_useTextures )
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
* The top side is located at aZpos + aThickness / 2
|
||||
* The bottom side is located at aZpos - aThickness / 2
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList,
|
||||
void Draw3D_SolidHorizontalPolyPolygons( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness, double aBiuTo3DUnits,
|
||||
bool aUseTextures,
|
||||
float aNormal_Z_Orientation );
|
||||
|
@ -61,7 +61,7 @@ void Draw3D_SolidHorizontalPolyPolygons( const SHAPE_POLY_SET& aPolysList,
|
|||
* The top side is located at aZpos + aThickness / 2
|
||||
* The bottom side is located at aZpos - aThickness / 2
|
||||
*/
|
||||
void Draw3D_SolidHorizontalPolygonWithHoles( const SHAPE_POLY_SET& aPolysList,
|
||||
void Draw3D_SolidHorizontalPolygonWithHoles( const CPOLYGONS_LIST& aPolysList,
|
||||
int aZpos, int aThickness, double aBiuTo3DUnits,
|
||||
bool aUseTextures,
|
||||
float aNormal_Z_Orientation );
|
||||
|
|
|
@ -57,8 +57,6 @@
|
|||
#include <trackball.h>
|
||||
#include <3d_draw_basic_functions.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <geometry/shape_file_io.h>
|
||||
|
||||
|
||||
#include <CImage.h>
|
||||
#include <reporter.h>
|
||||
|
@ -70,7 +68,6 @@
|
|||
*/
|
||||
GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer );
|
||||
|
||||
#if 0
|
||||
|
||||
// FIX ME: these 2 functions are fully duplicate of the same 2 functions in
|
||||
// pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp
|
||||
|
@ -91,7 +88,7 @@ static const SHAPE_POLY_SET convertPolyListToPolySet(const CPOLYGONS_LIST& aList
|
|||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
rv.Append( aList.GetX(ic), aList.GetY(ic) );
|
||||
rv.AppendVertex( aList.GetX(ic), aList.GetY(ic) );
|
||||
if( aList.IsEndContour( ic ) )
|
||||
break;
|
||||
|
||||
|
@ -131,7 +128,6 @@ static const CPOLYGONS_LIST convertPolySetToPolyList(const SHAPE_POLY_SET& aPoly
|
|||
return list;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
|
||||
REPORTER* aErrorMessages, REPORTER* aActivity )
|
||||
|
@ -160,11 +156,13 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
|
|||
// a fine representation
|
||||
double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) );
|
||||
|
||||
SHAPE_POLY_SET bufferPolys;
|
||||
SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines
|
||||
SHAPE_POLY_SET bufferZonesPolys;
|
||||
SHAPE_POLY_SET currLayerHoles, allLayerHoles; // Contains holes for the current layer
|
||||
// + zones when holes are removed from zones
|
||||
CPOLYGONS_LIST bufferPolys;
|
||||
bufferPolys.reserve( 500000 ); // Reserve for large board: tracks mainly
|
||||
// + zones when holes are removed from zones
|
||||
|
||||
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
|
||||
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
|
||||
allLayerHoles.reserve( 20000 );
|
||||
|
||||
// Build a polygon from edge cut items
|
||||
wxString msg;
|
||||
|
@ -181,7 +179,12 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
|
|||
}
|
||||
}
|
||||
|
||||
bool throughHolesListBuilt = false; // flag to build the through hole polygon list only once
|
||||
CPOLYGONS_LIST bufferZonesPolys;
|
||||
bufferZonesPolys.reserve( 300000 ); // Reserve for large board ( copper zones mainly )
|
||||
// when holes are not removed from zones
|
||||
|
||||
CPOLYGONS_LIST currLayerHoles; // Contains holes for the current layer
|
||||
bool throughHolesListBuilt = false; // flag to build the through hole polygon list only once
|
||||
|
||||
LSET cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount );
|
||||
|
||||
|
@ -348,18 +351,47 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
|
|||
|
||||
// bufferPolys contains polygons to merge. Many overlaps .
|
||||
// Calculate merged polygons
|
||||
if( bufferPolys.IsEmpty() )
|
||||
if( bufferPolys.GetCornersCount() == 0 )
|
||||
continue;
|
||||
|
||||
#if 0
|
||||
// Set to 1 to use boost::polygon to subtract holes to copper areas
|
||||
// (due to bugs in boost::polygon, this is deprecated and Clipper is used instead
|
||||
KI_POLYGON_SET currLayerPolyset;
|
||||
KI_POLYGON_SET polysetHoles;
|
||||
|
||||
// Add polygons, without holes
|
||||
bufferPolys.ExportTo( currLayerPolyset );
|
||||
|
||||
// Add through holes (created only once) in current polygon holes list
|
||||
currLayerHoles.Append( allLayerHoles );
|
||||
|
||||
if( currLayerHoles.GetCornersCount() > 0 )
|
||||
currLayerHoles.ExportTo( polysetHoles );
|
||||
|
||||
// Merge polygons, and remove holes
|
||||
currLayerPolyset -= polysetHoles;
|
||||
|
||||
bufferPolys.RemoveAllContours();
|
||||
bufferPolys.ImportFrom( currLayerPolyset );
|
||||
#else
|
||||
// Use Clipper lib to subtract holes to copper areas
|
||||
SHAPE_POLY_SET solidAreas = convertPolyListToPolySet( bufferPolys );
|
||||
solidAreas.Simplify();
|
||||
|
||||
bufferPolys.Simplify();
|
||||
currLayerHoles.Simplify();
|
||||
allLayerHoles.Simplify();
|
||||
|
||||
bufferPolys.BooleanSubtract( allLayerHoles );
|
||||
bufferPolys.Fracture();
|
||||
|
||||
// Add through holes (created only once) in current polygon holes list
|
||||
currLayerHoles.Append( allLayerHoles );
|
||||
if( currLayerHoles.GetCornersCount() > 0 )
|
||||
{
|
||||
SHAPE_POLY_SET holes = convertPolyListToPolySet( currLayerHoles );
|
||||
holes.Simplify();
|
||||
solidAreas.Subtract ( holes );
|
||||
}
|
||||
SHAPE_POLY_SET fractured = solidAreas;
|
||||
fractured.Fracture();
|
||||
bufferPolys.RemoveAllContours();
|
||||
bufferPolys = convertPolySetToPolyList( fractured );
|
||||
#endif
|
||||
int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
|
||||
int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );
|
||||
|
||||
|
@ -389,7 +421,7 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
|
|||
|
||||
// If holes are not removed from copper zones (for calculation time reasons,
|
||||
// the zone polygons are stored in bufferZonesPolys and have to be drawn now:
|
||||
if( !bufferZonesPolys.IsEmpty() )
|
||||
if( bufferZonesPolys.GetCornersCount() )
|
||||
{
|
||||
Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness,
|
||||
GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
|
||||
|
@ -458,10 +490,22 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
|
|||
zpos += (copper_thickness + epsilon) / 2.0f;
|
||||
board_thickness -= copper_thickness + epsilon;
|
||||
|
||||
bufferPcbOutlines.BooleanSubtract( allLayerHoles );
|
||||
bufferPcbOutlines.Fracture();
|
||||
KI_POLYGON_SET currLayerPolyset;
|
||||
KI_POLYGON_SET polysetHoles;
|
||||
|
||||
if( !bufferPcbOutlines.IsEmpty() )
|
||||
// Add polygons, without holes
|
||||
bufferPcbOutlines.ExportTo( currLayerPolyset );
|
||||
|
||||
// Build holes list
|
||||
allLayerHoles.ExportTo( polysetHoles );
|
||||
|
||||
// remove holes
|
||||
currLayerPolyset -= polysetHoles;
|
||||
|
||||
bufferPcbOutlines.RemoveAllContours();
|
||||
bufferPcbOutlines.ImportFrom( currLayerPolyset );
|
||||
|
||||
if( bufferPcbOutlines.GetCornersCount() )
|
||||
{
|
||||
Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0,
|
||||
board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
|
||||
|
@ -487,9 +531,12 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a
|
|||
|
||||
double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2) );
|
||||
|
||||
SHAPE_POLY_SET bufferPolys;
|
||||
SHAPE_POLY_SET allLayerHoles; // Contains through holes, calculated only once
|
||||
SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines
|
||||
CPOLYGONS_LIST bufferPolys;
|
||||
bufferPolys.reserve( 100000 ); // Reserve for large board
|
||||
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
|
||||
allLayerHoles.reserve( 20000 );
|
||||
|
||||
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
|
||||
|
||||
// Build a polygon from edge cut items
|
||||
wxString msg;
|
||||
|
@ -534,6 +581,9 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a
|
|||
|
||||
// draw graphic items, on technical layers
|
||||
|
||||
KI_POLYGON_SET brdpolysetHoles;
|
||||
allLayerHoles.ExportTo( brdpolysetHoles );
|
||||
|
||||
static const LAYER_ID teckLayerList[] = {
|
||||
B_Adhes,
|
||||
F_Adhes,
|
||||
|
@ -625,33 +675,32 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a
|
|||
|
||||
// bufferPolys contains polygons to merge. Many overlaps .
|
||||
// Calculate merged polygons and remove pads and vias holes
|
||||
if( bufferPolys.IsEmpty() )
|
||||
if( bufferPolys.GetCornersCount() == 0 )
|
||||
continue;
|
||||
|
||||
allLayerHoles.Simplify();
|
||||
KI_POLYGON_SET currLayerPolyset;
|
||||
KI_POLYGON_SET polyset;
|
||||
|
||||
// Solder mask layers are "negative" layers.
|
||||
// Shapes should be removed from the full board area.
|
||||
if( layer == B_Mask || layer == F_Mask )
|
||||
{
|
||||
SHAPE_POLY_SET cuts = bufferPolys;
|
||||
bufferPolys = bufferPcbOutlines;
|
||||
|
||||
cuts.Append(allLayerHoles);
|
||||
cuts.Simplify();
|
||||
|
||||
bufferPolys.BooleanSubtract( cuts );
|
||||
bufferPcbOutlines.ExportTo( currLayerPolyset );
|
||||
bufferPolys.Append( allLayerHoles );
|
||||
bufferPolys.ExportTo( polyset );
|
||||
currLayerPolyset -= polyset;
|
||||
}
|
||||
// Remove holes from Solder paste layers and siklscreen
|
||||
else if( layer == B_Paste || layer == F_Paste
|
||||
|| layer == B_SilkS || layer == F_SilkS )
|
||||
{
|
||||
|
||||
bufferPolys.BooleanSubtract( allLayerHoles );
|
||||
bufferPolys.ExportTo( currLayerPolyset );
|
||||
currLayerPolyset -= brdpolysetHoles;
|
||||
}
|
||||
else // usuall layers, merge polys built from each item shape:
|
||||
{
|
||||
bufferPolys.ExportTo( polyset );
|
||||
currLayerPolyset += polyset;
|
||||
}
|
||||
|
||||
bufferPolys.Fracture();
|
||||
|
||||
|
||||
int thickness = 0;
|
||||
|
||||
|
@ -679,6 +728,8 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a
|
|||
zpos -= thickness/2 ;
|
||||
}
|
||||
|
||||
bufferPolys.RemoveAllContours();
|
||||
bufferPolys.ImportFrom( currLayerPolyset );
|
||||
|
||||
float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)
|
||||
|
||||
|
|
|
@ -358,7 +358,7 @@ void EDA_3D_CANVAS::draw3DPadHole( const D_PAD* aPad )
|
|||
return;
|
||||
|
||||
// Store here the points to approximate hole by segments
|
||||
SHAPE_POLY_SET holecornersBuffer;
|
||||
CPOLYGONS_LIST holecornersBuffer;
|
||||
int thickness = GetPrm3DVisu().GetCopperThicknessBIU();
|
||||
int height = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu ) -
|
||||
GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
|
||||
|
@ -436,7 +436,7 @@ void EDA_3D_CANVAS::draw3DViaHole( const VIA* aVia )
|
|||
* Used only to draw pads outlines on silkscreen layers.
|
||||
*/
|
||||
void EDA_3D_CANVAS::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aWidth,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor )
|
||||
|
@ -449,22 +449,17 @@ void EDA_3D_CANVAS::buildPadShapeThickOutlineAsPolygon( const D_PAD* aPad,
|
|||
}
|
||||
|
||||
// For other shapes, draw polygon outlines
|
||||
SHAPE_POLY_SET corners;
|
||||
CPOLYGONS_LIST corners;
|
||||
aPad->BuildPadShapePolygon( corners, wxSize( 0, 0 ),
|
||||
aCircleToSegmentsCount, aCorrectionFactor );
|
||||
|
||||
// Add outlines as thick segments in polygon buffer
|
||||
|
||||
const SHAPE_LINE_CHAIN& path = corners.COutline( 0 );
|
||||
|
||||
for( int ii = 0; ii < path.PointCount(); ii++ )
|
||||
for( unsigned ii = 0, jj = corners.GetCornersCount() - 1;
|
||||
ii < corners.GetCornersCount(); jj = ii, ii++ )
|
||||
{
|
||||
const VECTOR2I& a = path.CPoint( ii );
|
||||
const VECTOR2I& b = path.CPoint( ii + 1 );
|
||||
|
||||
TransformRoundedEndsSegmentToPolygon( aCornerBuffer,
|
||||
wxPoint( a.x, a.y ),
|
||||
wxPoint( b.x, b.y ),
|
||||
corners.GetPos( jj ),
|
||||
corners.GetPos( ii ),
|
||||
aCircleToSegmentsCount, aWidth );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -346,7 +346,8 @@ void DXF_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, int
|
|||
* It does not know thhick segments, therefore filled polygons with thick outline
|
||||
* are converted to inflated polygon by aWidth/2
|
||||
*/
|
||||
void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
|
||||
#include "clipper.hpp"
|
||||
void DXF_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList,
|
||||
FILL_T aFill, int aWidth)
|
||||
{
|
||||
if( aCornerList.size() <= 1 )
|
||||
|
@ -391,12 +392,10 @@ void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
|
|||
// The polygon outline has thickness, and is filled
|
||||
// Build and plot the polygon which contains the initial
|
||||
// polygon and its thick outline
|
||||
SHAPE_POLY_SET bufferOutline;
|
||||
SHAPE_POLY_SET bufferPolybase;
|
||||
CPOLYGONS_LIST bufferOutline;
|
||||
CPOLYGONS_LIST bufferPolybase;
|
||||
const int circleToSegmentsCount = 16;
|
||||
|
||||
bufferPolybase.NewOutline();
|
||||
|
||||
// enter outline as polygon:
|
||||
for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
|
||||
{
|
||||
|
@ -407,40 +406,47 @@ void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
|
|||
// enter the initial polygon:
|
||||
for( unsigned ii = 0; ii < aCornerList.size(); ii++ )
|
||||
{
|
||||
bufferPolybase.Append( aCornerList[ii] );
|
||||
CPolyPt polypoint( aCornerList[ii].x, aCornerList[ii].y );
|
||||
bufferPolybase.Append( polypoint );
|
||||
}
|
||||
|
||||
bufferPolybase.CloseLastContour();
|
||||
|
||||
// Merge polygons to build the polygon which contains the initial
|
||||
// polygon and its thick outline
|
||||
KI_POLYGON_SET polysBase; // Store the main outline and the final outline
|
||||
KI_POLYGON_SET polysOutline; // Store the thick segments to draw the outline
|
||||
bufferPolybase.ExportTo( polysBase );
|
||||
bufferOutline.ExportTo( polysOutline );
|
||||
|
||||
bufferPolybase.BooleanAdd( bufferOutline ); // create the outline which contains thick outline
|
||||
bufferPolybase.Fracture();
|
||||
polysBase += polysOutline; // create the outline which contains thick outline
|
||||
|
||||
// We should have only one polygon in list, now.
|
||||
wxASSERT( polysBase.size() == 1 );
|
||||
|
||||
if( bufferPolybase.OutlineCount() < 1 ) // should not happen
|
||||
if( polysBase.size() < 1 ) // should not happen
|
||||
return;
|
||||
|
||||
const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );
|
||||
KI_POLYGON poly = polysBase[0]; // Expected only one polygon here
|
||||
|
||||
if( path.PointCount() < 2 ) // should not happen
|
||||
if( poly.size() < 2 ) // should not happen
|
||||
return;
|
||||
|
||||
// Now, output the final polygon to DXF file:
|
||||
last = path.PointCount() - 1;
|
||||
VECTOR2I point = path.CPoint( 0 );
|
||||
|
||||
wxPoint startPoint( point.x, point.y );
|
||||
last = poly.size() - 1;
|
||||
KI_POLY_POINT point = *(poly.begin());
|
||||
wxPoint startPoint( point.x(), point.y() );
|
||||
MoveTo( startPoint );
|
||||
|
||||
for( int ii = 1; ii < path.PointCount(); ii++ )
|
||||
for( unsigned ii = 1; ii < poly.size(); ii++ )
|
||||
{
|
||||
point = path.CPoint( ii );
|
||||
LineTo( wxPoint( point.x, point.y ) );
|
||||
point = *( poly.begin() + ii );
|
||||
LineTo( wxPoint( point.x(), point.y() ) );
|
||||
}
|
||||
|
||||
// Close polygon, if needed
|
||||
point = path.CPoint( last );
|
||||
wxPoint endPoint( point.x, point.y );
|
||||
point = *(poly.begin() + last);
|
||||
wxPoint endPoint( point.x(), point.y() );
|
||||
|
||||
if( endPoint != startPoint )
|
||||
LineTo( startPoint );
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
* Note: the polygon is inside the circle, so if you want to have the polygon
|
||||
* outside the circle, you should give aRadius calculated with a corrrection factor
|
||||
*/
|
||||
void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformCircleToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aCenter, int aRadius,
|
||||
int aCircleToSegmentsCount )
|
||||
{
|
||||
|
@ -51,8 +51,6 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
int delta = 3600 / aCircleToSegmentsCount; // rot angle in 0.1 degree
|
||||
int halfstep = 1800 / aCircleToSegmentsCount; // the starting value for rot angles
|
||||
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( int ii = 0; ii < aCircleToSegmentsCount; ii++ )
|
||||
{
|
||||
corner_position.x = aRadius;
|
||||
|
@ -60,8 +58,11 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
int angle = (ii * delta) + halfstep;
|
||||
RotatePoint( &corner_position.x, &corner_position.y, angle );
|
||||
corner_position += aCenter;
|
||||
aCornerBuffer.Append( corner_position.x, corner_position.y );
|
||||
CPolyPt polypoint( corner_position.x, corner_position.y );
|
||||
aCornerBuffer.Append( polypoint );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,7 +78,7 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* Note: the polygon is inside the arc ends, so if you want to have the polygon
|
||||
* outside the circle, you should give aStart and aEnd calculated with a correction factor
|
||||
*/
|
||||
void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformRoundedEndsSegmentToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aStart, wxPoint aEnd,
|
||||
int aCircleToSegmentsCount,
|
||||
int aWidth )
|
||||
|
@ -86,9 +87,7 @@ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
wxPoint endp = aEnd - aStart; // end point coordinate for the same segment starting at (0,0)
|
||||
wxPoint startp = aStart;
|
||||
wxPoint corner;
|
||||
VECTOR2I polypoint;
|
||||
|
||||
aCornerBuffer.NewOutline();
|
||||
CPolyPt polypoint;
|
||||
|
||||
// normalize the position in order to have endp.x >= 0;
|
||||
if( endp.x < 0 )
|
||||
|
@ -113,7 +112,7 @@ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
corner += startp;
|
||||
polypoint.x = corner.x;
|
||||
polypoint.y = corner.y;
|
||||
aCornerBuffer.Append( polypoint.x, polypoint.y );
|
||||
aCornerBuffer.Append( polypoint );
|
||||
}
|
||||
|
||||
// Finish arc:
|
||||
|
@ -122,7 +121,7 @@ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
corner += startp;
|
||||
polypoint.x = corner.x;
|
||||
polypoint.y = corner.y;
|
||||
aCornerBuffer.Append( polypoint.x, polypoint.y );
|
||||
aCornerBuffer.Append( polypoint );
|
||||
|
||||
// add left rounded end:
|
||||
for( int ii = 0; ii < 1800; ii += delta )
|
||||
|
@ -133,7 +132,7 @@ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
corner += startp;
|
||||
polypoint.x = corner.x;
|
||||
polypoint.y = corner.y;
|
||||
aCornerBuffer.Append( polypoint.x, polypoint.y );
|
||||
aCornerBuffer.Append( polypoint );
|
||||
}
|
||||
|
||||
// Finish arc:
|
||||
|
@ -142,7 +141,9 @@ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
corner += startp;
|
||||
polypoint.x = corner.x;
|
||||
polypoint.y = corner.y;
|
||||
aCornerBuffer.Append( polypoint.x, polypoint.y );
|
||||
aCornerBuffer.Append( polypoint );
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
}
|
||||
|
||||
|
||||
|
@ -157,7 +158,7 @@ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* @param aCircleToSegmentsCount = the number of segments to approximate a circle
|
||||
* @param aWidth = width (thickness) of the line
|
||||
*/
|
||||
void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformArcToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aCentre, wxPoint aStart, double aArcAngle,
|
||||
int aCircleToSegmentsCount, int aWidth )
|
||||
{
|
||||
|
@ -207,7 +208,7 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* @param aCircleToSegmentsCount = the number of segments to approximate a circle
|
||||
* @param aWidth = width (thickness) of the ring
|
||||
*/
|
||||
void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformRingToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aCentre, int aRadius,
|
||||
int aCircleToSegmentsCount, int aWidth )
|
||||
{
|
||||
|
@ -217,8 +218,7 @@ void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
wxPoint curr_point;
|
||||
int inner_radius = aRadius - ( aWidth / 2 );
|
||||
int outer_radius = inner_radius + aWidth;
|
||||
|
||||
aCornerBuffer.NewOutline();
|
||||
CPolyPt polycorner;
|
||||
|
||||
// Draw the inner circle of the ring
|
||||
for( int ii = 0; ii < 3600; ii += delta )
|
||||
|
@ -227,11 +227,15 @@ void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
curr_point.y = 0;
|
||||
RotatePoint( &curr_point, ii );
|
||||
curr_point += aCentre;
|
||||
aCornerBuffer.Append( curr_point.x, curr_point.y );
|
||||
polycorner.x = curr_point.x;
|
||||
polycorner.y = curr_point.y;
|
||||
aCornerBuffer.Append( polycorner );
|
||||
}
|
||||
|
||||
// Draw the last point of inner circle
|
||||
aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y );
|
||||
polycorner.x = aCentre.x + inner_radius;
|
||||
polycorner.y = aCentre.y;
|
||||
aCornerBuffer.Append( polycorner );
|
||||
|
||||
// Draw the outer circle of the ring
|
||||
for( int ii = 0; ii < 3600; ii += delta )
|
||||
|
@ -240,10 +244,18 @@ void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
curr_point.y = 0;
|
||||
RotatePoint( &curr_point, -ii );
|
||||
curr_point += aCentre;
|
||||
aCornerBuffer.Append( curr_point.x, curr_point.y );
|
||||
polycorner.x = curr_point.x;
|
||||
polycorner.y = curr_point.y;
|
||||
aCornerBuffer.Append( polycorner );
|
||||
}
|
||||
|
||||
// Draw the last point of outer circle
|
||||
aCornerBuffer.Append( aCentre.x + outer_radius, aCentre.y );
|
||||
aCornerBuffer.Append( aCentre.x + inner_radius, aCentre.y );
|
||||
polycorner.x = aCentre.x + outer_radius;
|
||||
polycorner.y = aCentre.y;
|
||||
aCornerBuffer.Append( polycorner );
|
||||
|
||||
// Close the polygon
|
||||
polycorner.x = aCentre.x + inner_radius;
|
||||
polycorner.end_contour = true;
|
||||
aCornerBuffer.Append( polycorner );
|
||||
}
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
* Copyright (C) 2015 CERN
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* Point in polygon algorithm adapted from Clipper Library (C) Angus Johnson,
|
||||
* subject to Clipper library license.
|
||||
*
|
||||
* 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
|
||||
|
@ -28,34 +25,22 @@
|
|||
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include "geometry/shape_poly_set.h"
|
||||
|
||||
using namespace ClipperLib;
|
||||
|
||||
SHAPE_POLY_SET::SHAPE_POLY_SET() :
|
||||
SHAPE( SH_POLY_SET )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::~SHAPE_POLY_SET()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::NewOutline()
|
||||
{
|
||||
SHAPE_LINE_CHAIN empty_path;
|
||||
POLYGON poly;
|
||||
Path empty_path;
|
||||
Paths poly;
|
||||
poly.push_back( empty_path );
|
||||
m_polys.push_back( poly );
|
||||
return m_polys.size() - 1;
|
||||
|
@ -64,13 +49,12 @@ int SHAPE_POLY_SET::NewOutline()
|
|||
|
||||
int SHAPE_POLY_SET::NewHole( int aOutline )
|
||||
{
|
||||
m_polys.back().push_back( SHAPE_LINE_CHAIN() );
|
||||
|
||||
return m_polys.back().size() - 2;
|
||||
assert( false );
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole )
|
||||
int SHAPE_POLY_SET::AppendVertex( int x, int y, int aOutline, int aHole )
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
@ -85,13 +69,13 @@ int SHAPE_POLY_SET::Append( int x, int y, int aOutline, int aHole )
|
|||
assert( aOutline < (int)m_polys.size() );
|
||||
assert( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
m_polys[aOutline][idx].Append( x, y );
|
||||
m_polys[aOutline][idx].push_back( IntPoint( x, y ) );
|
||||
|
||||
return m_polys[aOutline][idx].PointCount();
|
||||
return m_polys[aOutline][idx].size();
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const
|
||||
int SHAPE_POLY_SET::VertexCount( int aOutline, int aHole ) const
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
@ -106,11 +90,11 @@ int SHAPE_POLY_SET::VertexCount( int aOutline , int aHole ) const
|
|||
assert ( aOutline < (int)m_polys.size() );
|
||||
assert ( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
return m_polys[aOutline][idx].PointCount();
|
||||
return m_polys[aOutline][idx].size();
|
||||
}
|
||||
|
||||
|
||||
const VECTOR2I& SHAPE_POLY_SET::CVertex( int index, int aOutline , int aHole ) const
|
||||
const VECTOR2I SHAPE_POLY_SET::GetVertex( int index, int aOutline, int aHole ) const
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
@ -125,26 +109,8 @@ const VECTOR2I& SHAPE_POLY_SET::CVertex( int index, int aOutline , int aHole ) c
|
|||
assert( aOutline < (int)m_polys.size() );
|
||||
assert( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
return m_polys[aOutline][idx].CPoint( index );
|
||||
}
|
||||
|
||||
|
||||
VECTOR2I& SHAPE_POLY_SET::Vertex( int index, int aOutline , int aHole )
|
||||
{
|
||||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
||||
int idx;
|
||||
|
||||
if( aHole < 0 )
|
||||
idx = 0;
|
||||
else
|
||||
idx = aHole + 1;
|
||||
|
||||
assert( aOutline < (int)m_polys.size() );
|
||||
assert( idx < (int)m_polys[aOutline].size() );
|
||||
|
||||
return m_polys[aOutline][idx].Point( index );
|
||||
IntPoint p = m_polys[aOutline][idx][index];
|
||||
return VECTOR2I (p.X, p.Y);
|
||||
}
|
||||
|
||||
|
||||
|
@ -152,9 +118,13 @@ int SHAPE_POLY_SET::AddOutline( const SHAPE_LINE_CHAIN& aOutline )
|
|||
{
|
||||
assert( aOutline.IsClosed() );
|
||||
|
||||
POLYGON poly;
|
||||
Path p = convert( aOutline );
|
||||
Paths poly;
|
||||
|
||||
poly.push_back( aOutline );
|
||||
if( !Orientation( p ) )
|
||||
ReversePath( p ); // outlines are always CW
|
||||
|
||||
poly.push_back( p );
|
||||
|
||||
m_polys.push_back( poly );
|
||||
|
||||
|
@ -169,60 +139,49 @@ int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
|
|||
if( aOutline < 0 )
|
||||
aOutline += m_polys.size();
|
||||
|
||||
POLYGON& poly = m_polys[aOutline];
|
||||
Paths& poly = m_polys[aOutline];
|
||||
|
||||
assert( poly.size() );
|
||||
|
||||
poly.push_back( aHole );
|
||||
Path p = convert( aHole );
|
||||
|
||||
if( Orientation( p ) )
|
||||
ReversePath( p ); // holes are always CCW
|
||||
|
||||
poly.push_back( p );
|
||||
|
||||
return poly.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation )
|
||||
const ClipperLib::Path SHAPE_POLY_SET::convert( const SHAPE_LINE_CHAIN& aPath )
|
||||
{
|
||||
Path c_path;
|
||||
|
||||
for( int i = 0; i < aPath.PointCount(); i++ )
|
||||
{
|
||||
const VECTOR2I& vertex = aPath.CPoint( i );
|
||||
c_path.push_back( IntPoint( vertex.x, vertex.y ) );
|
||||
c_path.push_back( ClipperLib::IntPoint( vertex.x, vertex.y ) );
|
||||
}
|
||||
|
||||
if( Orientation( c_path ) != aRequiredOrientation )
|
||||
ReversePath( c_path );
|
||||
|
||||
return c_path;
|
||||
}
|
||||
|
||||
|
||||
const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath )
|
||||
{
|
||||
SHAPE_LINE_CHAIN lc;
|
||||
|
||||
for( unsigned int i = 0; i < aPath.size(); i++ )
|
||||
lc.Append( aPath[i].X, aPath[i].Y );
|
||||
|
||||
return lc;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::booleanOp( ClipType type, const SHAPE_POLY_SET& b )
|
||||
void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b )
|
||||
{
|
||||
Clipper c;
|
||||
|
||||
c.StrictlySimple( true );
|
||||
|
||||
BOOST_FOREACH( const POLYGON& poly, m_polys )
|
||||
BOOST_FOREACH( Paths& subject, m_polys )
|
||||
{
|
||||
for( unsigned int i = 0; i < poly.size(); i++ )
|
||||
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptSubject, true );
|
||||
c.AddPaths( subject, ptSubject, true );
|
||||
}
|
||||
|
||||
BOOST_FOREACH( const POLYGON& poly, b.m_polys )
|
||||
BOOST_FOREACH( const Paths& clip, b.m_polys )
|
||||
{
|
||||
for( unsigned int i = 0; i < poly.size(); i++ )
|
||||
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptClip, true );
|
||||
c.AddPaths( clip, ptClip, true );
|
||||
}
|
||||
|
||||
PolyTree solution;
|
||||
|
@ -233,39 +192,41 @@ void SHAPE_POLY_SET::booleanOp( ClipType type, const SHAPE_POLY_SET& b )
|
|||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& b )
|
||||
void SHAPE_POLY_SET::Add( const SHAPE_POLY_SET& b )
|
||||
{
|
||||
booleanOp( ctUnion, b );
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& b )
|
||||
void SHAPE_POLY_SET::Subtract( const SHAPE_POLY_SET& b )
|
||||
{
|
||||
booleanOp( ctDifference, b );
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount )
|
||||
void SHAPE_POLY_SET::Erode( int aFactor )
|
||||
{
|
||||
ClipperOffset c;
|
||||
|
||||
BOOST_FOREACH( const POLYGON& poly, m_polys )
|
||||
{
|
||||
for( unsigned int i = 0; i < poly.size(); i++ )
|
||||
c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), jtRound, etClosedPolygon );
|
||||
}
|
||||
BOOST_FOREACH( Paths& p, m_polys )
|
||||
c.AddPaths(p, jtRound, etClosedPolygon );
|
||||
|
||||
PolyTree solution;
|
||||
|
||||
c.ArcTolerance = (double)fabs( aFactor ) / M_PI / aCircleSegmentsCount;
|
||||
|
||||
c.Execute( solution, aFactor );
|
||||
|
||||
importTree( &solution );
|
||||
m_polys.clear();
|
||||
|
||||
for( PolyNode* n = solution.GetFirst(); n; n = n->GetNext() )
|
||||
{
|
||||
Paths ps;
|
||||
ps.push_back( n->Contour );
|
||||
m_polys.push_back( ps );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::importTree( PolyTree* tree)
|
||||
void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree )
|
||||
{
|
||||
m_polys.clear();
|
||||
|
||||
|
@ -273,37 +234,37 @@ void SHAPE_POLY_SET::importTree( PolyTree* tree)
|
|||
{
|
||||
if( !n->IsHole() )
|
||||
{
|
||||
POLYGON paths;
|
||||
paths.push_back( convertFromClipper( n->Contour ) );
|
||||
Paths paths;
|
||||
paths.push_back( n->Contour );
|
||||
|
||||
for( unsigned int i = 0; i < n->Childs.size(); i++ )
|
||||
paths.push_back( convertFromClipper( n->Childs[i]->Contour ) );
|
||||
for( unsigned i = 0; i < n->Childs.size(); i++ )
|
||||
paths.push_back( n->Childs[i]->Contour );
|
||||
|
||||
m_polys.push_back(paths);
|
||||
m_polys.push_back( paths );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Polygon fracturing code. Work in progress.
|
||||
|
||||
// Polygon fracturing code. Work in progress.
|
||||
struct FractureEdge
|
||||
{
|
||||
FractureEdge( bool connected, SHAPE_LINE_CHAIN* owner, int index ) :
|
||||
FractureEdge( bool connected, Path* owner, int index ) :
|
||||
m_connected( connected ),
|
||||
m_next( NULL )
|
||||
{
|
||||
m_p1 = owner->CPoint( index );
|
||||
m_p2 = owner->CPoint( index + 1 );
|
||||
m_p1 = (*owner)[index];
|
||||
m_p2 = (*owner)[(index + 1) % owner->size()];
|
||||
}
|
||||
|
||||
FractureEdge( int y = 0 ) :
|
||||
FractureEdge( int64_t y = 0 ) :
|
||||
m_connected( false ),
|
||||
m_next( NULL )
|
||||
{
|
||||
m_p1.x = m_p2.y = y;
|
||||
m_p1.Y = m_p2.Y = y;
|
||||
}
|
||||
|
||||
FractureEdge( bool connected, const VECTOR2I& p1, const VECTOR2I& p2 ) :
|
||||
FractureEdge( bool connected, const IntPoint& p1, const IntPoint& p2 ) :
|
||||
m_connected( connected ),
|
||||
m_p1( p1 ),
|
||||
m_p2( p2 ),
|
||||
|
@ -313,14 +274,14 @@ struct FractureEdge
|
|||
|
||||
bool matches( int y ) const
|
||||
{
|
||||
int y_min = std::min( m_p1.y, m_p2.y );
|
||||
int y_max = std::max( m_p1.y, m_p2.y );
|
||||
int y_min = std::min( m_p1.Y, m_p2.Y );
|
||||
int y_max = std::max( m_p1.Y, m_p2.Y );
|
||||
|
||||
return ( y >= y_min ) && ( y <= y_max );
|
||||
}
|
||||
|
||||
bool m_connected;
|
||||
VECTOR2I m_p1, m_p2;
|
||||
IntPoint m_p1, m_p2;
|
||||
FractureEdge* m_next;
|
||||
};
|
||||
|
||||
|
@ -329,10 +290,10 @@ typedef std::vector<FractureEdge*> FractureEdgeSet;
|
|||
|
||||
static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
||||
{
|
||||
int x = edge->m_p1.x;
|
||||
int y = edge->m_p1.y;
|
||||
int min_dist = std::numeric_limits<int>::max();
|
||||
int x_nearest = 0;
|
||||
int64_t x = edge->m_p1.X;
|
||||
int64_t y = edge->m_p1.Y;
|
||||
int64_t min_dist = std::numeric_limits<int64_t>::max();
|
||||
int64_t x_nearest = 0;
|
||||
|
||||
FractureEdge* e_nearest = NULL;
|
||||
|
||||
|
@ -341,14 +302,15 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
|||
if( !(*i)->matches( y ) )
|
||||
continue;
|
||||
|
||||
int x_intersect;
|
||||
int64_t x_intersect;
|
||||
|
||||
if( (*i)->m_p1.y == (*i)->m_p2.y ) // horizontal edge
|
||||
x_intersect = std::max ( (*i)->m_p1.x, (*i)->m_p2.x );
|
||||
if( (*i)->m_p1.Y == (*i)->m_p2.Y ) // horizontal edge
|
||||
x_intersect = std::max( (*i)->m_p1.X, (*i)->m_p2.X );
|
||||
else
|
||||
x_intersect = (*i)->m_p1.x + rescale((*i)->m_p2.x - (*i)->m_p1.x, y - (*i)->m_p1.y, (*i)->m_p2.y - (*i)->m_p1.y );
|
||||
x_intersect = (*i)->m_p1.X + rescale((*i)->m_p2.X - (*i)->m_p1.X,
|
||||
y - (*i)->m_p1.Y, (*i)->m_p2.Y - (*i)->m_p1.Y );
|
||||
|
||||
int dist = ( x - x_intersect );
|
||||
int64_t dist = ( x - x_intersect );
|
||||
|
||||
if( dist > 0 && dist < min_dist )
|
||||
{
|
||||
|
@ -362,9 +324,9 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
|||
{
|
||||
int count = 0;
|
||||
|
||||
FractureEdge* lead1 = new FractureEdge( true, VECTOR2I( x_nearest, y ), VECTOR2I( x, y ) );
|
||||
FractureEdge* lead2 = new FractureEdge( true, VECTOR2I( x, y ), VECTOR2I( x_nearest, y ) );
|
||||
FractureEdge* split_2 = new FractureEdge( true, VECTOR2I( x_nearest, y ), e_nearest->m_p2 );
|
||||
FractureEdge* lead1 = new FractureEdge( true, IntPoint( x_nearest, y), IntPoint( x, y ) );
|
||||
FractureEdge* lead2 = new FractureEdge( true, IntPoint( x, y), IntPoint( x_nearest, y ) );
|
||||
FractureEdge* split_2 = new FractureEdge( true, IntPoint( x_nearest, y ), e_nearest->m_p2 );
|
||||
|
||||
edges.push_back( split_2 );
|
||||
edges.push_back( lead1 );
|
||||
|
@ -372,11 +334,11 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
|||
|
||||
FractureEdge* link = e_nearest->m_next;
|
||||
|
||||
e_nearest->m_p2 = VECTOR2I( x_nearest, y );
|
||||
e_nearest->m_p2 = IntPoint( x_nearest, y );
|
||||
e_nearest->m_next = lead1;
|
||||
lead1->m_next = edge;
|
||||
FractureEdge* last;
|
||||
|
||||
FractureEdge*last;
|
||||
for( last = edge; last->m_next != edge; last = last->m_next )
|
||||
{
|
||||
last->m_connected = true;
|
||||
|
@ -394,7 +356,8 @@ static int processEdge( FractureEdgeSet& edges, FractureEdge* edge )
|
|||
return 0;
|
||||
}
|
||||
|
||||
void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
||||
|
||||
void SHAPE_POLY_SET::fractureSingle( ClipperLib::Paths& paths )
|
||||
{
|
||||
FractureEdgeSet edges;
|
||||
FractureEdgeSet border_edges;
|
||||
|
@ -407,23 +370,21 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
|||
|
||||
int num_unconnected = 0;
|
||||
|
||||
BOOST_FOREACH( SHAPE_LINE_CHAIN& path, paths )
|
||||
BOOST_FOREACH( Path& path, paths )
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
FractureEdge *prev = NULL, *first_edge = NULL;
|
||||
|
||||
int x_min = std::numeric_limits<int>::max();
|
||||
int64_t x_min = std::numeric_limits<int64_t>::max();
|
||||
|
||||
for( int i = 0; i < path.PointCount(); i++ )
|
||||
for( unsigned i = 0; i < path.size(); i++ )
|
||||
{
|
||||
const VECTOR2I& p = path.CPoint( i );
|
||||
|
||||
if( p.x < x_min )
|
||||
x_min = p.x;
|
||||
if( path[i].X < x_min )
|
||||
x_min = path[i].X;
|
||||
}
|
||||
|
||||
for( int i = 0; i < path.PointCount(); i++ )
|
||||
for( unsigned i = 0; i < path.size(); i++ )
|
||||
{
|
||||
FractureEdge* fe = new FractureEdge( first, &path, index++ );
|
||||
|
||||
|
@ -436,7 +397,7 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
|||
if( prev )
|
||||
prev->m_next = fe;
|
||||
|
||||
if( i == path.PointCount() - 1 )
|
||||
if( i == path.size() - 1 )
|
||||
fe->m_next = first_edge;
|
||||
|
||||
prev = fe;
|
||||
|
@ -444,27 +405,27 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
|||
|
||||
if( !first )
|
||||
{
|
||||
if( fe->m_p1.x == x_min )
|
||||
if( fe->m_p1.X == x_min )
|
||||
border_edges.push_back( fe );
|
||||
}
|
||||
|
||||
if( !fe->m_connected )
|
||||
num_unconnected++;
|
||||
}
|
||||
|
||||
first = false; // first path is always the outline
|
||||
}
|
||||
|
||||
// keep connecting holes to the main outline, until there's no holes left...
|
||||
while( num_unconnected > 0 )
|
||||
{
|
||||
int x_min = std::numeric_limits<int>::max();
|
||||
|
||||
int64_t x_min = std::numeric_limits<int64_t>::max();
|
||||
FractureEdge* smallestX = NULL;
|
||||
|
||||
// find the left-most hole edge and merge with the outline
|
||||
for( FractureEdgeSet::iterator i = border_edges.begin(); i != border_edges.end(); ++i )
|
||||
{
|
||||
int xt = (*i)->m_p1.x;
|
||||
int64_t xt = (*i)->m_p1.X;
|
||||
|
||||
if( ( xt < x_min ) && ! (*i)->m_connected )
|
||||
{
|
||||
|
@ -477,16 +438,13 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
|||
}
|
||||
|
||||
paths.clear();
|
||||
SHAPE_LINE_CHAIN newPath;
|
||||
|
||||
newPath.SetClosed( true );
|
||||
|
||||
Path newPath;
|
||||
FractureEdge* e;
|
||||
|
||||
for( e = root; e->m_next != root; e = e->m_next )
|
||||
newPath.Append( e->m_p1 );
|
||||
newPath.push_back( e->m_p1 );
|
||||
|
||||
newPath.Append( e->m_p1 );
|
||||
newPath.push_back( e->m_p1 );
|
||||
|
||||
for( FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i )
|
||||
delete *i;
|
||||
|
@ -497,9 +455,7 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths )
|
|||
|
||||
void SHAPE_POLY_SET::Fracture()
|
||||
{
|
||||
Simplify(); // remove overlallping holes/degeneracy
|
||||
|
||||
BOOST_FOREACH( POLYGON& paths, m_polys )
|
||||
BOOST_FOREACH( Paths& paths, m_polys )
|
||||
{
|
||||
fractureSingle( paths );
|
||||
}
|
||||
|
@ -508,9 +464,12 @@ void SHAPE_POLY_SET::Fracture()
|
|||
|
||||
void SHAPE_POLY_SET::Simplify()
|
||||
{
|
||||
SHAPE_POLY_SET empty;
|
||||
|
||||
booleanOp( ctUnion, empty );
|
||||
for( unsigned i = 0; i < m_polys.size(); i++ )
|
||||
{
|
||||
Paths out;
|
||||
SimplifyPolygons( m_polys[i], out, pftNonZero );
|
||||
m_polys[i] = out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -523,11 +482,13 @@ const std::string SHAPE_POLY_SET::Format() const
|
|||
for( unsigned i = 0; i < m_polys.size(); i++ )
|
||||
{
|
||||
ss << "poly " << m_polys[i].size() << "\n";
|
||||
|
||||
for( unsigned j = 0; j < m_polys[i].size(); j++)
|
||||
{
|
||||
ss << m_polys[i][j].PointCount() << "\n";
|
||||
for( int v = 0; v < m_polys[i][j].PointCount(); v++)
|
||||
ss << m_polys[i][j].CPoint( v ).x << " " << m_polys[i][j].CPoint( v ).y << "\n";
|
||||
ss << m_polys[i][j].size() << "\n";
|
||||
|
||||
for( unsigned v = 0; v < m_polys[i][j].size(); v++)
|
||||
ss << m_polys[i][j][v].X << " " << m_polys[i][j][v].Y << "\n";
|
||||
}
|
||||
ss << "\n";
|
||||
}
|
||||
|
@ -554,8 +515,7 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
|
|||
|
||||
for( int i = 0; i < n_polys; i++ )
|
||||
{
|
||||
POLYGON paths;
|
||||
|
||||
ClipperLib::Paths paths;
|
||||
aStream >> tmp;
|
||||
|
||||
if( tmp != "poly" )
|
||||
|
@ -569,26 +529,23 @@ bool SHAPE_POLY_SET::Parse( std::stringstream& aStream )
|
|||
|
||||
for( int j = 0; j < n_outlines; j++ )
|
||||
{
|
||||
SHAPE_LINE_CHAIN outline;
|
||||
|
||||
outline.SetClosed( true );
|
||||
ClipperLib::Path outline;
|
||||
|
||||
aStream >> tmp;
|
||||
int n_vertices = atoi( tmp.c_str() );
|
||||
for( int v = 0; v < n_vertices; v++ )
|
||||
{
|
||||
VECTOR2I p;
|
||||
ClipperLib::IntPoint p;
|
||||
|
||||
aStream >> tmp; p.x = atoi( tmp.c_str() );
|
||||
aStream >> tmp; p.y = atoi( tmp.c_str() );
|
||||
outline.Append( p );
|
||||
aStream >> tmp; p.X = atoi( tmp.c_str() );
|
||||
aStream >> tmp; p.Y = atoi( tmp.c_str() );
|
||||
outline.push_back( p );
|
||||
}
|
||||
|
||||
paths.push_back( outline );
|
||||
}
|
||||
|
||||
m_polys.push_back( paths );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -600,146 +557,22 @@ const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const
|
|||
|
||||
for( unsigned i = 0; i < m_polys.size(); i++ )
|
||||
{
|
||||
if( first )
|
||||
bb = m_polys[i][0].BBox();
|
||||
else
|
||||
bb.Merge( m_polys[i][0].BBox() );
|
||||
for( unsigned j = 0; j < m_polys[i].size(); j++)
|
||||
{
|
||||
for( unsigned v = 0; v < m_polys[i][j].size(); v++)
|
||||
{
|
||||
VECTOR2I p( m_polys[i][j][v].X, m_polys[i][j][v].Y );
|
||||
|
||||
if( first )
|
||||
bb = BOX2I( p, VECTOR2I( 0, 0 ) );
|
||||
else
|
||||
bb.Merge( p );
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb.Inflate( aClearance );
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::RemoveAllContours()
|
||||
{
|
||||
m_polys.clear();
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::DeletePolygon( int aIdx )
|
||||
{
|
||||
m_polys.erase( m_polys.begin() + aIdx );
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::Append( const SHAPE_POLY_SET& aSet )
|
||||
{
|
||||
m_polys.insert( m_polys.end(), aSet.m_polys.begin(), aSet.m_polys.end() );
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::Append( const VECTOR2I& aP, int aOutline, int aHole )
|
||||
{
|
||||
Append( aP.x, aP.y, aOutline, aHole );
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const
|
||||
{
|
||||
// fixme: support holes!
|
||||
|
||||
if( aSubpolyIndex >= 0 )
|
||||
return pointInPolygon( aP, m_polys[aSubpolyIndex][0] );
|
||||
|
||||
BOOST_FOREACH ( const POLYGON& polys, m_polys )
|
||||
{
|
||||
if( pointInPolygon( aP, polys[0] ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const
|
||||
{
|
||||
int result = 0;
|
||||
int cnt = aPath.PointCount();
|
||||
|
||||
if ( !aPath.BBox().Contains( aP ) ) // test with bounding box first
|
||||
return false;
|
||||
|
||||
if( cnt < 3 )
|
||||
return false;
|
||||
|
||||
VECTOR2I ip = aPath.CPoint( 0 );
|
||||
|
||||
for( int i = 1; i <= cnt; ++i )
|
||||
{
|
||||
VECTOR2I ipNext = ( i == cnt ? aPath.CPoint( 0 ) : aPath.CPoint( i ) );
|
||||
|
||||
if( ipNext.y == aP.y )
|
||||
{
|
||||
if( ( ipNext.x == aP.x ) || ( ip.y == aP.y &&
|
||||
( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
if( ( ip.y < aP.y ) != ( ipNext.y < aP.y ) )
|
||||
{
|
||||
if( ip.x >= aP.x )
|
||||
{
|
||||
if( ipNext.x > aP.x )
|
||||
result = 1 - result;
|
||||
else
|
||||
{
|
||||
int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) -
|
||||
(int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y );
|
||||
|
||||
if( !d )
|
||||
return true;
|
||||
|
||||
if( ( d > 0 ) == ( ipNext.y > ip.y ) )
|
||||
result = 1 - result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( ipNext.x > aP.x )
|
||||
{
|
||||
int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) -
|
||||
(int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y );
|
||||
|
||||
if( !d )
|
||||
return -1;
|
||||
|
||||
if( ( d > 0 ) == ( ipNext.y > ip.y ) )
|
||||
result = 1 - result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ip = ipNext;
|
||||
}
|
||||
|
||||
return result ? true : false;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::Move( const VECTOR2I& aVector )
|
||||
{
|
||||
BOOST_FOREACH( POLYGON &poly, m_polys )
|
||||
{
|
||||
BOOST_FOREACH( SHAPE_LINE_CHAIN &path, poly )
|
||||
{
|
||||
path.Move( aVector );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int SHAPE_POLY_SET::TotalVertices() const
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
BOOST_FOREACH( const POLYGON& poly, m_polys )
|
||||
{
|
||||
BOOST_FOREACH ( const SHAPE_LINE_CHAIN& path, poly )
|
||||
{
|
||||
c += path.PointCount();
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <polygons_defs.h>
|
||||
#include <gr_basic.h>
|
||||
#include <common.h>
|
||||
#include <trigo.h>
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
#include <fctsys.h>
|
||||
#include <trigo.h>
|
||||
#include <macros.h>
|
||||
#include <PolyLine.h>
|
||||
|
||||
#include <geometry/shape_poly_set.h>
|
||||
/**
|
||||
* Function TransformCircleToPolygon
|
||||
* convert a circle to a polygon, using multiple straight lines
|
||||
|
@ -45,9 +45,9 @@
|
|||
* Note: the polygon is inside the circle, so if you want to have the polygon
|
||||
* outside the circle, you should give aRadius calculated with a correction factor
|
||||
*/
|
||||
void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
wxPoint aCenter, int aRadius,
|
||||
int aCircleToSegmentsCount );
|
||||
void TransformCircleToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aCenter, int aRadius,
|
||||
int aCircleToSegmentsCount );
|
||||
|
||||
/**
|
||||
* Function TransformRoundedEndsSegmentToPolygon
|
||||
|
@ -61,7 +61,7 @@ void TransformCircleToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* Note: the polygon is inside the arc ends, so if you want to have the polygon
|
||||
* outside the circle, you should give aStart and aEnd calculated with a correction factor
|
||||
*/
|
||||
void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformRoundedEndsSegmentToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aStart, wxPoint aEnd,
|
||||
int aCircleToSegmentsCount,
|
||||
int aWidth );
|
||||
|
@ -78,7 +78,7 @@ void TransformRoundedEndsSegmentToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* @param aCircleToSegmentsCount = the number of segments to approximate a circle
|
||||
* @param aWidth = width (thickness) of the line
|
||||
*/
|
||||
void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformArcToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aCentre, wxPoint aStart, double aArcAngle,
|
||||
int aCircleToSegmentsCount, int aWidth );
|
||||
|
||||
|
@ -92,7 +92,7 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* @param aCircleToSegmentsCount = the number of segments to approximate a circle
|
||||
* @param aWidth = width (thickness) of the ring
|
||||
*/
|
||||
void TransformRingToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformRingToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxPoint aCentre, int aRadius,
|
||||
int aCircleToSegmentsCount, int aWidth );
|
||||
|
||||
|
|
|
@ -255,9 +255,6 @@ public:
|
|||
if( aIndex < 0 )
|
||||
aIndex += PointCount();
|
||||
|
||||
if( aIndex >= PointCount() )
|
||||
aIndex -= PointCount();
|
||||
|
||||
return m_points[aIndex];
|
||||
}
|
||||
|
||||
|
@ -581,7 +578,6 @@ public:
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
/// array of vertices
|
||||
std::vector<VECTOR2I> m_points;
|
||||
|
|
|
@ -32,103 +32,25 @@
|
|||
|
||||
#include "clipper.hpp"
|
||||
|
||||
|
||||
/**
|
||||
* Class SHAPE_POLY_SET
|
||||
*
|
||||
* Represents a set of closed polygons. Polygons may be nonconvex, self-intersecting
|
||||
* and have holes. Provides boolean operations (using Clipper library as the backend).
|
||||
*
|
||||
* TODO: add convex partitioning & spatial index
|
||||
* TODO: document, derive from class SHAPE, add convex partitioning & spatial index
|
||||
*/
|
||||
class SHAPE_POLY_SET : public SHAPE
|
||||
{
|
||||
public:
|
||||
///> represents a single polygon outline with holes. The first entry is the outline,
|
||||
///> the remaining (if any), are the holes
|
||||
typedef std::vector<SHAPE_LINE_CHAIN> POLYGON;
|
||||
|
||||
/**
|
||||
* Class ITERATOR_TEMPLATE
|
||||
*
|
||||
* Base class for iterating over all vertices in a given SHAPE_POLY_SET
|
||||
*/
|
||||
template <class T>
|
||||
class ITERATOR_TEMPLATE {
|
||||
public:
|
||||
|
||||
bool IsEndContour() const
|
||||
{
|
||||
return m_currentVertex + 1 == m_poly->CPolygon( m_currentOutline )[0].PointCount();
|
||||
}
|
||||
|
||||
bool IsLastContour() const
|
||||
{
|
||||
return m_currentOutline == m_lastOutline;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return m_currentOutline <= m_lastOutline;
|
||||
}
|
||||
|
||||
void Advance()
|
||||
{
|
||||
m_currentVertex ++;
|
||||
|
||||
if( m_currentVertex >= m_poly->CPolygon( m_currentOutline )[0].PointCount() )
|
||||
{
|
||||
m_currentVertex = 0;
|
||||
m_currentOutline++;
|
||||
}
|
||||
}
|
||||
|
||||
void operator++( int dummy )
|
||||
{
|
||||
Advance();
|
||||
}
|
||||
|
||||
void operator++()
|
||||
{
|
||||
Advance();
|
||||
}
|
||||
|
||||
T& Get()
|
||||
{
|
||||
return m_poly->Polygon( m_currentOutline )[0].Point( m_currentVertex );
|
||||
}
|
||||
|
||||
T& operator*()
|
||||
{
|
||||
return Get();
|
||||
}
|
||||
|
||||
T* operator->()
|
||||
{
|
||||
return &Get();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
friend class SHAPE_POLY_SET;
|
||||
|
||||
SHAPE_POLY_SET* m_poly;
|
||||
int m_currentOutline;
|
||||
int m_lastOutline;
|
||||
int m_currentVertex;
|
||||
};
|
||||
|
||||
typedef ITERATOR_TEMPLATE<VECTOR2I> ITERATOR;
|
||||
typedef ITERATOR_TEMPLATE<const VECTOR2I> CONST_ITERATOR;
|
||||
|
||||
SHAPE_POLY_SET();
|
||||
~SHAPE_POLY_SET();
|
||||
SHAPE_POLY_SET() : SHAPE( SH_POLY_SET ) {};
|
||||
~SHAPE_POLY_SET() {};
|
||||
|
||||
///> Creates a new empty polygon in the set and returns its index
|
||||
int NewOutline();
|
||||
|
||||
///> Creates a new hole in a given outline
|
||||
int NewHole( int aOutline = -1 );
|
||||
///> Cretes a new empty hole in the given outline (default: last one) and returns its index
|
||||
int NewHole( int aOutline = -1);
|
||||
|
||||
///> Adds a new outline to the set and returns its index
|
||||
int AddOutline( const SHAPE_LINE_CHAIN& aOutline );
|
||||
|
@ -136,20 +58,11 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
///> Adds a new hole to the given outline (default: last) and returns its index
|
||||
int AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline = -1 );
|
||||
|
||||
///> Appends a vertex at the end of the given outline/hole (default: the last outline)
|
||||
int Append( int x, int y, int aOutline = -1, int aHole = -1 );
|
||||
|
||||
///> Merges polygons from two sets.
|
||||
void Append( const SHAPE_POLY_SET& aSet );
|
||||
|
||||
///> Appends a vertex at the end of the given outline/hole (default: the last outline)
|
||||
void Append( const VECTOR2I& aP, int aOutline = -1, int aHole = -1 );
|
||||
///> Appends a vertex at the end of the given outline/hole (default: last hole in the last outline)
|
||||
int AppendVertex( int x, int y, int aOutline = -1, int aHole = -1 );
|
||||
|
||||
///> Returns the index-th vertex in a given hole outline within a given outline
|
||||
VECTOR2I& Vertex( int index, int aOutline = -1, int aHole = -1 );
|
||||
|
||||
///> Returns the index-th vertex in a given hole outline within a given outline
|
||||
const VECTOR2I& CVertex( int index, int aOutline = -1, int aHole = -1 ) const;
|
||||
const VECTOR2I GetVertex( int index, int aOutline = -1, int aHole = -1) const;
|
||||
|
||||
///> Returns true if any of the outlines is self-intersecting
|
||||
bool IsSelfIntersecting();
|
||||
|
@ -160,109 +73,28 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
///> Returns the number of vertices in a given outline/hole
|
||||
int VertexCount( int aOutline = -1, int aHole = -1 ) const;
|
||||
|
||||
///> Returns the number of holes in a given outline
|
||||
int HoleCount( int aOutline ) const;
|
||||
|
||||
///> Returns the reference to aIndex-th outline in the set
|
||||
SHAPE_LINE_CHAIN& Outline( int aIndex )
|
||||
{
|
||||
return m_polys[aIndex][0];
|
||||
}
|
||||
|
||||
///> Returns the reference to aHole-th hole in the aIndex-th outline
|
||||
SHAPE_LINE_CHAIN& Hole( int aOutline, int aHole )
|
||||
{
|
||||
return m_polys[aOutline][aHole + 1];
|
||||
}
|
||||
|
||||
///> Returns the aIndex-th subpolygon in the set
|
||||
POLYGON& Polygon( int aIndex )
|
||||
///> Returns the internal representation (ClipperLib) of a given polygon (outline + holes)
|
||||
const ClipperLib::Paths& GetPoly( int aIndex ) const
|
||||
{
|
||||
return m_polys[aIndex];
|
||||
}
|
||||
|
||||
const SHAPE_LINE_CHAIN& COutline( int aIndex ) const
|
||||
{
|
||||
return m_polys[aIndex][0];
|
||||
}
|
||||
|
||||
const SHAPE_LINE_CHAIN& CHole( int aOutline, int aHole ) const
|
||||
{
|
||||
return m_polys[aOutline][aHole + 1];
|
||||
}
|
||||
|
||||
const POLYGON& CPolygon( int aIndex ) const
|
||||
{
|
||||
return m_polys[aIndex];
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for iterating between aFirst and aLast outline.
|
||||
ITERATOR Iterate( int aFirst, int aLast )
|
||||
{
|
||||
ITERATOR iter;
|
||||
|
||||
iter.m_poly = this;
|
||||
iter.m_currentOutline = aFirst;
|
||||
iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast;
|
||||
iter.m_currentVertex = 0;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for iterating aOutline-th outline
|
||||
ITERATOR Iterate( int aOutline )
|
||||
{
|
||||
return Iterate( aOutline, aOutline );
|
||||
}
|
||||
|
||||
///> Returns an iterator object, for all outlines in the set (no holes)
|
||||
ITERATOR Iterate()
|
||||
{
|
||||
return Iterate( 0, OutlineCount() - 1 );
|
||||
}
|
||||
|
||||
CONST_ITERATOR CIterate( int aFirst, int aLast ) const
|
||||
{
|
||||
CONST_ITERATOR iter;
|
||||
|
||||
iter.m_poly = const_cast<SHAPE_POLY_SET*>( this );
|
||||
iter.m_currentOutline = aFirst;
|
||||
iter.m_lastOutline = aLast < 0 ? OutlineCount() - 1 : aLast;
|
||||
iter.m_currentVertex = 0;
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
CONST_ITERATOR CIterate( int aOutline ) const
|
||||
{
|
||||
return CIterate( aOutline, aOutline );
|
||||
}
|
||||
|
||||
CONST_ITERATOR CIterate() const
|
||||
{
|
||||
return CIterate( 0, OutlineCount() - 1 );
|
||||
}
|
||||
|
||||
|
||||
///> Performs boolean polyset union
|
||||
void BooleanAdd( const SHAPE_POLY_SET& b );
|
||||
|
||||
///> Performs boolean polyset difference
|
||||
void BooleanSubtract( const SHAPE_POLY_SET& b );
|
||||
void Subtract( const SHAPE_POLY_SET& b );
|
||||
|
||||
///> Performs outline inflation/deflation, using round corners.
|
||||
void Inflate( int aFactor, int aCircleSegmentsCount );
|
||||
///> Performs boolean polyset union
|
||||
void Add( const SHAPE_POLY_SET& b );
|
||||
|
||||
///> Performs smooth outline inflation (Minkowski sum of the outline and a circle of a given radius)
|
||||
void SmoothInflate( int aFactor );
|
||||
|
||||
///> Performs outline erosion/shrinking
|
||||
void Erode( int aFactor );
|
||||
|
||||
///> Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the outer ring
|
||||
///> to the inner holes
|
||||
void Fracture();
|
||||
|
||||
///> Converts a set of slitted polygons to a set of polygons with holes
|
||||
void Unfracture();
|
||||
|
||||
///> Returns true if the polygon set has any holes.
|
||||
bool HasHoles() const;
|
||||
|
||||
///> Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
|
||||
void Simplify();
|
||||
|
||||
|
@ -272,10 +104,8 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
/// @copydoc SHAPE::Parse()
|
||||
bool Parse( std::stringstream& aStream );
|
||||
|
||||
/// @copydoc SHAPE::Move()
|
||||
void Move( const VECTOR2I& aVector );
|
||||
void Move( const VECTOR2I& aVector ) { assert(false ); };
|
||||
|
||||
/// @copydoc SHAPE::IsSolid()
|
||||
bool IsSolid() const
|
||||
{
|
||||
return true;
|
||||
|
@ -287,43 +117,15 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const { return false; }
|
||||
bool Collide( const SEG& aSeg, int aClearance = 0 ) const { return false; }
|
||||
|
||||
|
||||
///> Returns true is a given subpolygon contains the point aP. If aSubpolyIndex < 0 (default value),
|
||||
///> checks all polygons in the set
|
||||
bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1 ) const;
|
||||
|
||||
///> Returns true if the set is empty (no polygons at all)
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return m_polys.size() == 0;
|
||||
}
|
||||
|
||||
///> Removes all outlines & holes (clears) the polygon set.
|
||||
void RemoveAllContours();
|
||||
|
||||
///> Returns total number of vertices stored in the set.
|
||||
int TotalVertices() const;
|
||||
|
||||
///> Deletes aIdx-th polygon from the set
|
||||
void DeletePolygon( int aIdx );
|
||||
|
||||
private:
|
||||
|
||||
SHAPE_LINE_CHAIN& getContourForCorner( int aCornerId, int& aIndexWithinContour );
|
||||
VECTOR2I& vertex( int aCornerId );
|
||||
const VECTOR2I& cvertex( int aCornerId ) const;
|
||||
|
||||
|
||||
void fractureSingle( POLYGON& paths );
|
||||
void importTree( ClipperLib::PolyTree* tree );
|
||||
void fractureSingle( ClipperLib::Paths& paths );
|
||||
void importTree( ClipperLib::PolyTree* tree);
|
||||
void booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b );
|
||||
|
||||
bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const;
|
||||
const ClipperLib::Path convert( const SHAPE_LINE_CHAIN& aPath );
|
||||
|
||||
const ClipperLib::Path convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation );
|
||||
const SHAPE_LINE_CHAIN convertFromClipper( const ClipperLib::Path& aPath );
|
||||
|
||||
typedef std::vector<POLYGON> Polyset;
|
||||
typedef std::vector<ClipperLib::Paths> Polyset;
|
||||
|
||||
Polyset m_polys;
|
||||
};
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <polygons_defs.h>
|
||||
#include <drawtxt.h>
|
||||
#include <pcbnew.h>
|
||||
#include <wxPcbStruct.h>
|
||||
|
@ -50,9 +51,9 @@
|
|||
// These variables are parameters used in addTextSegmToPoly.
|
||||
// But addTextSegmToPoly is a call-back function,
|
||||
// so we cannot send them as arguments.
|
||||
static int s_textWidth;
|
||||
static int s_textCircle2SegmentCount;
|
||||
static SHAPE_POLY_SET* s_cornerBuffer;
|
||||
int s_textWidth;
|
||||
int s_textCircle2SegmentCount;
|
||||
CPOLYGONS_LIST* s_cornerBuffer;
|
||||
|
||||
// This is a call back function, used by DrawGraphicText to draw the 3D text shape:
|
||||
static void addTextSegmToPoly( int x0, int y0, int xf, int yf )
|
||||
|
@ -63,7 +64,7 @@ static void addTextSegmToPoly( int x0, int y0, int xf, int yf )
|
|||
}
|
||||
|
||||
|
||||
void BOARD::ConvertBrdLayerToPolygonalContours( LAYER_ID aLayer, SHAPE_POLY_SET& aOutlines )
|
||||
void BOARD::ConvertBrdLayerToPolygonalContours( LAYER_ID aLayer, CPOLYGONS_LIST& aOutlines )
|
||||
{
|
||||
// Number of segments to convert a circle to a polygon
|
||||
const int segcountforcircle = 18;
|
||||
|
@ -127,7 +128,7 @@ void BOARD::ConvertBrdLayerToPolygonalContours( LAYER_ID aLayer, SHAPE_POLY_SET&
|
|||
|
||||
|
||||
void MODULE::TransformPadsShapesWithClearanceToPolygon( LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aInflateValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor,
|
||||
|
@ -202,7 +203,7 @@ void MODULE::TransformPadsShapesWithClearanceToPolygon( LAYER_ID aLayer,
|
|||
*/
|
||||
void MODULE::TransformGraphicShapesWithClearanceToPolygonSet(
|
||||
LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aInflateValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor )
|
||||
|
@ -279,30 +280,43 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet(
|
|||
* keep arc radius when approximated by segments
|
||||
*/
|
||||
void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet(
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor )
|
||||
{
|
||||
if( GetFilledPolysList().IsEmpty() )
|
||||
unsigned cornerscount = GetFilledPolysList().GetCornersCount();
|
||||
|
||||
if( cornerscount == 0 )
|
||||
return;
|
||||
|
||||
// add filled areas polygons
|
||||
aCornerBuffer.Append( m_FilledPolysList );
|
||||
|
||||
// add filled areas outlines, which are drawn with thick lines
|
||||
for( int i = 0; i < m_FilledPolysList.OutlineCount(); i++ )
|
||||
wxPoint seg_start, seg_end;
|
||||
int i_start_contour = 0;
|
||||
for( unsigned ic = 0; ic < cornerscount; ic++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& path = m_FilledPolysList.COutline( i );
|
||||
seg_start.x = m_FilledPolysList[ ic ].x;
|
||||
seg_start.y = m_FilledPolysList[ ic ].y;
|
||||
unsigned ic_next = ic+1;
|
||||
|
||||
for( int j = 0; j < path.PointCount(); j++ )
|
||||
if( !m_FilledPolysList[ic].end_contour &&
|
||||
ic_next < cornerscount )
|
||||
{
|
||||
const VECTOR2I& a = path.CPoint( j );
|
||||
const VECTOR2I& b = path.CPoint( j + 1 );
|
||||
|
||||
TransformRoundedEndsSegmentToPolygon( aCornerBuffer, wxPoint( a.x, a.y ), wxPoint( b.x, b.y ),
|
||||
aCircleToSegmentsCount,
|
||||
GetMinThickness() );
|
||||
seg_end.x = m_FilledPolysList[ ic_next ].x;
|
||||
seg_end.y = m_FilledPolysList[ ic_next ].y;
|
||||
}
|
||||
else
|
||||
{
|
||||
seg_end.x = m_FilledPolysList[ i_start_contour ].x;
|
||||
seg_end.y = m_FilledPolysList[ i_start_contour ].y;
|
||||
i_start_contour = ic_next;
|
||||
}
|
||||
|
||||
TransformRoundedEndsSegmentToPolygon( aCornerBuffer, seg_start, seg_end,
|
||||
aCircleToSegmentsCount,
|
||||
GetMinThickness() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,13 +329,13 @@ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet(
|
|||
* @param aClearanceValue = the clearance around the text bounding box
|
||||
*/
|
||||
void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon(
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aClearanceValue ) const
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue ) const
|
||||
{
|
||||
if( GetText().Length() == 0 )
|
||||
return;
|
||||
|
||||
wxPoint corners[4]; // Buffer of polygon corners
|
||||
CPolyPt corners[4]; // Buffer of polygon corners
|
||||
|
||||
EDA_RECT rect = GetTextBox( -1 );
|
||||
rect.Inflate( aClearanceValue );
|
||||
|
@ -334,14 +348,14 @@ void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon(
|
|||
corners[3].y = corners[2].y;
|
||||
corners[3].x = corners[0].x;
|
||||
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( int ii = 0; ii < 4; ii++ )
|
||||
{
|
||||
// Rotate polygon
|
||||
RotatePoint( &corners[ii].x, &corners[ii].y, m_Pos.x, m_Pos.y, m_Orient );
|
||||
aCornerBuffer.Append( corners[ii].x, corners[ii].y );
|
||||
aCornerBuffer.Append( corners[ii] );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
}
|
||||
|
||||
|
||||
|
@ -349,7 +363,7 @@ void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon(
|
|||
* Convert the text shape to a set of polygons (one by segment)
|
||||
* Used in filling zones calculations and 3D view
|
||||
* Circles and arcs are approximated by segments
|
||||
* aCornerBuffer = SHAPE_POLY_SET to store the polygon corners
|
||||
* aCornerBuffer = CPOLYGONS_LIST to store the polygon corners
|
||||
* aClearanceValue = the clearance around the text
|
||||
* aCircleToSegmentsCount = the number of segments to approximate a circle
|
||||
* aCorrectionFactor = the correction to apply to circles radius to keep
|
||||
|
@ -358,7 +372,7 @@ void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon(
|
|||
*/
|
||||
|
||||
void TEXTE_PCB::TransformShapeWithClearanceToPolygonSet(
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const
|
||||
|
@ -414,7 +428,7 @@ void TEXTE_PCB::TransformShapeWithClearanceToPolygonSet(
|
|||
* clearance when the circle is approxiamted by segment bigger or equal
|
||||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const
|
||||
|
@ -449,8 +463,6 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
|
|||
MODULE* module = GetParentModule(); // NULL for items not in footprints
|
||||
double orientation = module ? module->GetOrientation() : 0.0;
|
||||
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
// Build the polygon with the actual position and orientation:
|
||||
std::vector< wxPoint> poly;
|
||||
poly = GetPolyPoints();
|
||||
|
@ -486,8 +498,9 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
|
|||
for( unsigned ii = 0; ii < poly.size(); ii++ )
|
||||
{
|
||||
CPolyPt corner( poly[ii] );
|
||||
aCornerBuffer.Append( corner.x, corner.y );
|
||||
aCornerBuffer.Append( corner );
|
||||
}
|
||||
aCornerBuffer.CloseLastContour();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -512,7 +525,7 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
|
|||
* clearance when the circle is approximated by segment bigger or equal
|
||||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TRACK:: TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const
|
||||
|
@ -541,14 +554,15 @@ void TRACK::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* Convert the pad shape to a closed polygon
|
||||
* Used in filling zones calculations and 3D view generation
|
||||
* Circles and arcs are approximated by segments
|
||||
* aCornerBuffer = a SHAPE_POLY_SET to store the polygon corners
|
||||
* aCornerBuffer = a CPOLYGONS_LIST to store the polygon corners
|
||||
* aClearanceValue = the clearance around the pad
|
||||
* aCircleToSegmentsCount = the number of segments to approximate a circle
|
||||
* aCorrectionFactor = the correction to apply to circles radius to keep
|
||||
* clearance when the circle is approximated by segment bigger or equal
|
||||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void D_PAD:: TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
#include <clipper.hpp>
|
||||
void D_PAD:: TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const
|
||||
|
@ -601,21 +615,36 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer
|
|||
wxPoint corners[4];
|
||||
BuildPadPolygon( corners, wxSize( 0, 0 ), angle );
|
||||
|
||||
SHAPE_POLY_SET outline;
|
||||
|
||||
outline.NewOutline();
|
||||
// We are using ClipperLib to inflate the polygon shape, using
|
||||
// arcs to connect moved segments.
|
||||
ClipperLib::Path outline;
|
||||
ClipperLib::Paths shapeWithClearance;
|
||||
|
||||
for( int ii = 0; ii < 4; ii++ )
|
||||
{
|
||||
corners[ii] += PadShapePos;
|
||||
outline.Append( corners[ii].x, corners[ii].y );
|
||||
outline << ClipperLib::IntPoint( corners[ii].x, corners[ii].y );
|
||||
}
|
||||
|
||||
ClipperLib::ClipperOffset offset_engine;
|
||||
// Prepare an offset (inflate) transform, with edges connected by arcs
|
||||
offset_engine.AddPath( outline, ClipperLib::jtRound, ClipperLib::etClosedPolygon );
|
||||
|
||||
// Clipper approximates arcs by segments
|
||||
// It uses a value called ArcTolerance which is the max error between the arc
|
||||
// and segments created to approximate this arc
|
||||
// the number of segm per circle is:
|
||||
// n = PI / acos(1 - arc_tolerance / (arc radius))
|
||||
// the arc radius is aClearanceValue
|
||||
// because arc_tolerance is << aClearanceValue and aClearanceValue >= 0
|
||||
// n = PI / (arc_tolerance / aClearanceValue )
|
||||
offset_engine.ArcTolerance = (double)aClearanceValue / 3.14 / aCircleToSegmentsCount;
|
||||
|
||||
double rounding_radius = aClearanceValue * aCorrectionFactor;
|
||||
offset_engine.Execute( shapeWithClearance, rounding_radius );
|
||||
|
||||
outline.Inflate( (int) rounding_radius, aCircleToSegmentsCount );
|
||||
|
||||
aCornerBuffer.Append( outline );
|
||||
// get new outline (only one polygon is expected)
|
||||
aCornerBuffer.ImportFrom( shapeWithClearance );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -628,7 +657,7 @@ void D_PAD:: TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer
|
|||
* Note: for Round and oval pads this function is equivalent to
|
||||
* TransformShapeWithClearanceToPolygon, but not for other shapes
|
||||
*/
|
||||
void D_PAD::BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void D_PAD::BuildPadShapePolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxSize aInflateValue, int aSegmentsPerCircle,
|
||||
double aCorrectionFactor ) const
|
||||
{
|
||||
|
@ -645,15 +674,15 @@ void D_PAD::BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
|
||||
case PAD_TRAPEZOID:
|
||||
case PAD_RECT:
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
BuildPadPolygon( corners, aInflateValue, m_Orient );
|
||||
for( int ii = 0; ii < 4; ii++ )
|
||||
{
|
||||
corners[ii] += PadShapePos; // Shift origin to position
|
||||
aCornerBuffer.Append( corners[ii].x, corners[ii].y );
|
||||
CPolyPt polypoint( corners[ii].x, corners[ii].y );
|
||||
aCornerBuffer.Append( polypoint );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -664,7 +693,7 @@ void D_PAD::BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* depending on shape pad hole and orientation
|
||||
* return false if the pad has no hole, true otherwise
|
||||
*/
|
||||
bool D_PAD::BuildPadDrillShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
bool D_PAD::BuildPadDrillShapePolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aInflateValue, int aSegmentsPerCircle ) const
|
||||
{
|
||||
wxSize drillsize = GetDrillSize();
|
||||
|
@ -721,7 +750,7 @@ bool D_PAD::BuildPadDrillShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
* and are used in microwave applications and they *DO NOT* have a thermal relief that
|
||||
* change the shape by creating stubs and destroy their properties.
|
||||
*/
|
||||
void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void CreateThermalReliefPadPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
D_PAD& aPad,
|
||||
int aThermalGap,
|
||||
int aCopperThickness,
|
||||
|
@ -827,16 +856,15 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
|
||||
for( unsigned ihole = 0; ihole < 4; ihole++ )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( unsigned ii = 0; ii < corners_buffer.size(); ii++ )
|
||||
{
|
||||
corner = corners_buffer[ii];
|
||||
RotatePoint( &corner, th_angle + angle_pad ); // Rotate by segment angle and pad orientation
|
||||
corner += PadShapePos;
|
||||
aCornerBuffer.Append( corner.x, corner.y );
|
||||
aCornerBuffer.Append( CPolyPt( corner.x, corner.y ) );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
th_angle += 900; // Note: th_angle in in 0.1 deg.
|
||||
}
|
||||
}
|
||||
|
@ -932,15 +960,15 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
|
||||
for( int irect = 0; irect < 2; irect++ )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
|
||||
{
|
||||
wxPoint cpos = corners_buffer[ic];
|
||||
RotatePoint( &cpos, angle );
|
||||
cpos += PadShapePos;
|
||||
aCornerBuffer.Append( cpos.x, cpos.y );
|
||||
aCornerBuffer.Append( CPolyPt( cpos.x, cpos.y ) );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
angle = AddAngles( angle, 1800 ); // this is calculate hole 3
|
||||
}
|
||||
|
||||
|
@ -957,16 +985,15 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
|
||||
for( int irect = 0; irect < 2; irect++ )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
|
||||
{
|
||||
wxPoint cpos = corners_buffer[ic];
|
||||
RotatePoint( &cpos, angle );
|
||||
cpos += PadShapePos;
|
||||
aCornerBuffer.Append( cpos.x, cpos.y );
|
||||
aCornerBuffer.Append( CPolyPt( cpos.x, cpos.y ) );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
angle = AddAngles( angle, 1800 );
|
||||
}
|
||||
}
|
||||
|
@ -1030,16 +1057,15 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
|
||||
for( int irect = 0; irect < 2; irect++ )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
|
||||
{
|
||||
wxPoint cpos = corners_buffer[ic];
|
||||
RotatePoint( &cpos, angle ); // Rotate according to module orientation
|
||||
cpos += PadShapePos; // Shift origin to position
|
||||
aCornerBuffer.Append( cpos.x, cpos.y );
|
||||
aCornerBuffer.Append( CPolyPt( cpos.x, cpos.y ) );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
angle = AddAngles( angle, 1800 ); // this is calculate hole 3
|
||||
}
|
||||
|
||||
|
@ -1054,16 +1080,15 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
// Now add corner 4 and 2 (2 is the corner 4 rotated by 180 deg
|
||||
for( int irect = 0; irect < 2; irect++ )
|
||||
{
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
|
||||
{
|
||||
wxPoint cpos = corners_buffer[ic];
|
||||
RotatePoint( &cpos, angle );
|
||||
cpos += PadShapePos;
|
||||
aCornerBuffer.Append( cpos.x, cpos.y );
|
||||
aCornerBuffer.Append( CPolyPt( cpos.x, cpos.y ) );
|
||||
}
|
||||
|
||||
aCornerBuffer.CloseLastContour();
|
||||
angle = AddAngles( angle, 1800 );
|
||||
}
|
||||
}
|
||||
|
@ -1071,25 +1096,30 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
|
||||
case PAD_TRAPEZOID:
|
||||
{
|
||||
SHAPE_POLY_SET antipad; // The full antipad area
|
||||
|
||||
CPOLYGONS_LIST cbuffer;
|
||||
// We need a length to build the stubs of the thermal reliefs
|
||||
// the value is not very important. The pad bounding box gives a reasonable value
|
||||
EDA_RECT bbox = aPad.GetBoundingBox();
|
||||
int stub_len = std::max( bbox.GetWidth(), bbox.GetHeight() );
|
||||
|
||||
aPad.TransformShapeWithClearanceToPolygon( antipad, aThermalGap,
|
||||
aPad.TransformShapeWithClearanceToPolygon( cbuffer, aThermalGap,
|
||||
aCircleToSegmentsCount, aCorrectionFactor );
|
||||
|
||||
SHAPE_POLY_SET stub; // A basic stub ( a rectangle)
|
||||
SHAPE_POLY_SET stubs; // the full stubs shape
|
||||
SHAPE_POLY_SET thermalShape; // the holes in copper zone
|
||||
// We are using ClipperLib to substract stubs to clearance area (antipad area).
|
||||
ClipperLib::Path antipad; // The full antipad area
|
||||
ClipperLib::Path stub; // A basic stub ( a rectangle)
|
||||
ClipperLib::Paths stubs; // the full stubs shape
|
||||
ClipperLib::Paths thermalShape; // the holes in copper zone
|
||||
|
||||
// cbuffer is expected to contain only one polygon, which is
|
||||
// area of the pad + the thermal gap (the antipad)
|
||||
for( unsigned ii = 0; ii < cbuffer.GetCornersCount(); ii++ )
|
||||
antipad << ClipperLib::IntPoint( cbuffer.GetPos(ii).x, cbuffer.GetPos(ii).y );
|
||||
|
||||
// We now substract the stubs (connections to the copper zone)
|
||||
//ClipperLib::Clipper clip_engine;
|
||||
ClipperLib::Clipper clip_engine;
|
||||
// Prepare a clipping transform
|
||||
//clip_engine.AddPath( antipad, ClipperLib::ptSubject, true );
|
||||
clip_engine.AddPath( antipad, ClipperLib::ptSubject, true );
|
||||
|
||||
// Create stubs and add them to clipper engine
|
||||
wxPoint stubBuffer[4];
|
||||
|
@ -1102,17 +1132,16 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
stubBuffer[3] = stubBuffer[2];
|
||||
stubBuffer[3].y = copper_thickness.y/2;
|
||||
|
||||
stub.NewOutline();
|
||||
|
||||
for( unsigned ii = 0; ii < DIM( stubBuffer ); ii++ )
|
||||
{
|
||||
wxPoint cpos = stubBuffer[ii];
|
||||
RotatePoint( &cpos, aPad.GetOrientation() );
|
||||
cpos += PadShapePos;
|
||||
stub.Append( cpos.x, cpos.y );
|
||||
stub << ClipperLib::IntPoint( cpos.x, cpos.y );
|
||||
}
|
||||
|
||||
stubs.Append( stub );
|
||||
ClipperLib::Clipper stubs_engine;
|
||||
stubs_engine.AddPath( stub, ClipperLib::ptSubject, true );
|
||||
|
||||
stubBuffer[0].y = stub_len;
|
||||
stubBuffer[0].x = copper_thickness.x/2;
|
||||
|
@ -1122,24 +1151,27 @@ void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
stubBuffer[2].y = -stub_len;
|
||||
stubBuffer[3] = stubBuffer[2];
|
||||
stubBuffer[3].x = copper_thickness.x/2;
|
||||
|
||||
stub.RemoveAllContours();
|
||||
stub.NewOutline();
|
||||
stub.clear();
|
||||
|
||||
for( unsigned ii = 0; ii < DIM( stubBuffer ); ii++ )
|
||||
{
|
||||
wxPoint cpos = stubBuffer[ii];
|
||||
RotatePoint( &cpos, aPad.GetOrientation() );
|
||||
cpos += PadShapePos;
|
||||
stub.Append( cpos.x, cpos.y );
|
||||
stub << ClipperLib::IntPoint( cpos.x, cpos.y );
|
||||
}
|
||||
|
||||
stubs.Append( stub );
|
||||
stubs.Simplify();
|
||||
stubs_engine.AddPath( stub, ClipperLib::ptClip, true );
|
||||
|
||||
antipad.BooleanSubtract( stubs );
|
||||
aCornerBuffer.Append( antipad );
|
||||
// Build the full stubs shape:
|
||||
stubs_engine.Execute( ClipperLib::ctUnion, stubs );
|
||||
|
||||
// remove stubs to antipad area (i.e. add copper stubs)
|
||||
clip_engine.AddPath( stubs[0], ClipperLib::ptClip, true );
|
||||
clip_engine.Execute( ClipperLib::ctDifference, thermalShape );
|
||||
|
||||
// put thermal shapes (holes) to list:
|
||||
aCornerBuffer.ImportFrom( thermalShape );
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -2741,8 +2741,8 @@ wxString BOARD::GetNextModuleReferenceWithPrefix( const wxString& aPrefix,
|
|||
* return true if success, false if a contour is not valid
|
||||
*/
|
||||
#include <specctra.h>
|
||||
bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
|
||||
SHAPE_POLY_SET& aHoles,
|
||||
bool BOARD::GetBoardPolygonOutlines( CPOLYGONS_LIST& aOutlines,
|
||||
CPOLYGONS_LIST& aHoles,
|
||||
wxString* aErrorText )
|
||||
{
|
||||
// the SPECCTRA_DB function to extract board outlines:
|
||||
|
|
|
@ -57,7 +57,7 @@ class MSG_PANEL_ITEM;
|
|||
class NETLIST;
|
||||
class REPORTER;
|
||||
class RN_DATA;
|
||||
class SHAPE_POLY_SET;
|
||||
|
||||
|
||||
// non-owning container of item candidates when searching for items on the same track.
|
||||
typedef std::vector< TRACK* > TRACK_PTRS;
|
||||
|
@ -600,14 +600,15 @@ public:
|
|||
* from lines, arcs and circle items on edge cut layer
|
||||
* Any closed outline inside the main outline is a hole
|
||||
* All contours should be closed, i.e. have valid vertices to build a closed polygon
|
||||
* @param aPoly The SHAPE_POLY_SET to fill in with outlines/holes.
|
||||
* @param aOutlines The CPOLYGONS_LIST to fill in with main outlines.
|
||||
* @param aHoles The empty CPOLYGONS_LIST to fill in with holes, if any.
|
||||
* @param aErrorText = a wxString reference to display an error message
|
||||
* with the coordinate of the point which creates the error
|
||||
* (default = NULL , no message returned on error)
|
||||
* @return true if success, false if a contour is not valid
|
||||
*/
|
||||
bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
|
||||
SHAPE_POLY_SET& aHoles,
|
||||
bool GetBoardPolygonOutlines( CPOLYGONS_LIST& aOutlines,
|
||||
CPOLYGONS_LIST& aHoles,
|
||||
wxString* aErrorText = NULL );
|
||||
|
||||
/**
|
||||
|
@ -619,9 +620,9 @@ public:
|
|||
* or 3D viewer
|
||||
* the polygons are not merged.
|
||||
* @param aLayer = A copper layer, like B_Cu, etc.
|
||||
* @param aOutlines The SHAPE_POLY_SET to fill in with items outline.
|
||||
* @param aOutlines The CPOLYGONS_LIST to fill in with items outline.
|
||||
*/
|
||||
void ConvertBrdLayerToPolygonalContours( LAYER_ID aLayer, SHAPE_POLY_SET& aOutlines );
|
||||
void ConvertBrdLayerToPolygonalContours( LAYER_ID aLayer, CPOLYGONS_LIST& aOutlines );
|
||||
|
||||
/**
|
||||
* Function GetLayerID
|
||||
|
|
|
@ -231,7 +231,7 @@ public:
|
|||
* clearance when the circle is approximated by segment bigger or equal
|
||||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const;
|
||||
|
|
|
@ -336,7 +336,7 @@ public:
|
|||
* default = false
|
||||
*/
|
||||
void TransformPadsShapesWithClearanceToPolygon( LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aInflateValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor,
|
||||
|
@ -361,7 +361,7 @@ public:
|
|||
*/
|
||||
void TransformGraphicShapesWithClearanceToPolygonSet(
|
||||
LAYER_ID aLayer,
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aInflateValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor );
|
||||
|
|
|
@ -227,7 +227,7 @@ public:
|
|||
* clearance when the circle is approximated by segment bigger or equal
|
||||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const;
|
||||
|
@ -324,7 +324,7 @@ public:
|
|||
* @param aCorrectionFactor = the correction to apply to circles radius to keep
|
||||
* the pad size when the circle is approximated by segments
|
||||
*/
|
||||
void BuildPadShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void BuildPadShapePolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
wxSize aInflateValue, int aSegmentsPerCircle,
|
||||
double aCorrectionFactor ) const;
|
||||
|
||||
|
@ -339,7 +339,7 @@ public:
|
|||
* (used for round and oblong shapes only(16 to 32 is a good value)
|
||||
* @return false if the pad has no hole, true otherwise
|
||||
*/
|
||||
bool BuildPadDrillShapePolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
bool BuildPadDrillShapePolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aInflateValue, int aSegmentsPerCircle ) const;
|
||||
|
||||
/**
|
||||
|
|
|
@ -111,7 +111,7 @@ public:
|
|||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void TransformBoundingBoxWithClearanceToPolygon(
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue ) const;
|
||||
|
||||
/**
|
||||
|
@ -126,7 +126,7 @@ public:
|
|||
* clearance when the circle is approximated by segment bigger or equal
|
||||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void TransformShapeWithClearanceToPolygonSet( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformShapeWithClearanceToPolygonSet( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const;
|
||||
|
|
|
@ -178,7 +178,7 @@ public:
|
|||
* clearance when the circle is approximated by segment bigger or equal
|
||||
* to the real clearance value (usually near from 1.0)
|
||||
*/
|
||||
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aClearanceValue,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor ) const;
|
||||
|
|
|
@ -120,7 +120,7 @@ EDA_ITEM* ZONE_CONTAINER::Clone() const
|
|||
|
||||
bool ZONE_CONTAINER::UnFill()
|
||||
{
|
||||
bool change = ( !m_FilledPolysList.IsEmpty() ) ||
|
||||
bool change = ( m_FilledPolysList.GetCornersCount() > 0 ) ||
|
||||
( m_FillSegmList.size() > 0 );
|
||||
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
|
@ -228,7 +228,7 @@ void ZONE_CONTAINER::DrawFilledArea( EDA_DRAW_PANEL* panel,
|
|||
if( displ_opts->m_DisplayZonesMode == 1 ) // Do not show filled areas
|
||||
return;
|
||||
|
||||
if( m_FilledPolysList.IsEmpty() ) // Nothing to draw
|
||||
if( m_FilledPolysList.GetCornersCount() == 0 ) // Nothing to draw
|
||||
return;
|
||||
|
||||
BOARD* brd = GetBoard();
|
||||
|
@ -253,57 +253,69 @@ void ZONE_CONTAINER::DrawFilledArea( EDA_DRAW_PANEL* panel,
|
|||
|
||||
SetAlpha( &color, 150 );
|
||||
|
||||
CornersTypeBuffer.clear();
|
||||
CornersBuffer.clear();
|
||||
|
||||
for ( int ic = 0; ic < m_FilledPolysList.OutlineCount(); ic++ )
|
||||
// Draw all filled areas
|
||||
int imax = m_FilledPolysList.GetCornersCount() - 1;
|
||||
|
||||
for( int ic = 0; ic <= imax; ic++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& path = m_FilledPolysList.COutline( ic );
|
||||
const CPolyPt& corner = m_FilledPolysList.GetCorner( ic );
|
||||
wxPoint coord( corner.x + offset.x, corner.y + offset.y );
|
||||
CornersBuffer.push_back( coord );
|
||||
CornersTypeBuffer.push_back( (char) corner.m_flags );
|
||||
|
||||
CornersBuffer.clear();
|
||||
|
||||
wxPoint p0;
|
||||
|
||||
for( int j = 0; j < path.PointCount(); j++ )
|
||||
// the last corner of a filled area is found: draw it
|
||||
if( (corner.end_contour) || (ic == imax) )
|
||||
{
|
||||
const VECTOR2I& corner = path.CPoint( j );
|
||||
|
||||
wxPoint coord( corner.x + offset.x, corner.y + offset.y );
|
||||
|
||||
if( j == 0 )
|
||||
p0 = coord;
|
||||
|
||||
CornersBuffer.push_back( coord );
|
||||
}
|
||||
|
||||
CornersBuffer.push_back( p0 );
|
||||
|
||||
// Draw outlines:
|
||||
if( ( m_ZoneMinThickness > 1 ) || outline_mode )
|
||||
{
|
||||
int ilim = CornersBuffer.size() - 1;
|
||||
|
||||
for( int is = 0, ie = ilim; is <= ilim; ie = is, is++ )
|
||||
/* Draw the current filled area: draw segments outline first
|
||||
* Curiously, draw segments outline first and after draw filled polygons
|
||||
* with outlines thickness = 0 is a faster than
|
||||
* just draw filled polygons but with outlines thickness = m_ZoneMinThickness
|
||||
* So DO NOT use draw filled polygons with outlines having a thickness > 0
|
||||
* Note: Extra segments ( added to joint holes with external outline) flagged by
|
||||
* m_flags != 0 are not drawn
|
||||
* Note not all polygon libraries provide a flag for these extra-segments, therefore
|
||||
* the m_flags member can be always 0
|
||||
*/
|
||||
{
|
||||
int x0 = CornersBuffer[is].x;
|
||||
int y0 = CornersBuffer[is].y;
|
||||
int x1 = CornersBuffer[ie].x;
|
||||
int y1 = CornersBuffer[ie].y;
|
||||
// Draw outlines:
|
||||
if( (m_ZoneMinThickness > 1) || outline_mode )
|
||||
{
|
||||
int ilim = CornersBuffer.size() - 1;
|
||||
|
||||
// Draw only basic outlines, not extra segments.
|
||||
if( !displ_opts->m_DisplayPcbTrackFill || GetState( FORCE_SKETCH ) )
|
||||
GRCSegm( panel->GetClipBox(), DC,
|
||||
x0, y0, x1, y1,
|
||||
m_ZoneMinThickness, color );
|
||||
else
|
||||
GRFillCSegm( panel->GetClipBox(), DC,
|
||||
x0, y0, x1, y1,
|
||||
m_ZoneMinThickness, color );
|
||||
for( int is = 0, ie = ilim; is <= ilim; ie = is, is++ )
|
||||
{
|
||||
int x0 = CornersBuffer[is].x;
|
||||
int y0 = CornersBuffer[is].y;
|
||||
int x1 = CornersBuffer[ie].x;
|
||||
int y1 = CornersBuffer[ie].y;
|
||||
|
||||
// Draw only basic outlines, not extra segments.
|
||||
if( CornersTypeBuffer[ie] == 0 )
|
||||
{
|
||||
if( !displ_opts->m_DisplayPcbTrackFill || GetState( FORCE_SKETCH ) )
|
||||
GRCSegm( panel->GetClipBox(), DC,
|
||||
x0, y0, x1, y1,
|
||||
m_ZoneMinThickness, color );
|
||||
else
|
||||
GRFillCSegm( panel->GetClipBox(), DC,
|
||||
x0, y0, x1, y1,
|
||||
m_ZoneMinThickness, color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw areas:
|
||||
if( m_FillMode == 0 && !outline_mode )
|
||||
GRPoly( panel->GetClipBox(), DC, CornersBuffer.size(), &CornersBuffer[0],
|
||||
true, 0, color, color );
|
||||
}
|
||||
}
|
||||
|
||||
// Draw areas:
|
||||
if( m_FillMode == 0 && !outline_mode )
|
||||
GRPoly( panel->GetClipBox(), DC, CornersBuffer.size(), &CornersBuffer[0],
|
||||
true, 0, color, color );
|
||||
CornersTypeBuffer.clear();
|
||||
CornersBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if( m_FillMode == 1 && !outline_mode ) // filled with segments
|
||||
|
@ -564,7 +576,26 @@ int ZONE_CONTAINER::GetClearance( BOARD_CONNECTED_ITEM* aItem ) const
|
|||
|
||||
bool ZONE_CONTAINER::HitTestFilledArea( const wxPoint& aRefPos ) const
|
||||
{
|
||||
return m_FilledPolysList.Contains( VECTOR2I( aRefPos.x, aRefPos.y ) );
|
||||
unsigned indexstart = 0, indexend;
|
||||
bool inside = false;
|
||||
|
||||
for( indexend = 0; indexend < m_FilledPolysList.GetCornersCount(); indexend++ )
|
||||
{
|
||||
if( m_FilledPolysList.IsEndContour( indexend ) ) // end of a filled sub-area found
|
||||
{
|
||||
if( TestPointInsidePolygon( m_FilledPolysList, indexstart, indexend,
|
||||
aRefPos.x, aRefPos.y ) )
|
||||
{
|
||||
inside = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepare test of next area which starts after the current index end (if exists)
|
||||
indexstart = indexend + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
||||
|
||||
|
@ -643,9 +674,9 @@ void ZONE_CONTAINER::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
|
|||
msg.Printf( wxT( "%d" ), (int) m_Poly->m_HatchLines.size() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Hatch Lines" ), msg, BLUE ) );
|
||||
|
||||
if( !m_FilledPolysList.IsEmpty() )
|
||||
if( m_FilledPolysList.GetCornersCount() )
|
||||
{
|
||||
msg.Printf( wxT( "%d" ), (int) m_FilledPolysList.TotalVertices() );
|
||||
msg.Printf( wxT( "%d" ), (int) m_FilledPolysList.GetCornersCount() );
|
||||
aList.push_back( MSG_PANEL_ITEM( _( "Corner Count" ), msg, BLUE ) );
|
||||
}
|
||||
}
|
||||
|
@ -663,7 +694,12 @@ void ZONE_CONTAINER::Move( const wxPoint& offset )
|
|||
|
||||
m_Poly->Hatch();
|
||||
|
||||
m_FilledPolysList.Move( VECTOR2I( offset.x, offset.y ) );
|
||||
/* move filled areas: */
|
||||
for( unsigned ic = 0; ic < m_FilledPolysList.GetCornersCount(); ic++ )
|
||||
{
|
||||
m_FilledPolysList.SetX( ic, m_FilledPolysList.GetX( ic ) + offset.x );
|
||||
m_FilledPolysList.SetY( ic, m_FilledPolysList.GetY( ic ) + offset.y );
|
||||
}
|
||||
|
||||
for( unsigned ic = 0; ic < m_FillSegmList.size(); ic++ )
|
||||
{
|
||||
|
@ -710,8 +746,13 @@ void ZONE_CONTAINER::Rotate( const wxPoint& centre, double angle )
|
|||
m_Poly->Hatch();
|
||||
|
||||
/* rotate filled areas: */
|
||||
for( SHAPE_POLY_SET::ITERATOR ic = m_FilledPolysList.Iterate(); ic; ++ic )
|
||||
RotatePoint( &ic->x, &ic->y, centre.x, centre.y, angle );
|
||||
for( unsigned ic = 0; ic < m_FilledPolysList.GetCornersCount(); ic++ )
|
||||
{
|
||||
pos = m_FilledPolysList.GetPos( ic );
|
||||
RotatePoint( &pos, centre, angle );
|
||||
m_FilledPolysList.SetX( ic, pos.x );
|
||||
m_FilledPolysList.SetY( ic, pos.y );
|
||||
}
|
||||
|
||||
for( unsigned ic = 0; ic < m_FillSegmList.size(); ic++ )
|
||||
{
|
||||
|
@ -738,10 +779,11 @@ void ZONE_CONTAINER::Mirror( const wxPoint& mirror_ref )
|
|||
|
||||
m_Poly->Hatch();
|
||||
|
||||
for( SHAPE_POLY_SET::ITERATOR ic = m_FilledPolysList.Iterate(); ic; ++ic )
|
||||
/* mirror filled areas: */
|
||||
for( unsigned ic = 0; ic < m_FilledPolysList.GetCornersCount(); ic++ )
|
||||
{
|
||||
int py = mirror_ref.y - ic->y;
|
||||
ic->y = py + mirror_ref.y;
|
||||
int py = mirror_ref.y - m_FilledPolysList.GetY( ic );
|
||||
m_FilledPolysList.SetY( ic, py + mirror_ref.y );
|
||||
}
|
||||
|
||||
for( unsigned ic = 0; ic < m_FillSegmList.size(); ic++ )
|
||||
|
@ -854,4 +896,15 @@ wxString ZONE_CONTAINER::GetSelectMenuText() const
|
|||
GetChars( GetLayerName() ) );
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Copy polygons stored in aKiPolyList to m_FilledPolysList
|
||||
* The previous m_FilledPolysList contents is replaced.
|
||||
*/
|
||||
void ZONE_CONTAINER::CopyPolygonsFromClipperPathsToFilledPolysList(
|
||||
ClipperLib::Paths& aClipperPolyList )
|
||||
{
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
m_FilledPolysList.ImportFrom( aClipperPolyList );
|
||||
}
|
||||
|
|
|
@ -165,6 +165,18 @@ public:
|
|||
*/
|
||||
void TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb );
|
||||
|
||||
/**
|
||||
* Function CalculateSubAreaBoundaryBox
|
||||
* Calculates the bounding box of a a filled area ( list of CPolyPt )
|
||||
* use m_FilledPolysList as list of CPolyPt (that are the corners of one or more
|
||||
* polygons or filled areas )
|
||||
* @return an EDA_RECT as bounding box
|
||||
* @param aIndexStart = index of the first corner of a polygon (filled area)
|
||||
* in m_FilledPolysList
|
||||
* @param aIndexEnd = index of the last corner of a polygon in m_FilledPolysList
|
||||
*/
|
||||
EDA_RECT CalculateSubAreaBoundaryBox( int aIndexStart, int aIndexEnd );
|
||||
|
||||
/**
|
||||
* Function IsOnCopperLayer
|
||||
* @return true if this zone is on a copper layer, false if on a technical layer
|
||||
|
@ -259,7 +271,7 @@ public:
|
|||
* @param aCorrectionFactor = the correction to apply to arcs radius to roughly
|
||||
* keep arc radius when approximated by segments
|
||||
*/
|
||||
void TransformSolidAreasShapesToPolygonSet( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformSolidAreasShapesToPolygonSet( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aCircleToSegmentsCount,
|
||||
double aCorrectionFactor );
|
||||
/**
|
||||
|
@ -283,7 +295,32 @@ public:
|
|||
* AddClearanceAreasPolygonsToPolysList() to add holes for pads and tracks
|
||||
* and other items not in net.
|
||||
*/
|
||||
bool BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* aOutlineBuffer = NULL );
|
||||
bool BuildFilledSolidAreasPolygons( BOARD* aPcb, CPOLYGONS_LIST* aOutlineBuffer = NULL );
|
||||
|
||||
/**
|
||||
* Function CopyPolygonsFromKiPolygonListToFilledPolysList
|
||||
* Copy polygons stored in aKiPolyList to m_FilledPolysList
|
||||
* The previous m_FilledPolysList contents is replaced.
|
||||
* @param aKiPolyList = a KI_POLYGON_SET containing polygons.
|
||||
*/
|
||||
void CopyPolygonsFromKiPolygonListToFilledPolysList( KI_POLYGON_SET& aKiPolyList );
|
||||
|
||||
/**
|
||||
* Function CopyPolygonsFromClipperPathsToFilledPolysList
|
||||
* Copy polygons stored in aKiPolyList to m_FilledPolysList
|
||||
* The previous m_FilledPolysList contents is replaced.
|
||||
* @param aClipperPolyList = a ClipperLib::Paths containing polygons.
|
||||
*/
|
||||
void CopyPolygonsFromClipperPathsToFilledPolysList( ClipperLib::Paths& aClipperPolyList );
|
||||
|
||||
#if 0 //does not exist in rev 5741.
|
||||
/**
|
||||
* Function CopyPolygonsFromFilledPolysListToKiPolygonList
|
||||
* Copy polygons from m_FilledPolysList to aKiPolyList
|
||||
* @param aKiPolyList = a KI_POLYGON_SET to fill by polygons.
|
||||
*/
|
||||
void CopyPolygonsFromFilledPolysListToKiPolygonList( KI_POLYGON_SET& aKiPolyList );
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Function AddClearanceAreasPolygonsToPolysList
|
||||
|
@ -315,7 +352,7 @@ public:
|
|||
* false to use aMinClearanceValue only
|
||||
* if both aMinClearanceValue = 0 and aUseNetClearance = false: create the zone outline polygon.
|
||||
*/
|
||||
void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void TransformOutlinesShapeWithClearanceToPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
int aMinClearanceValue,
|
||||
bool aUseNetClearance );
|
||||
/**
|
||||
|
@ -473,7 +510,7 @@ public:
|
|||
* returns a reference to the list of filled polygons.
|
||||
* @return Reference to the list of filled polygons.
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetFilledPolysList() const
|
||||
const CPOLYGONS_LIST& GetFilledPolysList() const
|
||||
{
|
||||
return m_FilledPolysList;
|
||||
}
|
||||
|
@ -482,7 +519,7 @@ public:
|
|||
* Function AddFilledPolysList
|
||||
* sets the list of filled polygons.
|
||||
*/
|
||||
void AddFilledPolysList( SHAPE_POLY_SET& aPolysList )
|
||||
void AddFilledPolysList( CPOLYGONS_LIST& aPolysList )
|
||||
{
|
||||
m_FilledPolysList = aPolysList;
|
||||
}
|
||||
|
@ -511,7 +548,7 @@ public:
|
|||
|
||||
void AddPolygon( std::vector< wxPoint >& aPolygon );
|
||||
|
||||
void AddFilledPolygon( SHAPE_POLY_SET& aPolygon )
|
||||
void AddFilledPolygon( CPOLYGONS_LIST& aPolygon )
|
||||
{
|
||||
m_FilledPolysList.Append( aPolygon );
|
||||
}
|
||||
|
@ -547,7 +584,7 @@ public:
|
|||
|
||||
|
||||
private:
|
||||
void buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures );
|
||||
void buildFeatureHoleList( BOARD* aPcb, CPOLYGONS_LIST& aFeatures );
|
||||
|
||||
CPolyLine* m_Poly; ///< Outline of the zone.
|
||||
CPolyLine* m_smoothedPoly; // Corner-smoothed version of m_Poly
|
||||
|
@ -615,7 +652,7 @@ private:
|
|||
* connecting "holes" with external main outline. In complex cases an outline
|
||||
* described by m_Poly can have many filled areas
|
||||
*/
|
||||
SHAPE_POLY_SET m_FilledPolysList;
|
||||
CPOLYGONS_LIST m_FilledPolysList;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -698,8 +698,11 @@ static void export_vrml_drawings( MODEL_VRML& aModel, BOARD* pcb )
|
|||
// board edges and cutouts
|
||||
static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
|
||||
{
|
||||
SHAPE_POLY_SET bufferPcbOutlines; // stores the board main outlines
|
||||
SHAPE_POLY_SET allLayerHoles; // Contains through holes, calculated only once
|
||||
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
|
||||
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
|
||||
|
||||
allLayerHoles.reserve( 20000 );
|
||||
|
||||
// Build a polygon from edge cut items
|
||||
wxString msg;
|
||||
|
||||
|
@ -712,28 +715,47 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
|
|||
}
|
||||
|
||||
double scale = aModel.scale;
|
||||
|
||||
int i = 0;
|
||||
int seg;
|
||||
|
||||
for( int i = 0; i < bufferPcbOutlines.OutlineCount(); i++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& outline = bufferPcbOutlines.COutline( i );
|
||||
// deal with the solid outlines
|
||||
int nvert = bufferPcbOutlines.GetCornersCount();
|
||||
|
||||
while( i < nvert )
|
||||
{
|
||||
seg = aModel.board.NewContour();
|
||||
|
||||
for( int j = 0; j < outline.PointCount(); j++ )
|
||||
if( seg < 0 )
|
||||
{
|
||||
aModel.board.AddVertex( seg, (double)outline.CPoint(j).x * scale,
|
||||
-((double)outline.CPoint(j).y * scale ) );
|
||||
msg << wxT( "\n\n" ) <<
|
||||
_( "VRML Export Failed:\nCould not add outline to contours." );
|
||||
wxMessageBox( msg );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while( i < nvert )
|
||||
{
|
||||
if( bufferPcbOutlines[i].end_contour )
|
||||
break;
|
||||
|
||||
aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale,
|
||||
-(bufferPcbOutlines[i].y * scale ) );
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
aModel.board.EnsureWinding( seg, false );
|
||||
++i;
|
||||
}
|
||||
|
||||
for( int i = 0; i < allLayerHoles.OutlineCount(); i++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& outline = allLayerHoles.COutline( i );
|
||||
// deal with the holes
|
||||
nvert = allLayerHoles.GetCornersCount();
|
||||
|
||||
i = 0;
|
||||
while( i < nvert )
|
||||
{
|
||||
seg = aModel.holes.NewContour();
|
||||
|
||||
if( seg < 0 )
|
||||
|
@ -745,14 +767,19 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
|
|||
return;
|
||||
}
|
||||
|
||||
for( int j = 0; j < outline.PointCount(); j++ )
|
||||
while( i < nvert )
|
||||
{
|
||||
aModel.holes.AddVertex( seg, (double)outline.CPoint(j).x * scale,
|
||||
-((double)outline.CPoint(j).y * scale ) );
|
||||
if( allLayerHoles[i].end_contour )
|
||||
break;
|
||||
|
||||
aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale,
|
||||
-( allLayerHoles[i].y * scale ) );
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
aModel.holes.EnsureWinding( seg, true );
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -844,7 +871,9 @@ static void export_vrml_tracks( MODEL_VRML& aModel, BOARD* pcb )
|
|||
|
||||
static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
|
||||
{
|
||||
|
||||
double scale = aModel.scale;
|
||||
double x, y;
|
||||
|
||||
for( int ii = 0; ii < aPcb->GetAreaCount(); ii++ )
|
||||
{
|
||||
|
@ -861,23 +890,33 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
|
|||
zone->BuildFilledSolidAreasPolygons( aPcb );
|
||||
}
|
||||
|
||||
const SHAPE_POLY_SET& poly = zone->GetFilledPolysList();
|
||||
const CPOLYGONS_LIST& poly = zone->GetFilledPolysList();
|
||||
int nvert = poly.GetCornersCount();
|
||||
int i = 0;
|
||||
|
||||
for( int i = 0; i < poly.OutlineCount(); i++ )
|
||||
while( i < nvert )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& outline = poly.COutline( i );
|
||||
|
||||
int seg = vl->NewContour();
|
||||
|
||||
for( int j = 0; j < outline.PointCount(); j++ )
|
||||
if( seg < 0 )
|
||||
break;
|
||||
|
||||
while( i < nvert )
|
||||
{
|
||||
if( !vl->AddVertex( seg, (double)outline.CPoint( j ).x * scale,
|
||||
-((double)outline.CPoint( j ).y * scale ) ) )
|
||||
x = poly.GetX(i) * scale;
|
||||
y = -(poly.GetY(i) * scale);
|
||||
|
||||
if( poly.IsEndContour(i) )
|
||||
break;
|
||||
|
||||
if( !vl->AddVertex( seg, x, y ) )
|
||||
throw( std::runtime_error( vl->GetError() ) );
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
vl->EnsureWinding( seg, false );
|
||||
vl->EnsureWinding( seg, false );
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1617,22 +1617,22 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|||
}
|
||||
|
||||
// Save the PolysList
|
||||
const SHAPE_POLY_SET& fv = aZone->GetFilledPolysList();
|
||||
const CPOLYGONS_LIST& fv = aZone->GetFilledPolysList();
|
||||
newLine = 0;
|
||||
|
||||
if( !fv.IsEmpty() )
|
||||
if( fv.GetCornersCount() )
|
||||
{
|
||||
m_out->Print( aNestLevel+1, "(filled_polygon\n" );
|
||||
m_out->Print( aNestLevel+2, "(pts\n" );
|
||||
|
||||
for( SHAPE_POLY_SET::CONST_ITERATOR it = fv.CIterate(); it; ++it )
|
||||
for( unsigned it = 0; it < fv.GetCornersCount(); ++it )
|
||||
{
|
||||
if( newLine == 0 )
|
||||
m_out->Print( aNestLevel+3, "(xy %s %s)",
|
||||
FMT_IU( it->x ).c_str(), FMT_IU( it->y ).c_str() );
|
||||
FMT_IU( fv.GetX( it ) ).c_str(), FMT_IU( fv.GetY( it ) ).c_str() );
|
||||
else
|
||||
m_out->Print( 0, " (xy %s %s)",
|
||||
FMT_IU( it->x ) .c_str(), FMT_IU( it->y ).c_str() );
|
||||
FMT_IU( fv.GetX( it ) ).c_str(), FMT_IU( fv.GetY( it ) ).c_str() );
|
||||
|
||||
if( newLine < 4 )
|
||||
{
|
||||
|
@ -1644,14 +1644,14 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
|
|||
m_out->Print( 0, "\n" );
|
||||
}
|
||||
|
||||
if( it.IsEndContour() )
|
||||
if( fv.IsEndContour( it ) )
|
||||
{
|
||||
if( newLine != 0 )
|
||||
m_out->Print( 0, "\n" );
|
||||
|
||||
m_out->Print( aNestLevel+2, ")\n" );
|
||||
|
||||
if( !it.IsLastContour() )
|
||||
if( it+1 != fv.GetCornersCount() )
|
||||
{
|
||||
newLine = 0;
|
||||
m_out->Print( aNestLevel+1, ")\n" );
|
||||
|
|
|
@ -2678,9 +2678,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
|
|||
else if( TESTLINE( "$POLYSCORNERS" ) )
|
||||
{
|
||||
// Read the PolysList (polygons used for fill areas in the zone)
|
||||
SHAPE_POLY_SET polysList;
|
||||
|
||||
bool makeNewOutline = true;
|
||||
CPOLYGONS_LIST polysList;
|
||||
|
||||
while( ( line = READLINE( m_reader ) ) != NULL )
|
||||
{
|
||||
|
@ -2691,17 +2689,11 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
|
|||
BIU x = biuParse( line, &data );
|
||||
BIU y = biuParse( data, &data );
|
||||
|
||||
if( makeNewOutline )
|
||||
polysList.NewOutline();
|
||||
bool end_contour = intParse( data, &data ); // end_countour was a bool when file saved, so '0' or '1' here
|
||||
int cornerUtilityFlg = intParse( data );
|
||||
|
||||
polysList.Append( x, y );
|
||||
|
||||
bool end_contour = intParse( data, &data ); // end_countour was a bool when file saved, so '0' or '1' here
|
||||
intParse( data ); // skip corner utility flag
|
||||
|
||||
makeNewOutline = end_contour;
|
||||
polysList.Append( CPolyPt( x, y, end_contour, cornerUtilityFlg ) );
|
||||
}
|
||||
|
||||
zc->AddFilledPolysList( polysList );
|
||||
}
|
||||
|
||||
|
|
|
@ -757,7 +757,7 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu*
|
|||
AddMenuItem( zones_menu, ID_POPUP_PCB_FILL_ZONE, _( "Fill Zone" ),
|
||||
KiBitmap( fill_zone_xpm ) );
|
||||
|
||||
if( !edge_zone->GetFilledPolysList().IsEmpty() )
|
||||
if( edge_zone->GetFilledPolysList().GetCornersCount() > 0 )
|
||||
{
|
||||
AddMenuItem( zones_menu, ID_POPUP_PCB_REMOVE_FILLED_AREAS_IN_CURRENT_ZONE,
|
||||
_( "Remove Filled Areas in Zone" ), KiBitmap( zone_unfill_xpm ) );
|
||||
|
|
|
@ -885,9 +885,8 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone )
|
|||
// Draw the filling
|
||||
if( displayMode != PCB_RENDER_SETTINGS::DZ_HIDE_FILLED )
|
||||
{
|
||||
const SHAPE_POLY_SET& polySet = aZone->GetFilledPolysList();
|
||||
|
||||
if( polySet.OutlineCount() == 0 ) // Nothing to draw
|
||||
const std::vector<CPolyPt> polyPoints = aZone->GetFilledPolysList().GetList();
|
||||
if( polyPoints.size() == 0 ) // Nothing to draw
|
||||
return;
|
||||
|
||||
// Set up drawing options
|
||||
|
@ -905,28 +904,26 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone )
|
|||
m_gal->SetIsStroke( true );
|
||||
}
|
||||
|
||||
for( int i = 0; i < polySet.OutlineCount(); i++ )
|
||||
std::vector<CPolyPt>::const_iterator polyIterator;
|
||||
for( polyIterator = polyPoints.begin(); polyIterator != polyPoints.end(); ++polyIterator )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& outline = polySet.COutline( i );
|
||||
// fixme: GAL drawing API that accepts SHAPEs directly (this fiddling with double<>int conversion
|
||||
// is just a performance hog)
|
||||
// Find out all of polygons and then draw them
|
||||
corners.push_back( VECTOR2D( *polyIterator ) );
|
||||
|
||||
for( int j = 0; j < outline.PointCount(); j++ )
|
||||
corners.push_back ( (VECTOR2D) outline.CPoint( j ) );
|
||||
|
||||
corners.push_back( (VECTOR2D) outline.CPoint( 0 ) );
|
||||
|
||||
if( displayMode == PCB_RENDER_SETTINGS::DZ_SHOW_FILLED )
|
||||
if( polyIterator->end_contour )
|
||||
{
|
||||
m_gal->DrawPolygon( corners );
|
||||
m_gal->DrawPolyline( corners );
|
||||
}
|
||||
else if( displayMode == PCB_RENDER_SETTINGS::DZ_SHOW_OUTLINED )
|
||||
{
|
||||
m_gal->DrawPolyline( corners );
|
||||
}
|
||||
if( displayMode == PCB_RENDER_SETTINGS::DZ_SHOW_FILLED )
|
||||
{
|
||||
m_gal->DrawPolygon( corners );
|
||||
m_gal->DrawPolyline( corners );
|
||||
}
|
||||
else if( displayMode == PCB_RENDER_SETTINGS::DZ_SHOW_OUTLINED )
|
||||
{
|
||||
m_gal->DrawPolyline( corners );
|
||||
}
|
||||
|
||||
corners.clear();
|
||||
corners.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2540,7 +2540,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
|
|||
wxString netnameFromfile; // the zone net name find in file
|
||||
|
||||
// bigger scope since each filled_polygon is concatenated in here
|
||||
SHAPE_POLY_SET pts;
|
||||
CPOLYGONS_LIST pts;
|
||||
|
||||
std::auto_ptr< ZONE_CONTAINER > zone( new ZONE_CONTAINER( m_board ) );
|
||||
|
||||
|
@ -2790,14 +2790,13 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
|
|||
if( token != T_pts )
|
||||
Expecting( T_pts );
|
||||
|
||||
pts.NewOutline();
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
pts.Append( parseXY() );
|
||||
pts.Append( CPolyPt( parseXY() ) );
|
||||
}
|
||||
|
||||
NeedRIGHT();
|
||||
pts.CloseLastContour();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2842,7 +2841,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
|
|||
zone->Outline()->SetHatch( hatchStyle, hatchPitch, true );
|
||||
}
|
||||
|
||||
if( !pts.IsEmpty() )
|
||||
if( pts.GetCornersCount() )
|
||||
zone->AddFilledPolysList( pts );
|
||||
|
||||
// Ensure keepout and non copper zones do not have a net
|
||||
|
|
|
@ -542,14 +542,15 @@ static const LAYER_ID plot_seq[] = {
|
|||
|
||||
/* Plot outlines of copper, for copper layer
|
||||
*/
|
||||
void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
|
||||
#include "clipper.hpp"
|
||||
void PlotLayerOutlines( BOARD *aBoard, PLOTTER* aPlotter,
|
||||
LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
|
||||
{
|
||||
|
||||
BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
|
||||
itemplotter.SetLayerSet( aLayerMask );
|
||||
|
||||
SHAPE_POLY_SET outlines;
|
||||
CPOLYGONS_LIST outlines;
|
||||
|
||||
for( LSEQ seq = aLayerMask.Seq( plot_seq, DIM( plot_seq ) ); seq; ++seq )
|
||||
{
|
||||
|
@ -558,25 +559,51 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
|
|||
outlines.RemoveAllContours();
|
||||
aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines );
|
||||
|
||||
outlines.Simplify();
|
||||
// Merge all overlapping polygons.
|
||||
KI_POLYGON_SET kpolygons;
|
||||
KI_POLYGON_SET ktmp;
|
||||
outlines.ExportTo( ktmp );
|
||||
|
||||
kpolygons += ktmp;
|
||||
|
||||
// Plot outlines
|
||||
std::vector< wxPoint > cornerList;
|
||||
|
||||
// Now we have one or more basic polygons: plot each polygon
|
||||
for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
|
||||
for( unsigned ii = 0; ii < kpolygons.size(); ii++ )
|
||||
{
|
||||
cornerList.clear();
|
||||
const SHAPE_LINE_CHAIN& path = outlines.COutline( ii );
|
||||
KI_POLYGON polygon = kpolygons[ii];
|
||||
|
||||
for( int jj = 0; jj < path.PointCount(); jj++ )
|
||||
cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).x ) );
|
||||
// polygon contains only one polygon, but it can have holes linked by
|
||||
// overlapping segments.
|
||||
// To plot clean outlines, we have to break this polygon into more polygons with
|
||||
// no overlapping segments, using Clipper, because boost::polygon
|
||||
// does not allow that
|
||||
ClipperLib::Path raw_polygon;
|
||||
ClipperLib::Paths normalized_polygons;
|
||||
|
||||
// Ensure the polygon is closed
|
||||
if( cornerList[0] != cornerList[cornerList.size() - 1] )
|
||||
cornerList.push_back( cornerList[0] );
|
||||
for( unsigned ic = 0; ic < polygon.size(); ic++ )
|
||||
{
|
||||
KI_POLY_POINT corner = *(polygon.begin() + ic);
|
||||
raw_polygon.push_back( ClipperLib::IntPoint( corner.x(), corner.y() ) );
|
||||
}
|
||||
|
||||
aPlotter->PlotPoly( cornerList, NO_FILL );
|
||||
ClipperLib::SimplifyPolygon( raw_polygon, normalized_polygons );
|
||||
|
||||
// Now we have one or more basic polygons: plot each polygon
|
||||
for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ )
|
||||
{
|
||||
ClipperLib::Path& polygon = normalized_polygons[ii];
|
||||
cornerList.clear();
|
||||
|
||||
for( unsigned jj = 0; jj < polygon.size(); jj++ )
|
||||
cornerList.push_back( wxPoint( polygon[jj].X , polygon[jj].Y ) );
|
||||
|
||||
// Ensure the polygon is closed
|
||||
if( cornerList[0] != cornerList[cornerList.size()-1] )
|
||||
cornerList.push_back( cornerList[0] );
|
||||
|
||||
aPlotter->PlotPoly( cornerList, NO_FILL );
|
||||
}
|
||||
}
|
||||
|
||||
// Plot pad holes
|
||||
|
@ -677,8 +704,8 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
// This extra margin is used to merge too close shapes
|
||||
// (distance < aMinThickness), and will be removed when creating
|
||||
// the actual shapes
|
||||
SHAPE_POLY_SET areas; // Contains shapes to plot
|
||||
SHAPE_POLY_SET initialPolys; // Contains exact shapes to plot
|
||||
CPOLYGONS_LIST bufferPolys; // Contains shapes to plot
|
||||
CPOLYGONS_LIST initialPolys; // Contains exact shapes to plot
|
||||
|
||||
/* calculates the coeff to compensate radius reduction of holes clearance
|
||||
* due to the segment approx ( 1 /cos( PI/circleToSegmentsCount )
|
||||
|
@ -695,7 +722,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
circleToSegmentsCount, correction );
|
||||
// add shapes inflated by aMinThickness/2
|
||||
module->TransformPadsShapesWithClearanceToPolygon( layer,
|
||||
areas, inflate,
|
||||
bufferPolys, inflate,
|
||||
circleToSegmentsCount, correction );
|
||||
}
|
||||
|
||||
|
@ -727,7 +754,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
if( !( via_set & aLayerMask ).any() )
|
||||
continue;
|
||||
|
||||
via->TransformShapeWithClearanceToPolygon( areas, via_margin,
|
||||
via->TransformShapeWithClearanceToPolygon( bufferPolys, via_margin,
|
||||
circleToSegmentsCount,
|
||||
correction );
|
||||
via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance,
|
||||
|
@ -744,7 +771,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
if( zone->GetLayer() != layer )
|
||||
continue;
|
||||
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( areas,
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( bufferPolys,
|
||||
inflate, true );
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys,
|
||||
0, true );
|
||||
|
@ -761,15 +788,56 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
zone.SetMinThickness( 0 ); // trace polygons only
|
||||
zone.SetLayer ( layer );
|
||||
|
||||
areas.BooleanAdd( initialPolys );
|
||||
areas.Inflate( -inflate, circleToSegmentsCount );
|
||||
// Now:
|
||||
// 1 - merge polygons which are intersecting, i.e. remove gaps
|
||||
// having a thickness < aMinThickness
|
||||
// 2 - deflate resulting polygons by aMinThickness/2
|
||||
KI_POLYGON_SET areasToMerge;
|
||||
bufferPolys.ExportTo( areasToMerge );
|
||||
KI_POLYGON_SET initialAreas;
|
||||
initialPolys.ExportTo( initialAreas );
|
||||
|
||||
// Merge polygons: because each shape was created with an extra margin
|
||||
// = aMinThickness/2, shapes too close ( dist < aMinThickness )
|
||||
// will be merged, because they are overlapping
|
||||
KI_POLYGON_SET areas;
|
||||
areas |= areasToMerge; // Populates with merged polygons
|
||||
|
||||
// Deflate: remove the extra margin, to create the actual shapes
|
||||
// Here I am using polygon:resize, because this function creates better shapes
|
||||
// than deflate algo.
|
||||
// Use here deflate made by Clipper, because:
|
||||
// Clipper is (by far) faster and better, event using arcs to deflate shapes
|
||||
// boost::polygon < 1.56 polygon resize function sometimes crashes when deflating using arcs
|
||||
// boost::polygon >=1.56 polygon resize function just does not work
|
||||
// Note also we combine polygons using boost::polygon, which works better than Clipper,
|
||||
// especially with zones using holes linked to main outlines by overlapping segments
|
||||
CPOLYGONS_LIST tmp;
|
||||
tmp.ImportFrom( areas );
|
||||
|
||||
// Deflate area using Clipper, better than boost::polygon
|
||||
ClipperLib::Paths areasDeflate;
|
||||
tmp.ExportTo( areasDeflate );
|
||||
|
||||
// Deflate areas: they will have the right size after deflate
|
||||
ClipperLib::ClipperOffset offset_engine;
|
||||
circleToSegmentsCount = 16;
|
||||
offset_engine.ArcTolerance = (double)inflate / 3.14 / circleToSegmentsCount;
|
||||
offset_engine.AddPaths( areasDeflate, ClipperLib::jtRound, ClipperLib::etClosedPolygon );
|
||||
offset_engine.Execute( areasDeflate, -inflate );
|
||||
|
||||
// Combine the current areas to initial areas. This is mandatory because
|
||||
// inflate/deflate transform is not perfect, and we want the initial areas perfectly kept
|
||||
areas.BooleanAdd( initialPolys );
|
||||
areas.Fracture();
|
||||
tmp.RemoveAllContours();
|
||||
tmp.ImportFrom( areasDeflate );
|
||||
areas.clear();
|
||||
tmp.ExportTo( areas );
|
||||
|
||||
zone.AddFilledPolysList( areas );
|
||||
// Resize slightly changes shapes (the transform is not perfect).
|
||||
// So *ensure* initial shapes are kept
|
||||
areas |= initialAreas;
|
||||
|
||||
zone.CopyPolygonsFromKiPolygonListToFilledPolysList( areas );
|
||||
|
||||
itemplotter.PlotFilledAreas( &zone );
|
||||
}
|
||||
|
|
|
@ -493,9 +493,10 @@ void BRDITEMS_PLOTTER::PlotTextePcb( TEXTE_PCB* pt_texte )
|
|||
*/
|
||||
void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone )
|
||||
{
|
||||
const SHAPE_POLY_SET& polysList = aZone->GetFilledPolysList();
|
||||
const CPOLYGONS_LIST& polysList = aZone->GetFilledPolysList();
|
||||
unsigned imax = polysList.GetCornersCount();
|
||||
|
||||
if( polysList.IsEmpty() )
|
||||
if( imax == 0 ) // Nothing to draw
|
||||
return;
|
||||
|
||||
// We need a buffer to store corners coordinates:
|
||||
|
@ -510,12 +511,12 @@ void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone )
|
|||
*
|
||||
* in non filled mode the outline is plotted, but not the filling items
|
||||
*/
|
||||
for( SHAPE_POLY_SET::CONST_ITERATOR ic = polysList.CIterate(); ic; ++ic )
|
||||
for( unsigned ic = 0; ic < imax; ic++ )
|
||||
{
|
||||
wxPoint pos( ic->x, ic->y );
|
||||
wxPoint pos = polysList.GetPos( ic );
|
||||
cornerList.push_back( pos );
|
||||
|
||||
if( ic.IsEndContour() ) // Plot the current filled area outline
|
||||
if( polysList.IsEndContour( ic ) ) // Plot the current filled area outline
|
||||
{
|
||||
// First, close the outline
|
||||
if( cornerList[0] != cornerList[cornerList.size() - 1] )
|
||||
|
|
|
@ -44,8 +44,6 @@
|
|||
#include <boost/make_shared.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
@ -344,16 +342,11 @@ void RN_NET::clearNode( const RN_NODE_PTR& aNode )
|
|||
}
|
||||
|
||||
|
||||
RN_POLY::RN_POLY( const SHAPE_POLY_SET* aParent,
|
||||
int aSubpolygonIndex,
|
||||
RN_POLY::RN_POLY( const CPolyPt* aBegin, const CPolyPt* aEnd,
|
||||
RN_LINKS& aConnections, const BOX2I& aBBox ) :
|
||||
m_subpolygonIndex( aSubpolygonIndex ),
|
||||
m_bbox( aBBox ),
|
||||
m_parentPolyset( aParent )
|
||||
m_begin( aBegin ), m_end( aEnd ), m_bbox( aBBox )
|
||||
{
|
||||
const VECTOR2I& p = aParent->CVertex( aSubpolygonIndex, 0 );
|
||||
|
||||
m_node = aConnections.AddNode( p.x, p.y );
|
||||
m_node = aConnections.AddNode( m_begin->x, m_begin->y );
|
||||
|
||||
// Mark it as not appropriate as a destination of ratsnest edges
|
||||
// (edges coming out from a polygon vertex look weird)
|
||||
|
@ -363,9 +356,48 @@ RN_POLY::RN_POLY( const SHAPE_POLY_SET* aParent,
|
|||
|
||||
bool RN_POLY::HitTest( const RN_NODE_PTR& aNode ) const
|
||||
{
|
||||
VECTOR2I p( aNode->GetX(), aNode->GetY() );
|
||||
long xt = aNode->GetX();
|
||||
long yt = aNode->GetY();
|
||||
|
||||
return m_parentPolyset->Contains( p, m_subpolygonIndex );
|
||||
// If the point lies outside the bounding box, there is no point to check it further
|
||||
if( !m_bbox.Contains( xt, yt ) )
|
||||
return false;
|
||||
|
||||
long xNew, yNew, xOld, yOld, x1, y1, x2, y2;
|
||||
bool inside = false;
|
||||
|
||||
// For the first loop we have to use the last point as the previous point
|
||||
xOld = m_end->x;
|
||||
yOld = m_end->y;
|
||||
|
||||
for( const CPolyPt* point = m_begin; point <= m_end; ++point )
|
||||
{
|
||||
xNew = point->x;
|
||||
yNew = point->y;
|
||||
|
||||
// Swap points if needed, so always x2 >= x1
|
||||
if( xNew > xOld )
|
||||
{
|
||||
x1 = xOld; y1 = yOld;
|
||||
x2 = xNew; y2 = yNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = xNew; y1 = yNew;
|
||||
x2 = xOld; y2 = yOld;
|
||||
}
|
||||
|
||||
if( ( xNew < xt ) == ( xt <= xOld ) && /* edge "open" at left end */
|
||||
(double)( yt - y1 ) * (double)( x2 - x1 ) < (double)( y2 - y1 ) * (double)( xt - x1 ) )
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
|
||||
xOld = xNew;
|
||||
yOld = yNew;
|
||||
}
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
||||
|
||||
|
@ -422,14 +454,51 @@ void RN_NET::AddItem( const TRACK* aTrack )
|
|||
void RN_NET::AddItem( const ZONE_CONTAINER* aZone )
|
||||
{
|
||||
// Prepare a list of polygons (every zone can contain one or more polygons)
|
||||
const SHAPE_POLY_SET& polySet = aZone->GetFilledPolysList();
|
||||
const std::vector<CPolyPt>& polyPoints = aZone->GetFilledPolysList().GetList();
|
||||
|
||||
for( int i = 0; i < polySet.OutlineCount(); ++i )
|
||||
if( polyPoints.size() == 0 )
|
||||
return;
|
||||
|
||||
// Origin and end of bounding box for a polygon
|
||||
VECTOR2I origin( polyPoints[0].x, polyPoints[0].y );
|
||||
VECTOR2I end( polyPoints[0].x, polyPoints[0].y );
|
||||
unsigned int idxStart = 0;
|
||||
|
||||
// Extract polygons from zones
|
||||
for( unsigned int i = 0; i < polyPoints.size(); ++i )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& path = polySet.COutline( i );
|
||||
const CPolyPt& point = polyPoints[i];
|
||||
|
||||
RN_POLY poly = RN_POLY( &polySet, i, m_links, path.BBox() );
|
||||
m_zones[aZone].m_Polygons.push_back( poly );
|
||||
if( point.end_contour )
|
||||
{
|
||||
RN_POLY poly = RN_POLY( &polyPoints[idxStart], &point,
|
||||
m_links, BOX2I( origin, end - origin ) );
|
||||
poly.GetNode()->AddParent( aZone );
|
||||
m_zones[aZone].m_Polygons.push_back( poly );
|
||||
|
||||
idxStart = i + 1;
|
||||
|
||||
if( idxStart < polyPoints.size() )
|
||||
{
|
||||
origin.x = polyPoints[idxStart].x;
|
||||
origin.y = polyPoints[idxStart].y;
|
||||
end.x = polyPoints[idxStart].x;
|
||||
end.y = polyPoints[idxStart].y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine bounding box
|
||||
if( point.x < origin.x )
|
||||
origin.x = point.x;
|
||||
else if( point.x > end.x )
|
||||
end.x = point.x;
|
||||
|
||||
if( point.y < origin.y )
|
||||
origin.y = point.y;
|
||||
else if( point.y > end.y )
|
||||
end.y = point.y;
|
||||
}
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
|
|
|
@ -47,7 +47,7 @@ class D_PAD;
|
|||
class VIA;
|
||||
class TRACK;
|
||||
class ZONE_CONTAINER;
|
||||
class SHAPE_POLY_SET;
|
||||
class CPolyPt;
|
||||
|
||||
///> Types of items that are handled by the class
|
||||
enum RN_ITEM_TYPE
|
||||
|
@ -265,8 +265,7 @@ protected:
|
|||
class RN_POLY
|
||||
{
|
||||
public:
|
||||
RN_POLY( const SHAPE_POLY_SET* aParent,
|
||||
int aSubpolygonIndex,
|
||||
RN_POLY( const CPolyPt* aBegin, const CPolyPt* aEnd,
|
||||
RN_LINKS& aConnections, const BOX2I& aBBox );
|
||||
|
||||
/**
|
||||
|
@ -288,16 +287,15 @@ public:
|
|||
bool HitTest( const RN_NODE_PTR& aNode ) const;
|
||||
|
||||
private:
|
||||
///> Pointer to the first point of polyline bounding the polygon.
|
||||
const CPolyPt* m_begin;
|
||||
|
||||
///> Index of the outline in the parent polygon set
|
||||
int m_subpolygonIndex;
|
||||
///> Pointer to the last point of polyline bounding the polygon.
|
||||
const CPolyPt* m_end;
|
||||
|
||||
///> Bounding box of the polygon.
|
||||
BOX2I m_bbox;
|
||||
|
||||
///> Polygon set containing the geometry
|
||||
const SHAPE_POLY_SET* m_parentPolyset;
|
||||
|
||||
///> Node representing a polygon (it has the same coordinates as the first point of its
|
||||
///> bounding polyline.
|
||||
RN_NODE_PTR m_node;
|
||||
|
|
|
@ -43,7 +43,6 @@ class TRACK;
|
|||
class VIA;
|
||||
class NETCLASS;
|
||||
class MODULE;
|
||||
class SHAPE_POLY_SET;
|
||||
|
||||
typedef DSN::T DSN_T;
|
||||
|
||||
|
@ -3983,16 +3982,16 @@ public:
|
|||
* any closed outline inside the main outline is a hole
|
||||
* All contours should be closed, i.e. have valid vertices to build a closed polygon
|
||||
* @param aBoard The BOARD to get information from in order to make the outlines.
|
||||
* @param aOutlines The SHAPE_POLY_SET to fill in with main outlines.
|
||||
* @param aHoles The empty SHAPE_POLY_SET to fill in with holes, if any.
|
||||
* @param aOutlines The CPOLYGONS_LIST to fill in with main outlines.
|
||||
* @param aHoles The empty CPOLYGONS_LIST to fill in with holes, if any.
|
||||
* @param aErrorText = a wxString reference to display an error message
|
||||
* with the coordinate of the point which creates the error
|
||||
* (default = NULL , no message returned on error)
|
||||
* @return true if success, false if a contour is not valid
|
||||
*/
|
||||
bool GetBoardPolygonOutlines( BOARD* aBoard,
|
||||
SHAPE_POLY_SET& aOutlines,
|
||||
SHAPE_POLY_SET& aHoles,
|
||||
CPOLYGONS_LIST& aOutlines,
|
||||
CPOLYGONS_LIST& aHoles,
|
||||
wxString* aErrorText = NULL );
|
||||
};
|
||||
|
||||
|
|
|
@ -53,8 +53,6 @@
|
|||
|
||||
#include <collectors.h>
|
||||
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
#include <specctra.h>
|
||||
|
||||
using namespace DSN;
|
||||
|
@ -1332,8 +1330,8 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
|
|||
* All contours should be closed, i.e. valid closed polygon vertices
|
||||
*/
|
||||
bool SPECCTRA_DB::GetBoardPolygonOutlines( BOARD* aBoard,
|
||||
SHAPE_POLY_SET& aOutlines,
|
||||
SHAPE_POLY_SET& aHoles,
|
||||
CPOLYGONS_LIST& aOutlines,
|
||||
CPOLYGONS_LIST& aHoles,
|
||||
wxString* aErrorText )
|
||||
{
|
||||
bool success = true;
|
||||
|
@ -1349,8 +1347,6 @@ bool SPECCTRA_DB::GetBoardPolygonOutlines( BOARD* aBoard,
|
|||
BOUNDARY* boundary = new BOUNDARY( 0 );
|
||||
pcb->structure->SetBOUNDARY( boundary );
|
||||
|
||||
aOutlines.NewOutline();
|
||||
|
||||
try
|
||||
{
|
||||
fillBOUNDARY( aBoard, boundary );
|
||||
|
@ -1361,9 +1357,11 @@ bool SPECCTRA_DB::GetBoardPolygonOutlines( BOARD* aBoard,
|
|||
{
|
||||
corner.x = buffer[ii] * specctra2UIfactor;
|
||||
corner.y = - buffer[ii+1] * specctra2UIfactor;
|
||||
aOutlines.Append( corner.x, corner.y );
|
||||
aOutlines.Append( corner );
|
||||
}
|
||||
|
||||
aOutlines.CloseLastContour();
|
||||
|
||||
// Export holes, stored as keepouts polygonal shapes.
|
||||
// by fillBOUNDARY()
|
||||
KEEPOUTS& holes = pcb->structure->keepouts;
|
||||
|
@ -1373,15 +1371,13 @@ bool SPECCTRA_DB::GetBoardPolygonOutlines( BOARD* aBoard,
|
|||
KEEPOUT& keepout = *i;
|
||||
PATH* poly_hole = (PATH*)keepout.shape;
|
||||
POINTS& plist = poly_hole->GetPoints();
|
||||
|
||||
aHoles.NewOutline();
|
||||
|
||||
for( unsigned ii = 0; ii < plist.size(); ii++ )
|
||||
{
|
||||
corner.x = plist[ii].x * specctra2UIfactor;
|
||||
corner.y = - plist[ii].y * specctra2UIfactor;
|
||||
aHoles.Append( corner.x, corner.y );
|
||||
aHoles.Append( corner );
|
||||
}
|
||||
aHoles.CloseLastContour();
|
||||
}
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
|
@ -1401,24 +1397,23 @@ bool SPECCTRA_DB::GetBoardPolygonOutlines( BOARD* aBoard,
|
|||
if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
|
||||
bbbox.Inflate( Millimeter2iu( 1.0 ) );
|
||||
|
||||
aOutlines.RemoveAllContours();
|
||||
aOutlines.NewOutline();
|
||||
|
||||
corner.x = bbbox.GetOrigin().x;
|
||||
corner.y = bbbox.GetOrigin().y;
|
||||
aOutlines.Append( corner.x, corner.y );
|
||||
aOutlines.Append( corner );
|
||||
|
||||
corner.x = bbbox.GetOrigin().x;
|
||||
corner.y = bbbox.GetEnd().y;
|
||||
aOutlines.Append( corner.x, corner.y );
|
||||
aOutlines.Append( corner );
|
||||
|
||||
corner.x = bbbox.GetEnd().x;
|
||||
corner.y = bbbox.GetEnd().y;
|
||||
aOutlines.Append( corner.x, corner.y );
|
||||
aOutlines.Append( corner );
|
||||
|
||||
corner.x = bbbox.GetEnd().x;
|
||||
corner.y = bbbox.GetOrigin().y;
|
||||
aOutlines.Append( corner.x, corner.y );
|
||||
aOutlines.Append( corner );
|
||||
|
||||
aOutlines.CloseLastContour();
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
* to add holes for pads and tracks and other items not in net.
|
||||
*/
|
||||
|
||||
bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET* aOutlineBuffer )
|
||||
bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, CPOLYGONS_LIST* aOutlineBuffer )
|
||||
{
|
||||
/* convert outlines + holes to outlines without holes (adding extra segments if necessary)
|
||||
* m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building
|
||||
|
@ -92,7 +92,7 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
|
|||
}
|
||||
|
||||
if( aOutlineBuffer )
|
||||
aOutlineBuffer->Append( ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList ) );
|
||||
aOutlineBuffer->Append( m_smoothedPoly->m_CornersList );
|
||||
|
||||
/* For copper layers, we now must add holes in the Polygon list.
|
||||
* holes are pads and tracks with their clearance area
|
||||
|
@ -105,14 +105,15 @@ bool ZONE_CONTAINER::BuildFilledSolidAreasPolygons( BOARD* aPcb, SHAPE_POLY_SET*
|
|||
|
||||
if( IsOnCopperLayer() )
|
||||
{
|
||||
AddClearanceAreasPolygonsToPolysList_NG( aPcb );
|
||||
if(g_UseOldZoneFillingAlgo)
|
||||
AddClearanceAreasPolygonsToPolysList( aPcb );
|
||||
else
|
||||
AddClearanceAreasPolygonsToPolysList_NG( aPcb );
|
||||
}
|
||||
else
|
||||
{
|
||||
int margin = m_ZoneMinThickness / 2;
|
||||
m_FilledPolysList = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList );
|
||||
m_FilledPolysList.Inflate( -margin, 16 );
|
||||
m_FilledPolysList.Fracture();
|
||||
m_smoothedPoly->m_CornersList.InflateOutline(m_FilledPolysList, -margin, true );
|
||||
}
|
||||
|
||||
if( m_FillMode ) // if fill mode uses segments, create them:
|
||||
|
@ -134,9 +135,11 @@ static bool SortByXValues( const int& a, const int &b )
|
|||
|
||||
int ZONE_CONTAINER::FillZoneAreasWithSegments()
|
||||
{
|
||||
int ics, ice;
|
||||
int count = 0;
|
||||
std::vector <int> x_coordinates;
|
||||
bool error = false;
|
||||
int istart, iend; // index of the starting and the endif corner of one filled area in m_FilledPolysList
|
||||
int margin = m_ZoneMinThickness * 2 / 10;
|
||||
int minwidth = Mils2iu( 2 );
|
||||
margin = std::max ( minwidth, margin );
|
||||
|
@ -145,100 +148,113 @@ int ZONE_CONTAINER::FillZoneAreasWithSegments()
|
|||
|
||||
// Read all filled areas in m_FilledPolysList
|
||||
m_FillSegmList.clear();
|
||||
istart = 0;
|
||||
int end_list = m_FilledPolysList.GetCornersCount() - 1;
|
||||
|
||||
for ( int index = 0; index < m_FilledPolysList.OutlineCount(); index++ )
|
||||
for( int ic = 0; ic <= end_list; ic++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& outline = m_FilledPolysList.COutline( index );
|
||||
const BOX2I& rect = outline.BBox();
|
||||
|
||||
// Calculate the y limits of the zone
|
||||
for( int refy = rect.GetY(), endy = rect.GetBottom(); refy < endy; refy += step )
|
||||
CPolyPt* corner = &m_FilledPolysList[ic];
|
||||
if ( corner->end_contour || ( ic == end_list ) )
|
||||
{
|
||||
// find all intersection points of an infinite line with polyline sides
|
||||
x_coordinates.clear();
|
||||
iend = ic;
|
||||
EDA_RECT rect = CalculateSubAreaBoundaryBox( istart, iend );
|
||||
|
||||
for( int v = 0; v < outline.PointCount(); v++ )
|
||||
// Calculate the y limits of the zone
|
||||
for( int refy = rect.GetY(), endy = rect.GetBottom(); refy < endy; refy += step )
|
||||
{
|
||||
// find all intersection points of an infinite line with polyline sides
|
||||
x_coordinates.clear();
|
||||
|
||||
int seg_startX = outline.CPoint( v ).x;
|
||||
int seg_startY = outline.CPoint( v ).y;
|
||||
int seg_endX = outline.CPoint( v + 1 ).x;
|
||||
int seg_endY = outline.CPoint( v + 1 ).y;
|
||||
for( ics = istart, ice = iend; ics <= iend; ice = ics, ics++ )
|
||||
{
|
||||
if( m_FilledPolysList[ice].m_flags )
|
||||
continue;
|
||||
|
||||
/* Trivial cases: skip if ref above or below the segment to test */
|
||||
if( ( seg_startY > refy ) && ( seg_endY > refy ) )
|
||||
continue;
|
||||
int seg_startX = m_FilledPolysList[ics].x;
|
||||
int seg_startY = m_FilledPolysList[ics].y;
|
||||
int seg_endX = m_FilledPolysList[ice].x;
|
||||
int seg_endY = m_FilledPolysList[ice].y;
|
||||
|
||||
// segment below ref point, or its Y end pos on Y coordinate ref point: skip
|
||||
if( ( seg_startY <= refy ) && (seg_endY <= refy ) )
|
||||
continue;
|
||||
|
||||
/* at this point refy is between seg_startY and seg_endY
|
||||
* see if an horizontal line at Y = refy is intersecting this segment
|
||||
*/
|
||||
// calculate the x position of the intersection of this segment and the
|
||||
// infinite line this is more easier if we move the X,Y axis origin to
|
||||
// the segment start point:
|
||||
/* Trivial cases: skip if ref above or below the segment to test */
|
||||
if( ( seg_startY > refy ) && (seg_endY > refy ) )
|
||||
continue;
|
||||
|
||||
seg_endX -= seg_startX;
|
||||
seg_endY -= seg_startY;
|
||||
double newrefy = (double) ( refy - seg_startY );
|
||||
double intersec_x;
|
||||
// segment below ref point, or its Y end pos on Y coordinate ref point: skip
|
||||
if( ( seg_startY <= refy ) && (seg_endY <= refy ) )
|
||||
continue;
|
||||
|
||||
if ( seg_endY == 0 ) // horizontal segment on the same line: skip
|
||||
continue;
|
||||
/* at this point refy is between seg_startY and seg_endY
|
||||
* see if an horizontal line at Y = refy is intersecting this segment
|
||||
*/
|
||||
// calculate the x position of the intersection of this segment and the
|
||||
// infinite line this is more easier if we move the X,Y axis origin to
|
||||
// the segment start point:
|
||||
seg_endX -= seg_startX;
|
||||
seg_endY -= seg_startY;
|
||||
double newrefy = (double) ( refy - seg_startY );
|
||||
double intersec_x;
|
||||
|
||||
// Now calculate the x intersection coordinate of the horizontal line at
|
||||
// y = newrefy and the segment from (0,0) to (seg_endX,seg_endY) with the
|
||||
// horizontal line at the new refy position the line slope is:
|
||||
// slope = seg_endY/seg_endX; and inv_slope = seg_endX/seg_endY
|
||||
// and the x pos relative to the new origin is:
|
||||
// intersec_x = refy/slope = refy * inv_slope
|
||||
// Note: because horizontal segments are already tested and skipped, slope
|
||||
// exists (seg_end_y not O)
|
||||
double inv_slope = (double) seg_endX / seg_endY;
|
||||
intersec_x = newrefy * inv_slope;
|
||||
x_coordinates.push_back( (int) intersec_x + seg_startX );
|
||||
}
|
||||
if ( seg_endY == 0 ) // horizontal segment on the same line: skip
|
||||
continue;
|
||||
|
||||
// A line scan is finished: build list of segments
|
||||
// Now calculate the x intersection coordinate of the horizontal line at
|
||||
// y = newrefy and the segment from (0,0) to (seg_endX,seg_endY) with the
|
||||
// horizontal line at the new refy position the line slope is:
|
||||
// slope = seg_endY/seg_endX; and inv_slope = seg_endX/seg_endY
|
||||
// and the x pos relative to the new origin is:
|
||||
// intersec_x = refy/slope = refy * inv_slope
|
||||
// Note: because horizontal segments are already tested and skipped, slope
|
||||
// exists (seg_end_y not O)
|
||||
double inv_slope = (double) seg_endX / seg_endY;
|
||||
intersec_x = newrefy * inv_slope;
|
||||
x_coordinates.push_back( (int) intersec_x + seg_startX );
|
||||
}
|
||||
|
||||
// Sort intersection points by increasing x value:
|
||||
// So 2 consecutive points are the ends of a segment
|
||||
sort( x_coordinates.begin(), x_coordinates.end(), SortByXValues );
|
||||
// A line scan is finished: build list of segments
|
||||
|
||||
// Create segments
|
||||
// Sort intersection points by increasing x value:
|
||||
// So 2 consecutive points are the ends of a segment
|
||||
sort( x_coordinates.begin(), x_coordinates.end(), SortByXValues );
|
||||
|
||||
if( !error && ( x_coordinates.size() & 1 ) != 0 )
|
||||
{ // An even number of coordinates is expected, because a segment has 2 ends.
|
||||
// An if this algorithm always works, it must always find an even count.
|
||||
wxString msg = wxT( "Fill Zone: odd number of points at y = " );
|
||||
msg << refy;
|
||||
wxMessageBox( msg );
|
||||
error = true;
|
||||
}
|
||||
// Create segments
|
||||
|
||||
if ( !error && ( x_coordinates.size() & 1 ) != 0 )
|
||||
{ // An even number of coordinates is expected, because a segment has 2 ends.
|
||||
// An if this algorithm always works, it must always find an even count.
|
||||
wxString msg = wxT("Fill Zone: odd number of points at y = ");
|
||||
msg << refy;
|
||||
wxMessageBox(msg );
|
||||
error = true;
|
||||
}
|
||||
|
||||
if( error )
|
||||
break;
|
||||
|
||||
int iimax = x_coordinates.size() - 1;
|
||||
|
||||
for( int ii = 0; ii < iimax; ii +=2 )
|
||||
{
|
||||
wxPoint seg_start, seg_end;
|
||||
count++;
|
||||
seg_start.x = x_coordinates[ii];
|
||||
seg_start.y = refy;
|
||||
seg_end.x = x_coordinates[ii+1];
|
||||
seg_end.y = refy;
|
||||
SEGMENT segment( seg_start, seg_end );
|
||||
m_FillSegmList.push_back( segment );
|
||||
}
|
||||
} //End examine segments in one area
|
||||
|
||||
if( error )
|
||||
break;
|
||||
|
||||
int iimax = x_coordinates.size() - 1;
|
||||
|
||||
for( int ii = 0; ii < iimax; ii += 2 )
|
||||
{
|
||||
wxPoint seg_start, seg_end;
|
||||
count++;
|
||||
seg_start.x = x_coordinates[ii];
|
||||
seg_start.y = refy;
|
||||
seg_end.x = x_coordinates[ii + 1];
|
||||
seg_end.y = refy;
|
||||
SEGMENT segment( seg_start, seg_end );
|
||||
m_FillSegmList.push_back( segment );
|
||||
}
|
||||
} //End examine segments in one area
|
||||
istart = iend + 1; // istart points the first corner of the next area
|
||||
} // End find one end of outline
|
||||
|
||||
if( error )
|
||||
break;
|
||||
}
|
||||
} // End examine all areas
|
||||
|
||||
if( !error )
|
||||
m_IsFilled = true;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include <sstream>
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <polygons_defs.h>
|
||||
#include <wxPcbStruct.h>
|
||||
#include <trigo.h>
|
||||
|
||||
|
@ -69,7 +70,7 @@
|
|||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer,
|
||||
extern void BuildUnconnectedThermalStubsPolygonList( CPOLYGONS_LIST& aCornerBuffer,
|
||||
BOARD* aPcb, ZONE_CONTAINER* aZone,
|
||||
double aArcCorrection,
|
||||
double aRoundPadThermalRotation);
|
||||
|
@ -77,7 +78,7 @@ extern void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuff
|
|||
extern void Test_For_Copper_Island_And_Remove( BOARD* aPcb,
|
||||
ZONE_CONTAINER* aZone_container );
|
||||
|
||||
extern void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
extern void CreateThermalReliefPadPolygon( CPOLYGONS_LIST& aCornerBuffer,
|
||||
D_PAD& aPad,
|
||||
int aThermalGap,
|
||||
int aCopperThickness,
|
||||
|
@ -89,7 +90,7 @@ extern void CreateThermalReliefPadPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|||
// Local Variables:
|
||||
static double s_thermalRot = 450; // angle of stubs in thermal reliefs for round pads
|
||||
|
||||
void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeatures )
|
||||
void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, CPOLYGONS_LIST& aFeatures )
|
||||
{
|
||||
int segsPerCircle;
|
||||
double correctionFactor;
|
||||
|
@ -367,6 +368,83 @@ void ZONE_CONTAINER::buildFeatureHoleList( BOARD* aPcb, SHAPE_POLY_SET& aFeature
|
|||
|
||||
}
|
||||
|
||||
static const SHAPE_POLY_SET convertPolyListToPolySet(const CPOLYGONS_LIST& aList)
|
||||
{
|
||||
SHAPE_POLY_SET rv;
|
||||
|
||||
unsigned corners_count = aList.GetCornersCount();
|
||||
|
||||
// Enter main outline: this is the first contour
|
||||
unsigned ic = 0;
|
||||
|
||||
if(!corners_count)
|
||||
return rv;
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
rv.NewOutline( );
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
rv.AppendVertex( aList.GetX(ic), aList.GetY(ic) );
|
||||
if( aList.IsEndContour( ic ) )
|
||||
break;
|
||||
|
||||
ic++;
|
||||
}
|
||||
ic++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const CPOLYGONS_LIST convertPolySetToPolyList(const SHAPE_POLY_SET& aPolyset)
|
||||
{
|
||||
CPOLYGONS_LIST list;
|
||||
|
||||
CPolyPt corner, firstCorner;
|
||||
|
||||
for( int ii = 0; ii < aPolyset.OutlineCount(); ii++ )
|
||||
{
|
||||
|
||||
for( int jj = 0; jj < aPolyset.VertexCount(ii); jj++ )
|
||||
{
|
||||
VECTOR2I v = aPolyset.GetVertex( jj, ii );
|
||||
|
||||
|
||||
corner.x = v.x;
|
||||
corner.y = v.y;
|
||||
corner.end_contour = false;
|
||||
|
||||
if(!jj)
|
||||
firstCorner = corner;
|
||||
|
||||
list.AddCorner( corner );
|
||||
}
|
||||
|
||||
firstCorner.end_contour = true;
|
||||
list.AddCorner( firstCorner );
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
static const SHAPE_POLY_SET convertBoostToPolySet ( const KI_POLYGON_SET& aSet )
|
||||
{
|
||||
SHAPE_POLY_SET rv;
|
||||
|
||||
BOOST_FOREACH ( const KI_POLYGON &poly, aSet )
|
||||
{
|
||||
rv.NewOutline();
|
||||
for ( KI_POLYGON::iterator_type corner = poly.begin(); corner != poly.end(); ++ corner )
|
||||
{
|
||||
rv.AppendVertex ( corner->x(), corner->y() );
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function AddClearanceAreasPolygonsToPolysList
|
||||
|
@ -424,18 +502,16 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb )
|
|||
if(g_DumpZonesWhenFilling)
|
||||
dumper->BeginGroup("clipper-zone");
|
||||
|
||||
SHAPE_POLY_SET solidAreas = ConvertPolyListToPolySet( m_smoothedPoly->m_CornersList );
|
||||
|
||||
solidAreas.Inflate( -outline_half_thickness, segsPerCircle );
|
||||
solidAreas.Simplify();
|
||||
|
||||
SHAPE_POLY_SET holes;
|
||||
m_smoothedPoly->m_CornersList.InflateOutline( tmp, -outline_half_thickness, true );
|
||||
SHAPE_POLY_SET solidAreas = convertPolyListToPolySet( tmp );
|
||||
|
||||
if(g_DumpZonesWhenFilling)
|
||||
dumper->Write( &solidAreas, "solid-areas" );
|
||||
|
||||
|
||||
tmp.RemoveAllContours();
|
||||
buildFeatureHoleList( aPcb, holes );
|
||||
buildFeatureHoleList( aPcb, tmp );
|
||||
SHAPE_POLY_SET holes = convertPolyListToPolySet( tmp );
|
||||
|
||||
if(g_DumpZonesWhenFilling)
|
||||
dumper->Write( &holes, "feature-holes" );
|
||||
|
@ -445,49 +521,61 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb )
|
|||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write( &holes, "feature-holes-postsimplify" );
|
||||
|
||||
solidAreas.BooleanSubtract( holes );
|
||||
solidAreas.Subtract( holes );
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write( &solidAreas, "solid-areas-minus-holes" );
|
||||
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
|
||||
SHAPE_POLY_SET fractured = solidAreas;
|
||||
fractured.Fracture();
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write( &fractured, "fractured" );
|
||||
|
||||
m_FilledPolysList = fractured;
|
||||
m_FilledPolysList = convertPolySetToPolyList( fractured );
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
{
|
||||
SHAPE_POLY_SET dupa = convertPolyListToPolySet ( m_FilledPolysList );
|
||||
dumper->Write( &dupa, "verify-conv" );
|
||||
}
|
||||
|
||||
|
||||
// Remove insulated islands:
|
||||
if( GetNetCode() > 0 )
|
||||
TestForCopperIslandAndRemoveInsulatedIslands( aPcb );
|
||||
|
||||
SHAPE_POLY_SET thermalHoles;
|
||||
|
||||
tmp.RemoveAllContours();
|
||||
// Test thermal stubs connections and add polygons to remove unconnected stubs.
|
||||
// (this is a refinement for thermal relief shapes)
|
||||
if( GetNetCode() > 0 )
|
||||
BuildUnconnectedThermalStubsPolygonList( thermalHoles, aPcb, this,
|
||||
BuildUnconnectedThermalStubsPolygonList( tmp, aPcb, this,
|
||||
correctionFactor, s_thermalRot );
|
||||
|
||||
// remove copper areas corresponding to not connected stubs
|
||||
if( !thermalHoles.IsEmpty() )
|
||||
if( tmp.GetCornersCount() )
|
||||
{
|
||||
SHAPE_POLY_SET thermalHoles = convertPolyListToPolySet ( tmp );
|
||||
thermalHoles.Simplify();
|
||||
// Remove unconnected stubs
|
||||
solidAreas.BooleanSubtract( thermalHoles );
|
||||
solidAreas.Subtract ( thermalHoles );
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write ( &thermalHoles, "thermal-holes" );
|
||||
|
||||
if( g_DumpZonesWhenFilling )
|
||||
dumper->Write( &thermalHoles, "thermal-holes" );
|
||||
|
||||
// put these areas in m_FilledPolysList
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
|
||||
SHAPE_POLY_SET fractured = solidAreas;
|
||||
fractured.Fracture();
|
||||
|
||||
if( g_DumpZonesWhenFilling )
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write ( &fractured, "fractured" );
|
||||
|
||||
m_FilledPolysList = fractured;
|
||||
m_FilledPolysList = convertPolySetToPolyList( fractured );
|
||||
|
||||
if( GetNetCode() > 0 )
|
||||
TestForCopperIslandAndRemoveInsulatedIslands( aPcb );
|
||||
|
@ -499,4 +587,121 @@ void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList_NG( BOARD* aPcb )
|
|||
|
||||
void ZONE_CONTAINER::AddClearanceAreasPolygonsToPolysList( BOARD* aPcb )
|
||||
{
|
||||
int segsPerCircle;
|
||||
double correctionFactor;
|
||||
|
||||
std::auto_ptr<SHAPE_FILE_IO> dumper( new SHAPE_FILE_IO( "zones_dump.txt", true ) );
|
||||
|
||||
|
||||
if(g_DumpZonesWhenFilling)
|
||||
dumper->BeginGroup("boost-zone");
|
||||
|
||||
// Set the number of segments in arc approximations
|
||||
if( m_ArcToSegmentsCount == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF )
|
||||
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;
|
||||
else
|
||||
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;
|
||||
|
||||
/* calculates the coeff to compensate radius reduction of holes clearance
|
||||
* due to the segment approx.
|
||||
* For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
|
||||
* s_Correction is 1 /cos( PI/s_CircleToSegmentsCount )
|
||||
*/
|
||||
correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );
|
||||
|
||||
// this is a place to store holes (i.e. tracks, pads ... areas as polygons outlines)
|
||||
// static to avoid unnecessary memory allocation when filling many zones.
|
||||
CPOLYGONS_LIST cornerBufferPolysToSubstract;
|
||||
|
||||
// This KI_POLYGON_SET is the area(s) to fill, with m_ZoneMinThickness/2
|
||||
KI_POLYGON_SET polyset_zone_solid_areas;
|
||||
|
||||
|
||||
|
||||
int outline_half_thickness = m_ZoneMinThickness / 2;
|
||||
|
||||
/* First, creates the main polygon (i.e. the filled area using only one outline)
|
||||
* to reserve a m_ZoneMinThickness/2 margin around the outlines and holes
|
||||
* this margin is the room to redraw outlines with segments having a width set to
|
||||
* m_ZoneMinThickness
|
||||
* so m_ZoneMinThickness is the min thickness of the filled zones areas
|
||||
* the main polygon is stored in polyset_zone_solid_areas
|
||||
*/
|
||||
CPOLYGONS_LIST tmp;
|
||||
m_smoothedPoly->m_CornersList.InflateOutline( tmp, -outline_half_thickness, true );
|
||||
tmp.ExportTo( polyset_zone_solid_areas );
|
||||
|
||||
if( polyset_zone_solid_areas.size() == 0 )
|
||||
return;
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write ( convertBoostToPolySet( polyset_zone_solid_areas ), "solid-areas" );
|
||||
|
||||
buildFeatureHoleList( aPcb, cornerBufferPolysToSubstract );
|
||||
|
||||
// cornerBufferPolysToSubstract contains polygons to substract.
|
||||
// polyset_zone_solid_areas contains the main filled area
|
||||
// Calculate now actual solid areas
|
||||
if( cornerBufferPolysToSubstract.GetCornersCount() > 0 )
|
||||
{
|
||||
KI_POLYGON_SET polyset_holes;
|
||||
|
||||
cornerBufferPolysToSubstract.ExportTo( polyset_holes );
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write ( convertBoostToPolySet( polyset_holes ), "feature-holes" );
|
||||
|
||||
// Remove holes from initial area.:
|
||||
polyset_zone_solid_areas -= polyset_holes;
|
||||
}
|
||||
|
||||
// put solid areas in m_FilledPolysList:
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas );
|
||||
|
||||
// Remove insulated islands:
|
||||
if( GetNetCode() > 0 )
|
||||
TestForCopperIslandAndRemoveInsulatedIslands( aPcb );
|
||||
|
||||
// Now we remove all unused thermal stubs.
|
||||
cornerBufferPolysToSubstract.RemoveAllContours();
|
||||
|
||||
// Test thermal stubs connections and add polygons to remove unconnected stubs.
|
||||
// (this is a refinement for thermal relief shapes)
|
||||
if( GetNetCode() > 0 )
|
||||
BuildUnconnectedThermalStubsPolygonList( cornerBufferPolysToSubstract, aPcb, this,
|
||||
correctionFactor, s_thermalRot );
|
||||
|
||||
// remove copper areas corresponding to not connected stubs
|
||||
if( cornerBufferPolysToSubstract.GetCornersCount() )
|
||||
{
|
||||
KI_POLYGON_SET polyset_holes;
|
||||
cornerBufferPolysToSubstract.ExportTo( polyset_holes );
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write ( convertBoostToPolySet( polyset_holes ), "thermal-holes" );
|
||||
|
||||
// Remove unconnected stubs
|
||||
polyset_zone_solid_areas -= polyset_holes;
|
||||
|
||||
// put these areas in m_FilledPolysList
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
CopyPolygonsFromKiPolygonListToFilledPolysList( polyset_zone_solid_areas );
|
||||
|
||||
if( GetNetCode() > 0 )
|
||||
TestForCopperIslandAndRemoveInsulatedIslands( aPcb );
|
||||
}
|
||||
|
||||
|
||||
if (g_DumpZonesWhenFilling)
|
||||
dumper->Write ( convertBoostToPolySet( polyset_zone_solid_areas ), "complete" );
|
||||
|
||||
cornerBufferPolysToSubstract.RemoveAllContours();
|
||||
}
|
||||
|
||||
|
||||
void ZONE_CONTAINER::CopyPolygonsFromKiPolygonListToFilledPolysList( KI_POLYGON_SET& aKiPolyList )
|
||||
{
|
||||
m_FilledPolysList.RemoveAllContours();
|
||||
m_FilledPolysList.ImportFrom( aKiPolyList );
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
*/
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <polygons_defs.h>
|
||||
#include <PolyLine.h>
|
||||
#include <wxPcbStruct.h>
|
||||
#include <trigo.h>
|
||||
|
@ -47,10 +48,11 @@
|
|||
* false to create the outline polygon.
|
||||
*/
|
||||
void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon(
|
||||
SHAPE_POLY_SET& aCornerBuffer, int aMinClearanceValue, bool aUseNetClearance )
|
||||
CPOLYGONS_LIST& aCornerBuffer, int aMinClearanceValue, bool aUseNetClearance )
|
||||
{
|
||||
// Creates the zone outline polygon (with linked holes if any)
|
||||
BuildFilledSolidAreasPolygons( NULL, &aCornerBuffer );
|
||||
CPOLYGONS_LIST zoneOutline;
|
||||
BuildFilledSolidAreasPolygons( NULL, &zoneOutline );
|
||||
|
||||
// add clearance to outline
|
||||
int clearance = aMinClearanceValue;
|
||||
|
@ -65,9 +67,9 @@ void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon(
|
|||
// Calculate the polygon with clearance
|
||||
// holes are linked to the main outline, so only one polygon is created.
|
||||
if( clearance )
|
||||
aCornerBuffer.Inflate( clearance, 16 );
|
||||
|
||||
aCornerBuffer.Fracture( );
|
||||
zoneOutline.InflateOutline( aCornerBuffer, clearance, true );
|
||||
else
|
||||
ConvertPolysListWithHolesToOnePolygon( zoneOutline, aCornerBuffer );
|
||||
}
|
||||
|
||||
|
||||
|
@ -76,14 +78,14 @@ void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon(
|
|||
* Function BuildUnconnectedThermalStubsPolygonList
|
||||
* Creates a set of polygons corresponding to stubs created by thermal shapes on pads
|
||||
* which are not connected to a zone (dangling bridges)
|
||||
* @param aCornerBuffer = a SHAPE_POLY_SET where to store polygons
|
||||
* @param aCornerBuffer = a CPOLYGONS_LIST where to store polygons
|
||||
* @param aPcb = the board.
|
||||
* @param aZone = a pointer to the ZONE_CONTAINER to examine.
|
||||
* @param aArcCorrection = a pointer to the ZONE_CONTAINER to examine.
|
||||
* @param aRoundPadThermalRotation = the rotation in 1.0 degree for thermal stubs in round pads
|
||||
*/
|
||||
|
||||
void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer,
|
||||
void BuildUnconnectedThermalStubsPolygonList( CPOLYGONS_LIST& aCornerBuffer,
|
||||
BOARD* aPcb,
|
||||
ZONE_CONTAINER* aZone,
|
||||
double aArcCorrection,
|
||||
|
@ -233,7 +235,6 @@ void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer,
|
|||
break;
|
||||
}
|
||||
|
||||
aCornerBuffer.NewOutline();
|
||||
|
||||
// add computed polygon to list
|
||||
for( unsigned ic = 0; ic < corners_buffer.size(); ic++ )
|
||||
|
@ -241,7 +242,11 @@ void BuildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer,
|
|||
wxPoint cpos = corners_buffer[ic];
|
||||
RotatePoint( &cpos, fAngle ); // Rotate according to module orientation
|
||||
cpos += pad->ShapePos(); // Shift origin to position
|
||||
aCornerBuffer.Append( cpos.x, cpos.y );
|
||||
CPolyPt corner;
|
||||
corner.x = cpos.x;
|
||||
corner.y = cpos.y;
|
||||
corner.end_contour = ( ic < (corners_buffer.size() - 1) ) ? false : true;
|
||||
aCornerBuffer.Append( corner );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
void ZONE_CONTAINER::TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb )
|
||||
{
|
||||
if( m_FilledPolysList.IsEmpty() )
|
||||
if( m_FilledPolysList.GetCornersCount() == 0 )
|
||||
return;
|
||||
|
||||
// Build a list of points connected to the net:
|
||||
|
@ -76,27 +76,74 @@ void ZONE_CONTAINER::TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb )
|
|||
}
|
||||
|
||||
// test if a point is inside
|
||||
unsigned indexstart = 0, indexend;
|
||||
bool connected = false;
|
||||
|
||||
for( int outline = 0; outline < m_FilledPolysList.OutlineCount(); outline++ )
|
||||
for( indexend = 0; indexend < m_FilledPolysList.GetCornersCount(); indexend++ )
|
||||
{
|
||||
bool connected = false;
|
||||
|
||||
for( unsigned ic = 0; ic < listPointsCandidates.size(); ic++ )
|
||||
if( m_FilledPolysList[indexend].end_contour ) // end of a filled sub-area found
|
||||
{
|
||||
// test if this area is connected to a board item:
|
||||
wxPoint pos = listPointsCandidates[ic];
|
||||
EDA_RECT bbox = CalculateSubAreaBoundaryBox( indexstart, indexend );
|
||||
|
||||
if( m_FilledPolysList.Contains( VECTOR2I( pos.x, pos.y ), outline ) )
|
||||
for( unsigned ic = 0; ic < listPointsCandidates.size(); ic++ )
|
||||
{
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// test if this area is connected to a board item:
|
||||
wxPoint pos = listPointsCandidates[ic];
|
||||
|
||||
if( !connected ) // this polygon is connected: analyse next polygon
|
||||
{
|
||||
m_FilledPolysList.DeletePolygon( outline );
|
||||
outline--;
|
||||
if( !bbox.Contains( pos ) )
|
||||
continue;
|
||||
|
||||
if( TestPointInsidePolygon( m_FilledPolysList, indexstart, indexend,
|
||||
pos.x, pos.y ) )
|
||||
{
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( connected ) // this polygon is connected: analyse next polygon
|
||||
{
|
||||
indexstart = indexend + 1; // indexstart points the first point of the next polygon
|
||||
connected = false;
|
||||
}
|
||||
else // Not connected: remove this polygon
|
||||
{
|
||||
m_FilledPolysList.DeleteCorners( indexstart, indexend );
|
||||
indexend = indexstart; /* indexstart points the first point of the next polygon
|
||||
* because the current poly is removed */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EDA_RECT ZONE_CONTAINER::CalculateSubAreaBoundaryBox( int aIndexStart, int aIndexEnd )
|
||||
{
|
||||
CPolyPt start_point, end_point;
|
||||
EDA_RECT bbox;
|
||||
|
||||
start_point = m_FilledPolysList[aIndexStart];
|
||||
end_point = start_point;
|
||||
|
||||
for( int ii = aIndexStart; ii <= aIndexEnd; ii++ )
|
||||
{
|
||||
CPolyPt ptst = m_FilledPolysList[ii];
|
||||
|
||||
if( start_point.x > ptst.x )
|
||||
start_point.x = ptst.x;
|
||||
|
||||
if( start_point.y > ptst.y )
|
||||
start_point.y = ptst.y;
|
||||
|
||||
if( end_point.x < ptst.x )
|
||||
end_point.x = ptst.x;
|
||||
|
||||
if( end_point.y < ptst.y )
|
||||
end_point.y = ptst.y;
|
||||
}
|
||||
|
||||
bbox.SetOrigin( start_point.x, start_point.y );
|
||||
bbox.SetEnd( wxPoint( end_point.x, end_point.y ) );
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode );
|
|||
bool sort_areas( const ZONE_CONTAINER* ref, const ZONE_CONTAINER* tst )
|
||||
{
|
||||
if( ref->GetNetCode() == tst->GetNetCode() )
|
||||
return ref->GetFilledPolysList().TotalVertices() <
|
||||
tst->GetFilledPolysList().TotalVertices();
|
||||
return ref->GetFilledPolysList().GetCornersCount() <
|
||||
tst->GetFilledPolysList().GetCornersCount();
|
||||
else
|
||||
return ref->GetNetCode() < tst->GetNetCode();
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ void BOARD::Test_Connections_To_Copper_Areas( int aNetcode )
|
|||
if( aNetcode >= 0 && aNetcode != zone->GetNetCode() )
|
||||
continue;
|
||||
|
||||
if( zone->GetFilledPolysList().IsEmpty() )
|
||||
if( zone->GetFilledPolysList().GetCornersCount() == 0 )
|
||||
continue;
|
||||
|
||||
zones_candidates.push_back( zone );
|
||||
|
@ -156,11 +156,16 @@ void BOARD::Test_Connections_To_Copper_Areas( int aNetcode )
|
|||
}
|
||||
|
||||
// test if a candidate is inside a filled area of this zone
|
||||
const SHAPE_POLY_SET& polysList = zone->GetFilledPolysList();
|
||||
unsigned indexstart = 0, indexend;
|
||||
const CPOLYGONS_LIST& polysList = zone->GetFilledPolysList();
|
||||
|
||||
for( int outline = 0; outline < polysList.OutlineCount(); outline++ )
|
||||
for( indexend = 0; indexend < polysList.GetCornersCount(); indexend++ )
|
||||
{
|
||||
// end of a filled sub-area found
|
||||
if( polysList.IsEndContour( indexend ) )
|
||||
{
|
||||
subnet++;
|
||||
EDA_RECT bbox = zone->CalculateSubAreaBoundaryBox( indexstart, indexend );
|
||||
|
||||
for( unsigned ic = 0; ic < candidates.size(); ic++ )
|
||||
{
|
||||
|
@ -202,14 +207,22 @@ void BOARD::Test_Connections_To_Copper_Areas( int aNetcode )
|
|||
|
||||
bool connected = false;
|
||||
|
||||
if( polysList.Contains( VECTOR2I( pos1.x, pos1.y ), outline ) )
|
||||
connected = true;
|
||||
|
||||
if( !connected && ( pos1 != pos2 ) )
|
||||
if( bbox.Contains( pos1 ) )
|
||||
{
|
||||
if( polysList.Contains( VECTOR2I( pos2.x, pos2.y ), outline ) )
|
||||
if( TestPointInsidePolygon( polysList, indexstart,
|
||||
indexend, pos1.x, pos1.y ) )
|
||||
connected = true;
|
||||
}
|
||||
if( !connected && (pos1 != pos2 ) )
|
||||
{
|
||||
if( bbox.Contains( pos2 ) )
|
||||
{
|
||||
if( TestPointInsidePolygon( polysList,
|
||||
indexstart, indexend,
|
||||
pos2.x, pos2.y ) )
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( connected )
|
||||
{
|
||||
|
@ -234,7 +247,13 @@ void BOARD::Test_Connections_To_Copper_Areas( int aNetcode )
|
|||
} // End if ( old_subnet > 0 )
|
||||
} // End if( connected )
|
||||
}
|
||||
}
|
||||
|
||||
// End test candidates for the current filled area
|
||||
indexstart = indexend + 1; // prepare test next area, starting at indexend+1
|
||||
// (if exists). End read one area in
|
||||
// zone->m_FilledPolysList
|
||||
}
|
||||
} // End read all segments in zone
|
||||
} // End read all zones candidates
|
||||
}
|
||||
|
||||
|
|
|
@ -290,26 +290,62 @@ bool BOARD::CombineAreas( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_
|
|||
return false;
|
||||
}
|
||||
|
||||
SHAPE_POLY_SET mergedOutlines = ConvertPolyListToPolySet( area_ref->Outline()->m_CornersList );
|
||||
SHAPE_POLY_SET areaToMergePoly = ConvertPolyListToPolySet( area_to_combine->Outline()->m_CornersList );
|
||||
// polygons intersect, combine them
|
||||
KI_POLYGON_WITH_HOLES areaRefPoly;
|
||||
KI_POLYGON_WITH_HOLES areaToMergePoly;
|
||||
area_ref->Outline()->m_CornersList.ExportTo( areaRefPoly );
|
||||
area_to_combine->Outline()->m_CornersList.ExportTo( areaToMergePoly );
|
||||
|
||||
mergedOutlines.BooleanAdd( areaToMergePoly );
|
||||
mergedOutlines.Simplify();
|
||||
KI_POLYGON_WITH_HOLES_SET mergedOutlines;
|
||||
mergedOutlines.push_back( areaRefPoly );
|
||||
mergedOutlines |= areaToMergePoly;
|
||||
|
||||
// We should have one polygon with hole
|
||||
// We can have 2 polygons with hole, if the 2 initial polygons have only one common corner
|
||||
// and therefore cannot be merged (they are dectected as intersecting)
|
||||
// but we should never have more than 2 polys
|
||||
if( mergedOutlines.OutlineCount() > 2 )
|
||||
if( mergedOutlines.size() > 2 )
|
||||
{
|
||||
wxLogMessage(wxT("BOARD::CombineAreas error: more than 2 polys after merging") );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( mergedOutlines.OutlineCount() > 1 )
|
||||
if( mergedOutlines.size() > 1 )
|
||||
return false;
|
||||
|
||||
area_ref->Outline()->m_CornersList = ConvertPolySetToPolyList( mergedOutlines );
|
||||
areaRefPoly = mergedOutlines[0];
|
||||
area_ref->Outline()->RemoveAllContours();
|
||||
|
||||
KI_POLYGON_WITH_HOLES::iterator_type corner = areaRefPoly.begin();
|
||||
|
||||
// create area with external contour: Recreate only area edges, NOT holes
|
||||
area_ref->Outline()->Start( area_ref->GetLayer(), corner->x(), corner->y(),
|
||||
area_ref->Outline()->GetHatchStyle() );
|
||||
|
||||
while( ++corner != areaRefPoly.end() )
|
||||
{
|
||||
area_ref->Outline()->AppendCorner( corner->x(), corner->y() );
|
||||
}
|
||||
|
||||
area_ref->Outline()->CloseLastContour();
|
||||
|
||||
// add holes (set of polygons)
|
||||
KI_POLYGON_WITH_HOLES::iterator_holes_type hole = areaRefPoly.begin_holes();
|
||||
|
||||
while( hole != areaRefPoly.end_holes() )
|
||||
{
|
||||
KI_POLYGON::iterator_type hole_corner = hole->begin();
|
||||
|
||||
// create area with external contour: Recreate only area edges, NOT holes
|
||||
while( hole_corner != hole->end() )
|
||||
{
|
||||
area_ref->Outline()->AppendCorner( hole_corner->x(), hole_corner->y() );
|
||||
hole_corner++;
|
||||
}
|
||||
|
||||
area_ref->Outline()->CloseLastContour();
|
||||
hole++;
|
||||
}
|
||||
|
||||
RemoveArea( aDeletedList, area_to_combine );
|
||||
|
||||
|
|
|
@ -9,6 +9,12 @@ set(POLYGON_SRCS
|
|||
PolyLine.cpp
|
||||
polygon_test_point_inside.cpp
|
||||
clipper.cpp
|
||||
|
||||
poly2tri/common/shapes.cc
|
||||
poly2tri/sweep/sweep.cc
|
||||
poly2tri/sweep/cdt.cc
|
||||
poly2tri/sweep/advancing_front.cc
|
||||
poly2tri/sweep/sweep_context.cc
|
||||
)
|
||||
|
||||
add_library(polygon STATIC ${POLYGON_SRCS})
|
||||
|
|
|
@ -115,19 +115,85 @@ CPolyLine::~CPolyLine()
|
|||
* @return the polygon count (always >= 1, because there is at least one polygon)
|
||||
* There are new polygons only if the polygon count is > 1
|
||||
*/
|
||||
#include "clipper.hpp"
|
||||
int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
|
||||
{
|
||||
ClipperLib::Path raw_polygon;
|
||||
ClipperLib::Paths normalized_polygons;
|
||||
|
||||
SHAPE_POLY_SET polySet = ConvertPolyListToPolySet( m_CornersList );
|
||||
unsigned corners_count = m_CornersList.GetCornersCount();
|
||||
|
||||
polySet.Simplify();
|
||||
KI_POLYGON_SET polysholes;
|
||||
KI_POLYGON_WITH_HOLES mainpoly;
|
||||
std::vector<KI_POLY_POINT> cornerslist;
|
||||
KI_POLYGON_WITH_HOLES_SET all_contours;
|
||||
KI_POLYGON poly_tmp;
|
||||
|
||||
// Normalize first contour
|
||||
unsigned ic = 0;
|
||||
while( ic < corners_count )
|
||||
{
|
||||
const CPolyPt& corner = m_CornersList[ic++];
|
||||
raw_polygon.push_back( ClipperLib::IntPoint( corner.x, corner.y ) );
|
||||
|
||||
if( corner.end_contour )
|
||||
break;
|
||||
}
|
||||
|
||||
ClipperLib::SimplifyPolygon( raw_polygon, normalized_polygons );
|
||||
|
||||
// enter main outline
|
||||
for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ )
|
||||
{
|
||||
ClipperLib::Path& polygon = normalized_polygons[ii];
|
||||
cornerslist.clear();
|
||||
for( unsigned jj = 0; jj < polygon.size(); jj++ )
|
||||
cornerslist.push_back( KI_POLY_POINT( KiROUND( polygon[jj].X ),
|
||||
KiROUND( polygon[jj].Y ) ) );
|
||||
mainpoly.set( cornerslist.begin(), cornerslist.end() );
|
||||
all_contours.push_back( mainpoly );
|
||||
}
|
||||
|
||||
// Enter holes
|
||||
while( ic < corners_count )
|
||||
{
|
||||
cornerslist.clear();
|
||||
raw_polygon.clear();
|
||||
normalized_polygons.clear();
|
||||
|
||||
// Normalize current hole and add it to hole list
|
||||
while( ic < corners_count )
|
||||
{
|
||||
const CPolyPt& corner = m_CornersList[ic++];
|
||||
raw_polygon.push_back( ClipperLib::IntPoint( corner.x, corner.y ) );
|
||||
|
||||
if( corner.end_contour )
|
||||
{
|
||||
ClipperLib::SimplifyPolygon( raw_polygon, normalized_polygons );
|
||||
for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ )
|
||||
{
|
||||
ClipperLib::Path& polygon = normalized_polygons[ii];
|
||||
cornerslist.clear();
|
||||
for( unsigned jj = 0; jj < polygon.size(); jj++ )
|
||||
cornerslist.push_back( KI_POLY_POINT( KiROUND( polygon[jj].X ),
|
||||
KiROUND( polygon[jj].Y ) ) );
|
||||
bpl::set_points( poly_tmp, cornerslist.begin(), cornerslist.end() );
|
||||
polysholes.push_back( poly_tmp );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
all_contours -= polysholes;
|
||||
|
||||
// copy polygon with holes to destination
|
||||
RemoveAllContours();
|
||||
|
||||
for( int ii = 0; ii < polySet.OutlineCount(); ii++ )
|
||||
#define outlines all_contours
|
||||
|
||||
for( unsigned ii = 0; ii < outlines.size(); ii++ )
|
||||
{
|
||||
CPolyLine* polyline = this;
|
||||
|
||||
if( ii > 0 )
|
||||
{
|
||||
polyline = new CPolyLine;
|
||||
|
@ -135,14 +201,35 @@ int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
|
|||
aNewPolygonList->push_back( polyline );
|
||||
}
|
||||
|
||||
SHAPE_POLY_SET pnew;
|
||||
pnew.NewOutline();
|
||||
pnew.Polygon( 0 ) = polySet.CPolygon( ii );
|
||||
KI_POLYGON_WITH_HOLES& curr_poly = outlines[ii];
|
||||
KI_POLYGON_WITH_HOLES::iterator_type corner = curr_poly.begin();
|
||||
// enter main contour
|
||||
while( corner != curr_poly.end() )
|
||||
{
|
||||
polyline->AppendCorner( corner->x(), corner->y() );
|
||||
corner++;
|
||||
}
|
||||
polyline->CloseLastContour();
|
||||
|
||||
polyline->m_CornersList = ConvertPolySetToPolyList( pnew );
|
||||
// add holes (set of polygons)
|
||||
KI_POLYGON_WITH_HOLES::iterator_holes_type hole = curr_poly.begin_holes();
|
||||
while( hole != curr_poly.end_holes() )
|
||||
{
|
||||
KI_POLYGON::iterator_type hole_corner = hole->begin();
|
||||
// create area with external contour: Recreate only area edges, NOT holes
|
||||
while( hole_corner != hole->end() )
|
||||
{
|
||||
polyline->AppendCorner( hole_corner->x(), hole_corner->y() );
|
||||
hole_corner++;
|
||||
}
|
||||
polyline->CloseLastContour();
|
||||
hole++;
|
||||
}
|
||||
|
||||
polyline->RemoveNullSegments();
|
||||
}
|
||||
|
||||
return polySet.OutlineCount();
|
||||
return outlines.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1173,6 +1260,375 @@ int CPolyLine::HitTestForCorner( const wxPoint& aPos, int aDistMax ) const
|
|||
return corner;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the contours to a KI_POLYGON_WITH_HOLES
|
||||
* The first contour is the main outline, others are holes
|
||||
*/
|
||||
void CPOLYGONS_LIST::ExportTo( KI_POLYGON_WITH_HOLES& aPolygoneWithHole ) const
|
||||
{
|
||||
unsigned corners_count = m_cornersList.size();
|
||||
|
||||
std::vector<KI_POLY_POINT> cornerslist;
|
||||
KI_POLYGON poly;
|
||||
|
||||
// Enter main outline: this is the first contour
|
||||
unsigned ic = 0;
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
const CPolyPt& corner = GetCorner( ic++ );
|
||||
cornerslist.push_back( KI_POLY_POINT( corner.x, corner.y ) );
|
||||
|
||||
if( corner.end_contour )
|
||||
break;
|
||||
}
|
||||
|
||||
aPolygoneWithHole.set( cornerslist.begin(), cornerslist.end() );
|
||||
|
||||
// Enter holes: they are next contours (when exist)
|
||||
if( ic < corners_count )
|
||||
{
|
||||
KI_POLYGON_SET holePolyList;
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
cornerslist.clear();
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
cornerslist.push_back( KI_POLY_POINT( GetX( ic ), GetY( ic ) ) );
|
||||
|
||||
if( IsEndContour( ic++ ) )
|
||||
break;
|
||||
}
|
||||
|
||||
bpl::set_points( poly, cornerslist.begin(), cornerslist.end() );
|
||||
holePolyList.push_back( poly );
|
||||
}
|
||||
|
||||
aPolygoneWithHole.set_holes( holePolyList.begin(), holePolyList.end() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all contours to a KI_POLYGON_SET aPolygons
|
||||
* Each contour is copied into a KI_POLYGON, and each KI_POLYGON
|
||||
* is append to aPolygons
|
||||
*/
|
||||
void CPOLYGONS_LIST::ExportTo( KI_POLYGON_SET& aPolygons ) const
|
||||
{
|
||||
std::vector<KI_POLY_POINT> cornerslist;
|
||||
unsigned corners_count = GetCornersCount();
|
||||
|
||||
// Count the number of polygons in aCornersBuffer
|
||||
int polycount = 0;
|
||||
|
||||
for( unsigned ii = 0; ii < corners_count; ii++ )
|
||||
{
|
||||
if( IsEndContour( ii ) )
|
||||
polycount++;
|
||||
}
|
||||
|
||||
aPolygons.reserve( polycount );
|
||||
|
||||
for( unsigned icnt = 0; icnt < corners_count; )
|
||||
{
|
||||
KI_POLYGON poly;
|
||||
cornerslist.clear();
|
||||
|
||||
unsigned ii;
|
||||
|
||||
for( ii = icnt; ii < corners_count; ii++ )
|
||||
{
|
||||
cornerslist.push_back( KI_POLY_POINT( GetX( ii ), GetY( ii ) ) );
|
||||
|
||||
if( IsEndContour( ii ) )
|
||||
break;
|
||||
}
|
||||
|
||||
bpl::set_points( poly, cornerslist.begin(), cornerslist.end() );
|
||||
aPolygons.push_back( poly );
|
||||
icnt = ii + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy all contours to a ClipperLib::Paths& aPolygons
|
||||
* Each contour is copied into a ClipperLib::Path, and each ClipperLib::Path
|
||||
* is append to aPolygons
|
||||
*/
|
||||
void CPOLYGONS_LIST::ExportTo( ClipperLib::Paths& aPolygons ) const
|
||||
{
|
||||
unsigned corners_count = GetCornersCount();
|
||||
|
||||
// Count the number of polygons in aCornersBuffer
|
||||
int polycount = 0;
|
||||
|
||||
for( unsigned ii = 0; ii < corners_count; ii++ )
|
||||
{
|
||||
if( IsEndContour( ii ) )
|
||||
polycount++;
|
||||
}
|
||||
|
||||
aPolygons.reserve( polycount );
|
||||
|
||||
for( unsigned icnt = 0; icnt < corners_count; )
|
||||
{
|
||||
ClipperLib::Path poly;
|
||||
unsigned ii;
|
||||
|
||||
for( ii = icnt; ii < corners_count; ii++ )
|
||||
{
|
||||
poly << ClipperLib::IntPoint( GetX( ii ), GetY( ii ) );
|
||||
|
||||
if( IsEndContour( ii ) )
|
||||
break;
|
||||
}
|
||||
|
||||
aPolygons.push_back( poly );
|
||||
icnt = ii + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Imports all polygons found in a KI_POLYGON_SET in list
|
||||
*/
|
||||
void CPOLYGONS_LIST::ImportFrom( KI_POLYGON_SET& aPolygons )
|
||||
{
|
||||
CPolyPt corner;
|
||||
|
||||
for( unsigned ii = 0; ii < aPolygons.size(); ii++ )
|
||||
{
|
||||
KI_POLYGON& poly = aPolygons[ii];
|
||||
|
||||
for( unsigned jj = 0; jj < poly.size(); jj++ )
|
||||
{
|
||||
KI_POLY_POINT point = *(poly.begin() + jj);
|
||||
corner.x = point.x();
|
||||
corner.y = point.y();
|
||||
corner.end_contour = false;
|
||||
AddCorner( corner );
|
||||
}
|
||||
|
||||
CloseLastContour();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Imports all polygons found in a ClipperLib::Paths in list
|
||||
*/
|
||||
void CPOLYGONS_LIST::ImportFrom( ClipperLib::Paths& aPolygons )
|
||||
{
|
||||
CPolyPt corner;
|
||||
|
||||
for( unsigned ii = 0; ii < aPolygons.size(); ii++ )
|
||||
{
|
||||
ClipperLib::Path& polygon = aPolygons[ii];
|
||||
|
||||
for( unsigned jj = 0; jj < polygon.size(); jj++ )
|
||||
{
|
||||
corner.x = int( polygon[jj].X );
|
||||
corner.y = int( polygon[jj].Y );
|
||||
corner.end_contour = false;
|
||||
AddCorner( corner );
|
||||
}
|
||||
|
||||
CloseLastContour();
|
||||
}
|
||||
}
|
||||
|
||||
/* Inflate the outline stored in m_cornersList.
|
||||
* The first polygon is the external outline. It is inflated
|
||||
* The other polygons are holes. they are deflated
|
||||
* aResult = the Inflated outline
|
||||
* aInflateValue = the Inflate value. when < 0, this is a deflate transform
|
||||
* aLinkHoles = if true, aResult contains only one polygon,
|
||||
* with holes linked by overlapping segments
|
||||
*
|
||||
* Important Note:
|
||||
* Inflating a polygon with acute angles or a non convex polygon gives non optimal shapes
|
||||
* for your purposes (creating a clearance area from zones).
|
||||
* So when inflating a polygon, we combine it with a "thick outline"
|
||||
* with a thickness = aInflateValue*2.
|
||||
* the inflated polygon shape is much better to build a polygon
|
||||
* from a polygon + clearance area
|
||||
*
|
||||
* Generic algos (Clipper, Boost Polygon) can inflate polygons, but the result is
|
||||
* not always suitable (they work fine only for polygons with non acute angle)
|
||||
*
|
||||
* To deflate polygons, the same calculation is made, but instead of adding the "thick outline"
|
||||
* we substract it.
|
||||
*/
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
|
||||
void CPOLYGONS_LIST::InflateOutline( CPOLYGONS_LIST& aResult, int aInflateValue, bool aLinkHoles )
|
||||
{
|
||||
KI_POLYGON_SET polyset_outline;
|
||||
ExportTo( polyset_outline );
|
||||
|
||||
// Extract holes (cutout areas) and add them to the hole buffer
|
||||
KI_POLYGON_SET outlineHoles;
|
||||
|
||||
while( polyset_outline.size() > 1 )
|
||||
{
|
||||
outlineHoles.push_back( polyset_outline.back() );
|
||||
polyset_outline.pop_back();
|
||||
}
|
||||
|
||||
// inflate main outline
|
||||
unsigned icnt = 0;
|
||||
int width = std::abs( aInflateValue * 2 );
|
||||
|
||||
if( polyset_outline.size() )
|
||||
{
|
||||
CPOLYGONS_LIST outlines;
|
||||
|
||||
for( ; icnt < GetCornersCount(); icnt++ )
|
||||
{
|
||||
unsigned ii = icnt+1;
|
||||
|
||||
if( IsEndContour( icnt ) )
|
||||
ii = 0;
|
||||
|
||||
TransformRoundedEndsSegmentToPolygon( outlines,
|
||||
GetPos( icnt ), GetPos( ii ), 16, width );
|
||||
|
||||
if( IsEndContour( icnt ) )
|
||||
break;
|
||||
}
|
||||
|
||||
KI_POLYGON_SET thicklines;
|
||||
outlines.ExportTo( thicklines );
|
||||
|
||||
if( aInflateValue > 0 ) // Inflate main outline
|
||||
polyset_outline += thicklines;
|
||||
else if( aInflateValue < 0 ) // Actually a deflate transform
|
||||
polyset_outline -= thicklines; // deflate main outline
|
||||
|
||||
}
|
||||
|
||||
// deflate outline holes
|
||||
if( outlineHoles.size() )
|
||||
{
|
||||
int deflateValue = -aInflateValue;
|
||||
|
||||
CPOLYGONS_LIST outlines;
|
||||
icnt += 1; // points the first point of the first hole
|
||||
unsigned firstpoint = icnt;
|
||||
|
||||
for( ; icnt < GetCornersCount(); icnt++ )
|
||||
{
|
||||
unsigned ii = icnt+1;
|
||||
|
||||
if( IsEndContour( icnt ) || ii >= GetCornersCount() )
|
||||
{
|
||||
ii = firstpoint;
|
||||
firstpoint = icnt+1;
|
||||
}
|
||||
|
||||
TransformRoundedEndsSegmentToPolygon( outlines,
|
||||
GetPos( icnt ), GetPos( ii ), 16, width );
|
||||
}
|
||||
|
||||
KI_POLYGON_SET thicklines;
|
||||
outlines.ExportTo( thicklines );
|
||||
|
||||
if( deflateValue > 0 ) // Inflate holes
|
||||
outlineHoles += thicklines;
|
||||
else if( deflateValue < 0 ) // deflate holes
|
||||
outlineHoles -= thicklines;
|
||||
}
|
||||
|
||||
// Copy modified polygons
|
||||
if( !aLinkHoles )
|
||||
{
|
||||
aResult.ImportFrom( polyset_outline );
|
||||
|
||||
if( outlineHoles.size() )
|
||||
aResult.ImportFrom( outlineHoles );
|
||||
}
|
||||
else
|
||||
{
|
||||
polyset_outline -= outlineHoles;
|
||||
aResult.ImportFrom( polyset_outline );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function ConvertPolysListWithHolesToOnePolygon
|
||||
* converts the outline contours aPolysListWithHoles with holes to one polygon
|
||||
* with no holes (only one contour)
|
||||
* holes are linked to main outlines by overlap segments, to give only one polygon
|
||||
*
|
||||
* @param aPolysListWithHoles = the list of corners of contours (haing holes
|
||||
* @param aOnePolyList = a polygon with no holes
|
||||
*/
|
||||
void ConvertPolysListWithHolesToOnePolygon( const CPOLYGONS_LIST& aPolysListWithHoles,
|
||||
CPOLYGONS_LIST& aOnePolyList )
|
||||
{
|
||||
unsigned corners_count = aPolysListWithHoles.GetCornersCount();
|
||||
|
||||
int polycount = 0;
|
||||
for( unsigned ii = 0; ii < corners_count; ii++ )
|
||||
{
|
||||
if( aPolysListWithHoles.IsEndContour( ii ) )
|
||||
polycount++;
|
||||
}
|
||||
|
||||
// If polycount<= 1, there is no holes found, and therefore just copy the polygon.
|
||||
if( polycount <= 1 )
|
||||
{
|
||||
aOnePolyList.Append( aPolysListWithHoles );
|
||||
return;
|
||||
}
|
||||
|
||||
// Holes are found: convert them to only one polygon with overlap segments
|
||||
KI_POLYGON_SET polysholes;
|
||||
KI_POLYGON_SET mainpoly;
|
||||
KI_POLYGON poly_tmp;
|
||||
std::vector<KI_POLY_POINT> cornerslist;
|
||||
corners_count = aPolysListWithHoles.GetCornersCount();
|
||||
|
||||
unsigned ic = 0;
|
||||
// enter main outline
|
||||
while( ic < corners_count )
|
||||
{
|
||||
const CPolyPt& corner = aPolysListWithHoles.GetCorner( ic++ );
|
||||
cornerslist.push_back( KI_POLY_POINT( corner.x, corner.y ) );
|
||||
|
||||
if( corner.end_contour )
|
||||
break;
|
||||
}
|
||||
bpl::set_points( poly_tmp, cornerslist.begin(), cornerslist.end() );
|
||||
mainpoly.push_back( poly_tmp );
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
cornerslist.clear();
|
||||
{
|
||||
while( ic < corners_count )
|
||||
{
|
||||
const CPolyPt& corner = aPolysListWithHoles.GetCorner( ic++ );
|
||||
cornerslist.push_back( KI_POLY_POINT( corner.x, corner.y ) );
|
||||
|
||||
if( corner.end_contour )
|
||||
break;
|
||||
}
|
||||
|
||||
bpl::set_points( poly_tmp, cornerslist.begin(), cornerslist.end() );
|
||||
polysholes.push_back( poly_tmp );
|
||||
}
|
||||
}
|
||||
|
||||
mainpoly -= polysholes;
|
||||
|
||||
// copy polygon with no holes to destination
|
||||
// Because all holes are now linked to the main outline
|
||||
// by overlapping segments, we should have only one polygon in list
|
||||
wxASSERT( mainpoly.size() == 1 );
|
||||
aOnePolyList.ImportFrom( mainpoly );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function IsPolygonSelfIntersecting
|
||||
|
@ -1268,80 +1724,21 @@ bool CPolyLine::IsPolygonSelfIntersecting()
|
|||
return false;
|
||||
}
|
||||
|
||||
const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList )
|
||||
|
||||
/* converts the outline aOnePolyList (only one contour,
|
||||
* holes are linked by overlapping segments) to
|
||||
* to one main polygon and holes (polygons inside main polygon)
|
||||
* aOnePolyList = a only one polygon ( holes are linked )
|
||||
* aPolysListWithHoles = the list of corners of contours
|
||||
* (main outline and holes)
|
||||
*/
|
||||
void ConvertOnePolygonToPolysListWithHoles( const CPOLYGONS_LIST& aOnePolyList,
|
||||
CPOLYGONS_LIST& aPolysListWithHoles )
|
||||
{
|
||||
SHAPE_POLY_SET rv;
|
||||
ClipperLib::Paths initialPoly;
|
||||
ClipperLib::Paths modifiedPoly;
|
||||
|
||||
unsigned corners_count = aList.GetCornersCount();
|
||||
|
||||
// Enter main outline: this is the first contour
|
||||
unsigned ic = 0;
|
||||
|
||||
if( !corners_count )
|
||||
return rv;
|
||||
|
||||
int index = 0;
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
int hole = -1;
|
||||
|
||||
if( index == 0 )
|
||||
{
|
||||
rv.NewOutline();
|
||||
hole = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hole = rv.NewHole();
|
||||
}
|
||||
|
||||
while( ic < corners_count )
|
||||
{
|
||||
rv.Append( aList.GetX( ic ), aList.GetY( ic ), 0, hole );
|
||||
|
||||
if( aList.IsEndContour( ic ) )
|
||||
break;
|
||||
|
||||
ic++;
|
||||
}
|
||||
ic++;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
aOnePolyList.ExportTo( initialPoly );
|
||||
SimplifyPolygon(initialPoly[0], modifiedPoly );
|
||||
aPolysListWithHoles.ImportFrom( modifiedPoly );
|
||||
}
|
||||
|
||||
|
||||
const CPOLYGONS_LIST ConvertPolySetToPolyList(const SHAPE_POLY_SET& aPolyset)
|
||||
{
|
||||
CPOLYGONS_LIST list;
|
||||
CPolyPt corner, firstCorner;
|
||||
|
||||
const SHAPE_POLY_SET::POLYGON& poly = aPolyset.CPolygon( 0 );
|
||||
|
||||
for( unsigned int jj = 0; jj < poly.size() ; jj++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN& path = poly[jj];
|
||||
|
||||
for( int i = 0; i < path.PointCount(); i++ )
|
||||
{
|
||||
const VECTOR2I &v = path.CPoint( i );
|
||||
|
||||
corner.x = v.x;
|
||||
corner.y = v.y;
|
||||
corner.end_contour = false;
|
||||
|
||||
if( i == 0 )
|
||||
firstCorner = corner;
|
||||
|
||||
list.AddCorner( corner );
|
||||
}
|
||||
|
||||
firstCorner.end_contour = true;
|
||||
list.AddCorner( firstCorner );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
|
@ -53,8 +53,8 @@
|
|||
#include <wx/gdicmn.h> // for wxPoint definition
|
||||
#include <layers_id_colors_and_visibility.h> // for LAYER_NUM definition
|
||||
#include <class_eda_rect.h> // for EDA_RECT definition
|
||||
|
||||
#include <geometry/shape_poly_set.h> // fixme
|
||||
#include <polygons_defs.h>
|
||||
#include <clipper.hpp>
|
||||
|
||||
class CSegment
|
||||
{
|
||||
|
@ -116,21 +116,30 @@ private:
|
|||
public:
|
||||
CPOLYGONS_LIST() {};
|
||||
|
||||
CPolyPt& operator [](int aIdx) { return m_cornersList[aIdx]; }
|
||||
CPolyPt& operator [](int aIdx) {return m_cornersList[aIdx]; }
|
||||
|
||||
// Accessor:
|
||||
const std::vector <CPolyPt>& GetList() const {return m_cornersList;}
|
||||
|
||||
int GetX( int ic ) const { return m_cornersList[ic].x; }
|
||||
void SetX( int ic, int aValue ) { m_cornersList[ic].x = aValue; }
|
||||
int GetY( int ic ) const { return m_cornersList[ic].y; }
|
||||
void SetY( int ic, int aValue ) { m_cornersList[ic].y = aValue; }
|
||||
int GetUtility( int ic ) const { return m_cornersList[ic].m_flags; }
|
||||
void SetFlag( int ic, int aFlag )
|
||||
{
|
||||
m_cornersList[ic].m_flags = aFlag;
|
||||
}
|
||||
|
||||
bool IsEndContour( int ic ) const
|
||||
{
|
||||
return m_cornersList[ic].end_contour;
|
||||
}
|
||||
|
||||
void SetEndContour( int ic, bool end_contour )
|
||||
{
|
||||
m_cornersList[ic].end_contour = end_contour;
|
||||
}
|
||||
|
||||
const wxPoint& GetPos( int ic ) const { return m_cornersList[ic]; }
|
||||
const CPolyPt& GetCorner( int ic ) const { return m_cornersList[ic]; }
|
||||
|
||||
|
@ -148,7 +157,6 @@ public:
|
|||
m_cornersList.erase( m_cornersList.begin() + aIdx );
|
||||
}
|
||||
|
||||
// used only to erase an entire polygon
|
||||
void DeleteCorners( int aIdFirstCorner, int aIdLastCorner )
|
||||
{
|
||||
m_cornersList.erase( m_cornersList.begin() + aIdFirstCorner,
|
||||
|
@ -204,6 +212,56 @@ public:
|
|||
* (number of corners flagged "end_contour"
|
||||
*/
|
||||
int GetContoursCount() const;
|
||||
|
||||
/**
|
||||
* Function ExportTo
|
||||
* Copy all contours to a KI_POLYGON_SET, each contour is exported
|
||||
* to a KI_POLYGON
|
||||
* @param aPolygons = the KI_POLYGON_SET to populate
|
||||
*/
|
||||
void ExportTo( KI_POLYGON_SET& aPolygons ) const;
|
||||
|
||||
/**
|
||||
* Function ExportTo
|
||||
* Copy the contours to a KI_POLYGON_WITH_HOLES
|
||||
* The first contour is the main outline, others are holes
|
||||
* @param aPolygoneWithHole = the KI_POLYGON_WITH_HOLES to populate
|
||||
*/
|
||||
void ExportTo( KI_POLYGON_WITH_HOLES& aPolygoneWithHole ) const;
|
||||
|
||||
/**
|
||||
* Function ExportTo
|
||||
* Copy all contours to a ClipperLib::Paths, each contour is exported
|
||||
* to a ClipperLib::Path
|
||||
* @param aPolygons = the ClipperLib::Paths to populate
|
||||
*/
|
||||
void ExportTo( ClipperLib::Paths& aPolygons ) const;
|
||||
|
||||
/**
|
||||
* Function ImportFrom
|
||||
* Copy all polygons from a KI_POLYGON_SET in list
|
||||
* @param aPolygons = the KI_POLYGON_SET to import
|
||||
*/
|
||||
void ImportFrom( KI_POLYGON_SET& aPolygons );
|
||||
|
||||
/**
|
||||
* Function ImportFrom
|
||||
* Copy all polygons from a ClipperLib::Paths in list
|
||||
* @param aPolygons = the ClipperLib::Paths to import
|
||||
*/
|
||||
void ImportFrom( ClipperLib::Paths& aPolygons );
|
||||
|
||||
/**
|
||||
* Function InflateOutline
|
||||
* Inflate the outline stored in m_cornersList.
|
||||
* The first polygon is the external outline. It is inflated
|
||||
* The other polygons are holes. they are deflated
|
||||
* @param aResult = the Inflated outline
|
||||
* @param aInflateValue = the Inflate value. when < 0, this is a deflate transform
|
||||
* @param aLinkHoles = if true, aResult contains only one polygon,
|
||||
* with holes linked by overlapping segments
|
||||
*/
|
||||
void InflateOutline( CPOLYGONS_LIST& aResult, int aInflateValue, bool aLinkHoles );
|
||||
};
|
||||
|
||||
class CPolyLine
|
||||
|
@ -389,6 +447,9 @@ public:
|
|||
|
||||
const wxPoint& GetPos( int ic ) const { return m_CornersList.GetPos( ic ); }
|
||||
|
||||
int GetUtility( int ic ) const { return m_CornersList.GetUtility( ic ); };
|
||||
void SetUtility( int ic, int aFlag ) { m_CornersList.SetFlag( ic, aFlag ); };
|
||||
|
||||
int GetHatchPitch() const { return m_hatchPitch; }
|
||||
static int GetDefaultHatchPitchMils() { return 20; } // default hatch pitch value in mils
|
||||
|
||||
|
@ -412,6 +473,11 @@ public:
|
|||
m_CornersList.SetY( ic, y );
|
||||
}
|
||||
|
||||
void SetEndContour( int ic, bool end_contour )
|
||||
{
|
||||
m_CornersList.SetEndContour( ic, end_contour );
|
||||
}
|
||||
|
||||
void SetHatchStyle( enum HATCH_STYLE style )
|
||||
{
|
||||
m_hatchStyle = style;
|
||||
|
@ -483,7 +549,30 @@ public:
|
|||
std::vector <CSegment> m_HatchLines; // hatch lines showing the polygon area
|
||||
};
|
||||
|
||||
const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList );
|
||||
const CPOLYGONS_LIST ConvertPolySetToPolyList( const SHAPE_POLY_SET& aPolyset );
|
||||
/**
|
||||
* Function ConvertPolysListWithHolesToOnePolygon
|
||||
* converts the outline contours aPolysListWithHoles with holes to one polygon
|
||||
* with no holes (only one contour)
|
||||
* holes are linked to main outlines by overlap segments, to give only one polygon
|
||||
*
|
||||
* @param aPolysListWithHoles = the list of corners of contours
|
||||
* (main outline and holes)
|
||||
* @param aOnePolyList = a polygon with no holes
|
||||
*/
|
||||
void ConvertPolysListWithHolesToOnePolygon( const CPOLYGONS_LIST& aPolysListWithHoles,
|
||||
CPOLYGONS_LIST& aOnePolyList );
|
||||
|
||||
/**
|
||||
* Function ConvertOnePolygonToPolysListWithHoles
|
||||
* converts the outline aOnePolyList (only one contour,
|
||||
* holes are linked by overlapping segments) to
|
||||
* to one main polygon and holes (polygons inside main polygon)
|
||||
* @param aOnePolyList = a polygon with no holes
|
||||
* @param aPolysListWithHoles = the list of corners of contours
|
||||
* (main outline and holes)
|
||||
*/
|
||||
void ConvertOnePolygonToPolysListWithHoles( const CPOLYGONS_LIST& aOnePolyList,
|
||||
CPOLYGONS_LIST& aPolysListWithHoles );
|
||||
|
||||
|
||||
#endif // #ifndef POLYLINE_H
|
||||
|
|
|
@ -0,0 +1,500 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "shapes.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace p2t {
|
||||
Triangle::Triangle( Point& a, Point& b, Point& c )
|
||||
{
|
||||
points_[0] = &a; points_[1] = &b; points_[2] = &c;
|
||||
neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL;
|
||||
constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false;
|
||||
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
|
||||
interior_ = false;
|
||||
}
|
||||
|
||||
|
||||
// Update neighbor pointers
|
||||
void Triangle::MarkNeighbor( Point* p1, Point* p2, Triangle* t )
|
||||
{
|
||||
if( (p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2]) )
|
||||
neighbors_[0] = t;
|
||||
else if( (p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0]) )
|
||||
neighbors_[1] = t;
|
||||
else if( (p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0]) )
|
||||
neighbors_[2] = t;
|
||||
else
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
|
||||
// Exhaustive search to update neighbor pointers
|
||||
void Triangle::MarkNeighbor( Triangle& t )
|
||||
{
|
||||
if( t.Contains( points_[1], points_[2] ) )
|
||||
{
|
||||
neighbors_[0] = &t;
|
||||
t.MarkNeighbor( points_[1], points_[2], this );
|
||||
}
|
||||
else if( t.Contains( points_[0], points_[2] ) )
|
||||
{
|
||||
neighbors_[1] = &t;
|
||||
t.MarkNeighbor( points_[0], points_[2], this );
|
||||
}
|
||||
else if( t.Contains( points_[0], points_[1] ) )
|
||||
{
|
||||
neighbors_[2] = &t;
|
||||
t.MarkNeighbor( points_[0], points_[1], this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears all references to all other triangles and points
|
||||
*/
|
||||
void Triangle::Clear()
|
||||
{
|
||||
Triangle* t;
|
||||
|
||||
for( int i = 0; i<3; i++ )
|
||||
{
|
||||
t = neighbors_[i];
|
||||
|
||||
if( t != NULL )
|
||||
{
|
||||
t->ClearNeighbor( this );
|
||||
}
|
||||
}
|
||||
|
||||
ClearNeighbors();
|
||||
points_[0] = points_[1] = points_[2] = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Triangle::ClearNeighbor( Triangle* triangle )
|
||||
{
|
||||
if( neighbors_[0] == triangle )
|
||||
{
|
||||
neighbors_[0] = NULL;
|
||||
}
|
||||
else if( neighbors_[1] == triangle )
|
||||
{
|
||||
neighbors_[1] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
neighbors_[2] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Triangle::ClearNeighbors()
|
||||
{
|
||||
neighbors_[0] = NULL;
|
||||
neighbors_[1] = NULL;
|
||||
neighbors_[2] = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Triangle::ClearDelunayEdges()
|
||||
{
|
||||
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
|
||||
}
|
||||
|
||||
|
||||
Point* Triangle::OppositePoint( Triangle& t, Point& p )
|
||||
{
|
||||
Point* cw = t.PointCW( p );
|
||||
|
||||
/*
|
||||
double x = cw->x;
|
||||
double y = cw->y;
|
||||
|
||||
x = p.x;
|
||||
y = p.y;
|
||||
*/
|
||||
|
||||
return PointCW( *cw );
|
||||
}
|
||||
|
||||
|
||||
// Legalized triangle by rotating clockwise around point(0)
|
||||
void Triangle::Legalize( Point& point )
|
||||
{
|
||||
points_[1] = points_[0];
|
||||
points_[0] = points_[2];
|
||||
points_[2] = &point;
|
||||
}
|
||||
|
||||
|
||||
// Legalize triagnle by rotating clockwise around oPoint
|
||||
void Triangle::Legalize( Point& opoint, Point& npoint )
|
||||
{
|
||||
if( &opoint == points_[0] )
|
||||
{
|
||||
points_[1] = points_[0];
|
||||
points_[0] = points_[2];
|
||||
points_[2] = &npoint;
|
||||
}
|
||||
else if( &opoint == points_[1] )
|
||||
{
|
||||
points_[2] = points_[1];
|
||||
points_[1] = points_[0];
|
||||
points_[0] = &npoint;
|
||||
}
|
||||
else if( &opoint == points_[2] )
|
||||
{
|
||||
points_[0] = points_[2];
|
||||
points_[2] = points_[1];
|
||||
points_[1] = &npoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Triangle::Index( const Point* p )
|
||||
{
|
||||
if( p == points_[0] )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if( p == points_[1] )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if( p == points_[2] )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
assert( 0 );
|
||||
return 0; // you better hope its a Debug build.
|
||||
}
|
||||
|
||||
|
||||
int Triangle::EdgeIndex( const Point* p1, const Point* p2 )
|
||||
{
|
||||
if( points_[0] == p1 )
|
||||
{
|
||||
if( points_[1] == p2 )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if( points_[2] == p2 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if( points_[1] == p1 )
|
||||
{
|
||||
if( points_[2] == p2 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if( points_[0] == p2 )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else if( points_[2] == p1 )
|
||||
{
|
||||
if( points_[0] == p2 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if( points_[1] == p2 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void Triangle::MarkConstrainedEdge( const int index )
|
||||
{
|
||||
constrained_edge[index] = true;
|
||||
}
|
||||
|
||||
|
||||
void Triangle::MarkConstrainedEdge( Edge& edge )
|
||||
{
|
||||
MarkConstrainedEdge( edge.p, edge.q );
|
||||
}
|
||||
|
||||
|
||||
// Mark edge as constrained
|
||||
void Triangle::MarkConstrainedEdge( Point* p, Point* q )
|
||||
{
|
||||
if( (q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0]) )
|
||||
{
|
||||
constrained_edge[2] = true;
|
||||
}
|
||||
else if( (q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0]) )
|
||||
{
|
||||
constrained_edge[1] = true;
|
||||
}
|
||||
else if( (q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1]) )
|
||||
{
|
||||
constrained_edge[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The point counter-clockwise to given point
|
||||
Point* Triangle::PointCW( Point& point )
|
||||
{
|
||||
if( &point == points_[0] )
|
||||
{
|
||||
return points_[2];
|
||||
}
|
||||
else if( &point == points_[1] )
|
||||
{
|
||||
return points_[0];
|
||||
}
|
||||
else if( &point == points_[2] )
|
||||
{
|
||||
return points_[1];
|
||||
}
|
||||
|
||||
assert( 0 );
|
||||
return NULL; // you better hope its a Debug build.
|
||||
}
|
||||
|
||||
|
||||
// The point counter-clockwise to given point
|
||||
Point* Triangle::PointCCW( Point& point )
|
||||
{
|
||||
if( &point == points_[0] )
|
||||
{
|
||||
return points_[1];
|
||||
}
|
||||
else if( &point == points_[1] )
|
||||
{
|
||||
return points_[2];
|
||||
}
|
||||
else if( &point == points_[2] )
|
||||
{
|
||||
return points_[0];
|
||||
}
|
||||
|
||||
assert( 0 );
|
||||
return NULL; // you better hope its a Debug build.
|
||||
}
|
||||
|
||||
|
||||
// The neighbor clockwise to given point
|
||||
Triangle* Triangle::NeighborCW( Point& point )
|
||||
{
|
||||
if( &point == points_[0] )
|
||||
{
|
||||
return neighbors_[1];
|
||||
}
|
||||
else if( &point == points_[1] )
|
||||
{
|
||||
return neighbors_[2];
|
||||
}
|
||||
|
||||
return neighbors_[0];
|
||||
}
|
||||
|
||||
|
||||
// The neighbor counter-clockwise to given point
|
||||
Triangle* Triangle::NeighborCCW( Point& point )
|
||||
{
|
||||
if( &point == points_[0] )
|
||||
{
|
||||
return neighbors_[2];
|
||||
}
|
||||
else if( &point == points_[1] )
|
||||
{
|
||||
return neighbors_[0];
|
||||
}
|
||||
|
||||
return neighbors_[1];
|
||||
}
|
||||
|
||||
|
||||
bool Triangle::GetConstrainedEdgeCCW( Point& p )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return constrained_edge[2];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return constrained_edge[0];
|
||||
}
|
||||
|
||||
return constrained_edge[1];
|
||||
}
|
||||
|
||||
|
||||
bool Triangle::GetConstrainedEdgeCW( Point& p )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return constrained_edge[1];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return constrained_edge[2];
|
||||
}
|
||||
|
||||
return constrained_edge[0];
|
||||
}
|
||||
|
||||
|
||||
void Triangle::SetConstrainedEdgeCCW( Point& p, bool ce )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
constrained_edge[2] = ce;
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
constrained_edge[0] = ce;
|
||||
}
|
||||
else
|
||||
{
|
||||
constrained_edge[1] = ce;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Triangle::SetConstrainedEdgeCW( Point& p, bool ce )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
constrained_edge[1] = ce;
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
constrained_edge[2] = ce;
|
||||
}
|
||||
else
|
||||
{
|
||||
constrained_edge[0] = ce;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Triangle::GetDelunayEdgeCCW( Point& p )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return delaunay_edge[2];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return delaunay_edge[0];
|
||||
}
|
||||
|
||||
return delaunay_edge[1];
|
||||
}
|
||||
|
||||
|
||||
bool Triangle::GetDelunayEdgeCW( Point& p )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return delaunay_edge[1];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return delaunay_edge[2];
|
||||
}
|
||||
|
||||
return delaunay_edge[0];
|
||||
}
|
||||
|
||||
|
||||
void Triangle::SetDelunayEdgeCCW( Point& p, bool e )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
delaunay_edge[2] = e;
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
delaunay_edge[0] = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
delaunay_edge[1] = e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Triangle::SetDelunayEdgeCW( Point& p, bool e )
|
||||
{
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
delaunay_edge[1] = e;
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
delaunay_edge[2] = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
delaunay_edge[0] = e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The neighbor across to given point
|
||||
Triangle& Triangle::NeighborAcross( Point& opoint )
|
||||
{
|
||||
if( &opoint == points_[0] )
|
||||
{
|
||||
return *neighbors_[0];
|
||||
}
|
||||
else if( &opoint == points_[1] )
|
||||
{
|
||||
return *neighbors_[1];
|
||||
}
|
||||
|
||||
return *neighbors_[2];
|
||||
}
|
||||
|
||||
|
||||
void Triangle::DebugPrint()
|
||||
{
|
||||
std::cout << points_[0]->x << "," << points_[0]->y << " ";
|
||||
std::cout << points_[1]->x << "," << points_[1]->y << " ";
|
||||
std::cout << points_[2]->x << "," << points_[2]->y << "\n";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Include guard
|
||||
#ifndef SHAPES_H
|
||||
#define SHAPES_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace p2t {
|
||||
struct Edge;
|
||||
|
||||
struct Point
|
||||
{
|
||||
double x, y;
|
||||
|
||||
/// Default constructor does nothing (for performance).
|
||||
Point()
|
||||
{
|
||||
x = 0.0;
|
||||
y = 0.0;
|
||||
}
|
||||
|
||||
/// The edges this point constitutes an upper ending point
|
||||
std::vector<Edge*> edge_list;
|
||||
|
||||
/// Construct using coordinates.
|
||||
Point( double x, double y ) : x( x ), y( y ) {}
|
||||
|
||||
/// Set this point to all zeros.
|
||||
void set_zero()
|
||||
{
|
||||
x = 0.0;
|
||||
y = 0.0;
|
||||
}
|
||||
|
||||
/// Set this point to some specified coordinates.
|
||||
void set( double x_, double y_ )
|
||||
{
|
||||
x = x_;
|
||||
y = y_;
|
||||
}
|
||||
|
||||
/// Negate this point.
|
||||
Point operator -() const
|
||||
{
|
||||
Point v;
|
||||
|
||||
v.set( -x, -y );
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Add a point to this point.
|
||||
void operator +=( const Point& v )
|
||||
{
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
}
|
||||
|
||||
/// Subtract a point from this point.
|
||||
void operator -=( const Point& v )
|
||||
{
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
}
|
||||
|
||||
/// Multiply this point by a scalar.
|
||||
void operator *=( double a )
|
||||
{
|
||||
x *= a;
|
||||
y *= a;
|
||||
}
|
||||
|
||||
/// Get the length of this point (the norm).
|
||||
double Length() const
|
||||
{
|
||||
return sqrt( x * x + y * y );
|
||||
}
|
||||
|
||||
/// Convert this point into a unit point. Returns the Length.
|
||||
double Normalize()
|
||||
{
|
||||
double len = Length();
|
||||
|
||||
x /= len;
|
||||
y /= len;
|
||||
return len;
|
||||
}
|
||||
};
|
||||
|
||||
// Represents a simple polygon's edge
|
||||
struct Edge
|
||||
{
|
||||
Point* p, * q;
|
||||
|
||||
/// Constructor
|
||||
Edge( Point& p1, Point& p2 ) : p( &p1 ), q( &p2 )
|
||||
{
|
||||
if( p1.y > p2.y )
|
||||
{
|
||||
q = &p1;
|
||||
p = &p2;
|
||||
}
|
||||
else if( p1.y == p2.y )
|
||||
{
|
||||
if( p1.x > p2.x )
|
||||
{
|
||||
q = &p1;
|
||||
p = &p2;
|
||||
}
|
||||
else if( p1.x == p2.x )
|
||||
{
|
||||
// Repeat points
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
|
||||
q->edge_list.push_back( this );
|
||||
}
|
||||
};
|
||||
|
||||
// Triangle-based data structures are know to have better performance than quad-edge structures
|
||||
// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
|
||||
// "Triangulations in CGAL"
|
||||
class Triangle
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
Triangle( Point& a, Point& b, Point& c );
|
||||
|
||||
/// Flags to determine if an edge is a Constrained edge
|
||||
bool constrained_edge[3];
|
||||
/// Flags to determine if an edge is a Delauney edge
|
||||
bool delaunay_edge[3];
|
||||
|
||||
Point* GetPoint( const int& index );
|
||||
Point* PointCW( Point& point );
|
||||
Point* PointCCW( Point& point );
|
||||
Point* OppositePoint( Triangle& t, Point& p );
|
||||
|
||||
Triangle* GetNeighbor( const int& index );
|
||||
void MarkNeighbor( Point* p1, Point* p2, Triangle* t );
|
||||
void MarkNeighbor( Triangle& t );
|
||||
|
||||
void MarkConstrainedEdge( const int index );
|
||||
void MarkConstrainedEdge( Edge& edge );
|
||||
void MarkConstrainedEdge( Point* p, Point* q );
|
||||
|
||||
int Index( const Point* p );
|
||||
int EdgeIndex( const Point* p1, const Point* p2 );
|
||||
|
||||
Triangle* NeighborCW( Point& point );
|
||||
Triangle* NeighborCCW( Point& point );
|
||||
bool GetConstrainedEdgeCCW( Point& p );
|
||||
bool GetConstrainedEdgeCW( Point& p );
|
||||
void SetConstrainedEdgeCCW( Point& p, bool ce );
|
||||
void SetConstrainedEdgeCW( Point& p, bool ce );
|
||||
bool GetDelunayEdgeCCW( Point& p );
|
||||
bool GetDelunayEdgeCW( Point& p );
|
||||
void SetDelunayEdgeCCW( Point& p, bool e );
|
||||
void SetDelunayEdgeCW( Point& p, bool e );
|
||||
|
||||
bool Contains( Point* p );
|
||||
bool Contains( const Edge& e );
|
||||
bool Contains( Point* p, Point* q );
|
||||
void Legalize( Point& point );
|
||||
void Legalize( Point& opoint, Point& npoint );
|
||||
|
||||
/**
|
||||
* Clears all references to all other triangles and points
|
||||
*/
|
||||
void Clear();
|
||||
void ClearNeighbor( Triangle* triangle );
|
||||
void ClearNeighbors();
|
||||
void ClearDelunayEdges();
|
||||
|
||||
inline bool IsInterior();
|
||||
inline void IsInterior( bool b );
|
||||
|
||||
Triangle& NeighborAcross( Point& opoint );
|
||||
|
||||
void DebugPrint();
|
||||
|
||||
private:
|
||||
|
||||
/// Triangle points
|
||||
Point* points_[3];
|
||||
/// Neighbor list
|
||||
Triangle* neighbors_[3];
|
||||
|
||||
/// Has this triangle been marked as an interior triangle?
|
||||
bool interior_;
|
||||
};
|
||||
|
||||
inline bool cmp( const Point* a, const Point* b )
|
||||
{
|
||||
if( a->y < b->y )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if( a->y == b->y )
|
||||
{
|
||||
// Make sure q is point with greater x value
|
||||
if( a->x < b->x )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// Add two points_ component-wise.
|
||||
inline Point operator +( const Point& a, const Point& b )
|
||||
{
|
||||
return Point( a.x + b.x, a.y + b.y );
|
||||
}
|
||||
|
||||
|
||||
/// Subtract two points_ component-wise.
|
||||
inline Point operator -( const Point& a, const Point& b )
|
||||
{
|
||||
return Point( a.x - b.x, a.y - b.y );
|
||||
}
|
||||
|
||||
|
||||
/// Multiply point by scalar
|
||||
inline Point operator *( double s, const Point& a )
|
||||
{
|
||||
return Point( s * a.x, s * a.y );
|
||||
}
|
||||
|
||||
|
||||
inline bool operator ==( const Point& a, const Point& b )
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
|
||||
inline bool operator !=( const Point& a, const Point& b )
|
||||
{
|
||||
return !(a.x == b.x) && !(a.y == b.y);
|
||||
}
|
||||
|
||||
|
||||
/// Peform the dot product on two vectors.
|
||||
inline double Dot( const Point& a, const Point& b )
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
|
||||
/// Perform the cross product on two vectors. In 2D this produces a scalar.
|
||||
inline double Cross( const Point& a, const Point& b )
|
||||
{
|
||||
return a.x * b.y - a.y * b.x;
|
||||
}
|
||||
|
||||
|
||||
/// Perform the cross product on a point and a scalar. In 2D this produces
|
||||
/// a point.
|
||||
inline Point Cross( const Point& a, double s )
|
||||
{
|
||||
return Point( s * a.y, -s * a.x );
|
||||
}
|
||||
|
||||
|
||||
/// Perform the cross product on a scalar and a point. In 2D this produces
|
||||
/// a point.
|
||||
inline Point Cross( const double s, const Point& a )
|
||||
{
|
||||
return Point( -s * a.y, s * a.x );
|
||||
}
|
||||
|
||||
|
||||
inline Point* Triangle::GetPoint( const int& index )
|
||||
{
|
||||
return points_[index];
|
||||
}
|
||||
|
||||
|
||||
inline Triangle* Triangle::GetNeighbor( const int& index )
|
||||
{
|
||||
return neighbors_[index];
|
||||
}
|
||||
|
||||
|
||||
inline bool Triangle::Contains( Point* p )
|
||||
{
|
||||
return p == points_[0] || p == points_[1] || p == points_[2];
|
||||
}
|
||||
|
||||
|
||||
inline bool Triangle::Contains( const Edge& e )
|
||||
{
|
||||
return Contains( e.p ) && Contains( e.q );
|
||||
}
|
||||
|
||||
|
||||
inline bool Triangle::Contains( Point* p, Point* q )
|
||||
{
|
||||
return Contains( p ) && Contains( q );
|
||||
}
|
||||
|
||||
|
||||
inline bool Triangle::IsInterior()
|
||||
{
|
||||
return interior_;
|
||||
}
|
||||
|
||||
|
||||
inline void Triangle::IsInterior( bool b )
|
||||
{
|
||||
interior_ = b;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
// Otherwise #defines like M_PI are undeclared under Visual Studio
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <exception>
|
||||
#include <math.h>
|
||||
|
||||
namespace p2t {
|
||||
const double PI_3div4 = 3 * M_PI / 4;
|
||||
const double PI_div2 = 1.57079632679489661923;
|
||||
const double EPSILON = 1e-12;
|
||||
|
||||
enum Orientation {
|
||||
CW, CCW, COLLINEAR
|
||||
};
|
||||
|
||||
/**
|
||||
* Forumla to calculate signed area<br>
|
||||
* Positive if CCW<br>
|
||||
* Negative if CW<br>
|
||||
* 0 if collinear<br>
|
||||
* <pre>
|
||||
* A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
|
||||
* = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
|
||||
* </pre>
|
||||
*/
|
||||
Orientation Orient2d( Point& pa, Point& pb, Point& pc )
|
||||
{
|
||||
double detleft = (pa.x - pc.x) * (pb.y - pc.y);
|
||||
double detright = (pa.y - pc.y) * (pb.x - pc.x);
|
||||
double val = detleft - detright;
|
||||
|
||||
if( val > -EPSILON && val < EPSILON )
|
||||
{
|
||||
return COLLINEAR;
|
||||
}
|
||||
else if( val > 0 )
|
||||
{
|
||||
return CCW;
|
||||
}
|
||||
|
||||
return CW;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
|
||||
* {
|
||||
* double pdx = pd.x;
|
||||
* double pdy = pd.y;
|
||||
* double adx = pa.x - pdx;
|
||||
* double ady = pa.y - pdy;
|
||||
* double bdx = pb.x - pdx;
|
||||
* double bdy = pb.y - pdy;
|
||||
*
|
||||
* double adxbdy = adx * bdy;
|
||||
* double bdxady = bdx * ady;
|
||||
* double oabd = adxbdy - bdxady;
|
||||
*
|
||||
* if (oabd <= EPSILON) {
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* double cdx = pc.x - pdx;
|
||||
* double cdy = pc.y - pdy;
|
||||
*
|
||||
* double cdxady = cdx * ady;
|
||||
* double adxcdy = adx * cdy;
|
||||
* double ocad = cdxady - adxcdy;
|
||||
*
|
||||
* if (ocad <= EPSILON) {
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* return true;
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
bool InScanArea( Point& pa, Point& pb, Point& pc, Point& pd )
|
||||
{
|
||||
double oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y);
|
||||
|
||||
if( oadb >= -EPSILON )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y);
|
||||
|
||||
if( oadc <= EPSILON )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef POLY2TRI_H
|
||||
#define POLY2TRI_H
|
||||
|
||||
#include "common/shapes.h"
|
||||
#include "sweep/cdt.h"
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "advancing_front.h"
|
||||
|
||||
namespace p2t {
|
||||
|
||||
AdvancingFront::AdvancingFront(Node& head, Node& tail)
|
||||
{
|
||||
head_ = &head;
|
||||
tail_ = &tail;
|
||||
search_node_ = &head;
|
||||
}
|
||||
|
||||
Node* AdvancingFront::LocateNode(const double& x)
|
||||
{
|
||||
Node* node = search_node_;
|
||||
|
||||
if (x < node->value) {
|
||||
while ((node = node->prev) != NULL) {
|
||||
if (x >= node->value) {
|
||||
search_node_ = node;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while ((node = node->next) != NULL) {
|
||||
if (x < node->value) {
|
||||
search_node_ = node->prev;
|
||||
return node->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node* AdvancingFront::FindSearchNode(const double& x)
|
||||
{
|
||||
(void)x; // suppress compiler warnings "unused parameter 'x'"
|
||||
// TODO: implement BST index
|
||||
return search_node_;
|
||||
}
|
||||
|
||||
Node* AdvancingFront::LocatePoint(const Point* point)
|
||||
{
|
||||
const double px = point->x;
|
||||
Node* node = FindSearchNode(px);
|
||||
const double nx = node->point->x;
|
||||
|
||||
if (px == nx) {
|
||||
if (point != node->point) {
|
||||
// We might have two nodes with same x value for a short time
|
||||
if (point == node->prev->point) {
|
||||
node = node->prev;
|
||||
} else if (point == node->next->point) {
|
||||
node = node->next;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
} else if (px < nx) {
|
||||
while ((node = node->prev) != NULL) {
|
||||
if (point == node->point) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while ((node = node->next) != NULL) {
|
||||
if (point == node->point)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(node) search_node_ = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
AdvancingFront::~AdvancingFront()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ADVANCED_FRONT_H
|
||||
#define ADVANCED_FRONT_H
|
||||
|
||||
#include "../common/shapes.h"
|
||||
|
||||
namespace p2t {
|
||||
|
||||
struct Node;
|
||||
|
||||
// Advancing front node
|
||||
struct Node {
|
||||
Point* point;
|
||||
Triangle* triangle;
|
||||
|
||||
Node* next;
|
||||
Node* prev;
|
||||
|
||||
double value;
|
||||
|
||||
Node(Point& p) : point(&p), triangle(NULL), next(NULL), prev(NULL), value(p.x)
|
||||
{
|
||||
}
|
||||
|
||||
Node(Point& p, Triangle& t) : point(&p), triangle(&t), next(NULL), prev(NULL), value(p.x)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Advancing front
|
||||
class AdvancingFront {
|
||||
public:
|
||||
|
||||
AdvancingFront(Node& head, Node& tail);
|
||||
// Destructor
|
||||
~AdvancingFront();
|
||||
|
||||
Node* head();
|
||||
void set_head(Node* node);
|
||||
Node* tail();
|
||||
void set_tail(Node* node);
|
||||
Node* search();
|
||||
void set_search(Node* node);
|
||||
|
||||
/// Locate insertion point along advancing front
|
||||
Node* LocateNode(const double& x);
|
||||
|
||||
Node* LocatePoint(const Point* point);
|
||||
|
||||
private:
|
||||
|
||||
Node* head_, *tail_, *search_node_;
|
||||
|
||||
Node* FindSearchNode(const double& x);
|
||||
};
|
||||
|
||||
inline Node* AdvancingFront::head()
|
||||
{
|
||||
return head_;
|
||||
}
|
||||
inline void AdvancingFront::set_head(Node* node)
|
||||
{
|
||||
head_ = node;
|
||||
}
|
||||
|
||||
inline Node* AdvancingFront::tail()
|
||||
{
|
||||
return tail_;
|
||||
}
|
||||
inline void AdvancingFront::set_tail(Node* node)
|
||||
{
|
||||
tail_ = node;
|
||||
}
|
||||
|
||||
inline Node* AdvancingFront::search()
|
||||
{
|
||||
return search_node_;
|
||||
}
|
||||
|
||||
inline void AdvancingFront::set_search(Node* node)
|
||||
{
|
||||
search_node_ = node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "cdt.h"
|
||||
|
||||
namespace p2t {
|
||||
|
||||
CDT::CDT(std::vector<Point*> polyline)
|
||||
{
|
||||
sweep_context_ = new SweepContext(polyline);
|
||||
sweep_ = new Sweep;
|
||||
}
|
||||
|
||||
void CDT::AddHole(std::vector<Point*> polyline)
|
||||
{
|
||||
sweep_context_->AddHole(polyline);
|
||||
}
|
||||
|
||||
void CDT::AddPoint(Point* point) {
|
||||
sweep_context_->AddPoint(point);
|
||||
}
|
||||
|
||||
void CDT::Triangulate()
|
||||
{
|
||||
sweep_->Triangulate(*sweep_context_);
|
||||
}
|
||||
|
||||
std::vector<p2t::Triangle*> CDT::GetTriangles()
|
||||
{
|
||||
return sweep_context_->GetTriangles();
|
||||
}
|
||||
|
||||
std::list<p2t::Triangle*> CDT::GetMap()
|
||||
{
|
||||
return sweep_context_->GetMap();
|
||||
}
|
||||
|
||||
CDT::~CDT()
|
||||
{
|
||||
delete sweep_context_;
|
||||
delete sweep_;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef CDT_H
|
||||
#define CDT_H
|
||||
|
||||
#include "advancing_front.h"
|
||||
#include "sweep_context.h"
|
||||
#include "sweep.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Mason Green <mason.green@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace p2t {
|
||||
|
||||
class CDT
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor - add polyline with non repeating points
|
||||
*
|
||||
* @param polyline
|
||||
*/
|
||||
CDT(std::vector<Point*> polyline);
|
||||
|
||||
/**
|
||||
* Destructor - clean up memory
|
||||
*/
|
||||
~CDT();
|
||||
|
||||
/**
|
||||
* Add a hole
|
||||
*
|
||||
* @param polyline
|
||||
*/
|
||||
void AddHole(std::vector<Point*> polyline);
|
||||
|
||||
/**
|
||||
* Add a steiner point
|
||||
*
|
||||
* @param point
|
||||
*/
|
||||
void AddPoint(Point* point);
|
||||
|
||||
/**
|
||||
* Triangulate - do this AFTER you've added the polyline, holes, and Steiner points
|
||||
*/
|
||||
void Triangulate();
|
||||
|
||||
/**
|
||||
* Get CDT triangles
|
||||
*/
|
||||
std::vector<Triangle*> GetTriangles();
|
||||
|
||||
/**
|
||||
* Get triangle map
|
||||
*/
|
||||
std::list<Triangle*> GetMap();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Internals
|
||||
*/
|
||||
|
||||
SweepContext* sweep_context_;
|
||||
Sweep* sweep_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,817 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdexcept>
|
||||
#include "sweep.h"
|
||||
#include "sweep_context.h"
|
||||
#include "advancing_front.h"
|
||||
#include "../common/utils.h"
|
||||
|
||||
namespace p2t {
|
||||
|
||||
// Triangulate simple polygon with holes
|
||||
void Sweep::Triangulate(SweepContext& tcx)
|
||||
{
|
||||
tcx.InitTriangulation();
|
||||
tcx.CreateAdvancingFront(nodes_);
|
||||
// Sweep points; build mesh
|
||||
SweepPoints(tcx);
|
||||
// Clean up
|
||||
FinalizationPolygon(tcx);
|
||||
}
|
||||
|
||||
void Sweep::SweepPoints(SweepContext& tcx)
|
||||
{
|
||||
for (int i = 1; i < tcx.point_count(); i++) {
|
||||
Point& point = *tcx.GetPoint(i);
|
||||
Node* node = &PointEvent(tcx, point);
|
||||
for (unsigned int i = 0; i < point.edge_list.size(); i++) {
|
||||
EdgeEvent(tcx, point.edge_list[i], node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FinalizationPolygon(SweepContext& tcx)
|
||||
{
|
||||
// Get an Internal triangle to start with
|
||||
Triangle* t = tcx.front()->head()->next->triangle;
|
||||
Point* p = tcx.front()->head()->next->point;
|
||||
while (!t->GetConstrainedEdgeCW(*p)) {
|
||||
t = t->NeighborCCW(*p);
|
||||
}
|
||||
|
||||
// Collect interior triangles constrained by edges
|
||||
tcx.MeshClean(*t);
|
||||
}
|
||||
|
||||
Node& Sweep::PointEvent(SweepContext& tcx, Point& point)
|
||||
{
|
||||
Node& node = tcx.LocateNode(point);
|
||||
Node& new_node = NewFrontTriangle(tcx, point, node);
|
||||
|
||||
// Only need to check +epsilon since point never have smaller
|
||||
// x value than node due to how we fetch nodes from the front
|
||||
if (point.x <= node.point->x + EPSILON) {
|
||||
Fill(tcx, node);
|
||||
}
|
||||
|
||||
//tcx.AddNode(new_node);
|
||||
|
||||
FillAdvancingFront(tcx, new_node);
|
||||
return new_node;
|
||||
}
|
||||
|
||||
void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||
{
|
||||
tcx.edge_event.constrained_edge = edge;
|
||||
tcx.edge_event.right = (edge->p->x > edge->q->x);
|
||||
|
||||
if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For now we will do all needed filling
|
||||
// TODO: integrate with flip process might give some better performance
|
||||
// but for now this avoid the issue with cases that needs both flips and fills
|
||||
FillEdgeEvent(tcx, edge, node);
|
||||
EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q);
|
||||
}
|
||||
|
||||
void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
|
||||
{
|
||||
if (IsEdgeSideOfTriangle(*triangle, ep, eq)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point* p1 = triangle->PointCCW(point);
|
||||
Orientation o1 = Orient2d(eq, *p1, ep);
|
||||
if (o1 == COLLINEAR) {
|
||||
if( triangle->Contains(&eq, p1)) {
|
||||
triangle->MarkConstrainedEdge(&eq, p1 );
|
||||
// We are modifying the constraint maybe it would be better to
|
||||
// not change the given constraint and just keep a variable for the new constraint
|
||||
tcx.edge_event.constrained_edge->q = p1;
|
||||
triangle = &triangle->NeighborAcross(point);
|
||||
EdgeEvent( tcx, ep, *p1, triangle, *p1 );
|
||||
} else {
|
||||
std::runtime_error("EdgeEvent - collinear points not supported");
|
||||
assert(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Point* p2 = triangle->PointCW(point);
|
||||
Orientation o2 = Orient2d(eq, *p2, ep);
|
||||
if (o2 == COLLINEAR) {
|
||||
if( triangle->Contains(&eq, p2)) {
|
||||
triangle->MarkConstrainedEdge(&eq, p2 );
|
||||
// We are modifying the constraint maybe it would be better to
|
||||
// not change the given constraint and just keep a variable for the new constraint
|
||||
tcx.edge_event.constrained_edge->q = p2;
|
||||
triangle = &triangle->NeighborAcross(point);
|
||||
EdgeEvent( tcx, ep, *p2, triangle, *p2 );
|
||||
} else {
|
||||
std::runtime_error("EdgeEvent - collinear points not supported");
|
||||
assert(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (o1 == o2) {
|
||||
// Need to decide if we are rotating CW or CCW to get to a triangle
|
||||
// that will cross edge
|
||||
if (o1 == CW) {
|
||||
triangle = triangle->NeighborCCW(point);
|
||||
} else{
|
||||
triangle = triangle->NeighborCW(point);
|
||||
}
|
||||
EdgeEvent(tcx, ep, eq, triangle, point);
|
||||
} else {
|
||||
// This triangle crosses constraint so lets flippin start!
|
||||
FlipEdgeEvent(tcx, ep, eq, triangle, point);
|
||||
}
|
||||
}
|
||||
|
||||
bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq)
|
||||
{
|
||||
int index = triangle.EdgeIndex(&ep, &eq);
|
||||
|
||||
if (index != -1) {
|
||||
triangle.MarkConstrainedEdge(index);
|
||||
Triangle* t = triangle.GetNeighbor(index);
|
||||
if (t) {
|
||||
t->MarkConstrainedEdge(&ep, &eq);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node)
|
||||
{
|
||||
Triangle* triangle = new Triangle(point, *node.point, *node.next->point);
|
||||
|
||||
triangle->MarkNeighbor(*node.triangle);
|
||||
tcx.AddToMap(triangle);
|
||||
|
||||
Node* new_node = new Node(point);
|
||||
nodes_.push_back(new_node);
|
||||
|
||||
new_node->next = node.next;
|
||||
new_node->prev = &node;
|
||||
node.next->prev = new_node;
|
||||
node.next = new_node;
|
||||
|
||||
if (!Legalize(tcx, *triangle)) {
|
||||
tcx.MapTriangleToNodes(*triangle);
|
||||
}
|
||||
|
||||
return *new_node;
|
||||
}
|
||||
|
||||
void Sweep::Fill(SweepContext& tcx, Node& node)
|
||||
{
|
||||
Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point);
|
||||
|
||||
// TODO: should copy the constrained_edge value from neighbor triangles
|
||||
// for now constrained_edge values are copied during the legalize
|
||||
triangle->MarkNeighbor(*node.prev->triangle);
|
||||
triangle->MarkNeighbor(*node.triangle);
|
||||
|
||||
tcx.AddToMap(triangle);
|
||||
|
||||
// Update the advancing front
|
||||
node.prev->next = node.next;
|
||||
node.next->prev = node.prev;
|
||||
|
||||
// If it was legalized the triangle has already been mapped
|
||||
if (!Legalize(tcx, *triangle)) {
|
||||
tcx.MapTriangleToNodes(*triangle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
|
||||
{
|
||||
|
||||
// Fill right holes
|
||||
Node* node = n.next;
|
||||
|
||||
while (node->next) {
|
||||
// if HoleAngle exceeds 90 degrees then break.
|
||||
if (LargeHole_DontFill(node)) break;
|
||||
Fill(tcx, *node);
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
// Fill left holes
|
||||
node = n.prev;
|
||||
|
||||
while (node->prev) {
|
||||
// if HoleAngle exceeds 90 degrees then break.
|
||||
if (LargeHole_DontFill(node)) break;
|
||||
Fill(tcx, *node);
|
||||
node = node->prev;
|
||||
}
|
||||
|
||||
// Fill right basins
|
||||
if (n.next && n.next->next) {
|
||||
double angle = BasinAngle(n);
|
||||
if (angle < PI_3div4) {
|
||||
FillBasin(tcx, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// True if HoleAngle exceeds 90 degrees.
|
||||
bool Sweep::LargeHole_DontFill(Node* node) {
|
||||
|
||||
Node* nextNode = node->next;
|
||||
Node* prevNode = node->prev;
|
||||
if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point))
|
||||
return false;
|
||||
|
||||
// Check additional points on front.
|
||||
Node* next2Node = nextNode->next;
|
||||
// "..Plus.." because only want angles on same side as point being added.
|
||||
if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point))
|
||||
return false;
|
||||
|
||||
Node* prev2Node = prevNode->prev;
|
||||
// "..Plus.." because only want angles on same side as point being added.
|
||||
if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) {
|
||||
double angle = Angle(*origin, *pa, *pb);
|
||||
bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2));
|
||||
return exceeds90Degrees;
|
||||
}
|
||||
|
||||
bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) {
|
||||
double angle = Angle(*origin, *pa, *pb);
|
||||
bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0);
|
||||
return exceedsPlus90DegreesOrIsNegative;
|
||||
}
|
||||
|
||||
double Sweep::Angle(Point& origin, Point& pa, Point& pb) {
|
||||
/* Complex plane
|
||||
* ab = cosA +i*sinA
|
||||
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
|
||||
* atan2(y,x) computes the principal value of the argument function
|
||||
* applied to the complex number x+iy
|
||||
* Where x = ax*bx + ay*by
|
||||
* y = ax*by - ay*bx
|
||||
*/
|
||||
double px = origin.x;
|
||||
double py = origin.y;
|
||||
double ax = pa.x- px;
|
||||
double ay = pa.y - py;
|
||||
double bx = pb.x - px;
|
||||
double by = pb.y - py;
|
||||
double x = ax * by - ay * bx;
|
||||
double y = ax * bx + ay * by;
|
||||
double angle = atan2(x, y);
|
||||
return angle;
|
||||
}
|
||||
|
||||
double Sweep::BasinAngle(Node& node)
|
||||
{
|
||||
double ax = node.point->x - node.next->next->point->x;
|
||||
double ay = node.point->y - node.next->next->point->y;
|
||||
return atan2(ay, ax);
|
||||
}
|
||||
|
||||
double Sweep::HoleAngle(Node& node)
|
||||
{
|
||||
/* Complex plane
|
||||
* ab = cosA +i*sinA
|
||||
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
|
||||
* atan2(y,x) computes the principal value of the argument function
|
||||
* applied to the complex number x+iy
|
||||
* Where x = ax*bx + ay*by
|
||||
* y = ax*by - ay*bx
|
||||
*/
|
||||
double ax = node.next->point->x - node.point->x;
|
||||
double ay = node.next->point->y - node.point->y;
|
||||
double bx = node.prev->point->x - node.point->x;
|
||||
double by = node.prev->point->y - node.point->y;
|
||||
return atan2(ax * by - ay * bx, ax * bx + ay * by);
|
||||
}
|
||||
|
||||
bool Sweep::Legalize(SweepContext& tcx, Triangle& t)
|
||||
{
|
||||
// To legalize a triangle we start by finding if any of the three edges
|
||||
// violate the Delaunay condition
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (t.delaunay_edge[i])
|
||||
continue;
|
||||
|
||||
Triangle* ot = t.GetNeighbor(i);
|
||||
|
||||
if (ot) {
|
||||
Point* p = t.GetPoint(i);
|
||||
Point* op = ot->OppositePoint(t, *p);
|
||||
int oi = ot->Index(op);
|
||||
|
||||
// If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
|
||||
// then we should not try to legalize
|
||||
if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) {
|
||||
t.constrained_edge[i] = ot->constrained_edge[oi];
|
||||
continue;
|
||||
}
|
||||
|
||||
bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op);
|
||||
|
||||
if (inside) {
|
||||
// Lets mark this shared edge as Delaunay
|
||||
t.delaunay_edge[i] = true;
|
||||
ot->delaunay_edge[oi] = true;
|
||||
|
||||
// Lets rotate shared edge one vertex CW to legalize it
|
||||
RotateTrianglePair(t, *p, *ot, *op);
|
||||
|
||||
// We now got one valid Delaunay Edge shared by two triangles
|
||||
// This gives us 4 new edges to check for Delaunay
|
||||
|
||||
// Make sure that triangle to node mapping is done only one time for a specific triangle
|
||||
bool not_legalized = !Legalize(tcx, t);
|
||||
if (not_legalized) {
|
||||
tcx.MapTriangleToNodes(t);
|
||||
}
|
||||
|
||||
not_legalized = !Legalize(tcx, *ot);
|
||||
if (not_legalized)
|
||||
tcx.MapTriangleToNodes(*ot);
|
||||
|
||||
// Reset the Delaunay edges, since they only are valid Delaunay edges
|
||||
// until we add a new triangle or point.
|
||||
// XXX: need to think about this. Can these edges be tried after we
|
||||
// return to previous recursive level?
|
||||
t.delaunay_edge[i] = false;
|
||||
ot->delaunay_edge[oi] = false;
|
||||
|
||||
// If triangle have been legalized no need to check the other edges since
|
||||
// the recursive legalization will handles those so we can end here.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd)
|
||||
{
|
||||
double adx = pa.x - pd.x;
|
||||
double ady = pa.y - pd.y;
|
||||
double bdx = pb.x - pd.x;
|
||||
double bdy = pb.y - pd.y;
|
||||
|
||||
double adxbdy = adx * bdy;
|
||||
double bdxady = bdx * ady;
|
||||
double oabd = adxbdy - bdxady;
|
||||
|
||||
if (oabd <= 0)
|
||||
return false;
|
||||
|
||||
double cdx = pc.x - pd.x;
|
||||
double cdy = pc.y - pd.y;
|
||||
|
||||
double cdxady = cdx * ady;
|
||||
double adxcdy = adx * cdy;
|
||||
double ocad = cdxady - adxcdy;
|
||||
|
||||
if (ocad <= 0)
|
||||
return false;
|
||||
|
||||
double bdxcdy = bdx * cdy;
|
||||
double cdxbdy = cdx * bdy;
|
||||
|
||||
double alift = adx * adx + ady * ady;
|
||||
double blift = bdx * bdx + bdy * bdy;
|
||||
double clift = cdx * cdx + cdy * cdy;
|
||||
|
||||
double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
|
||||
|
||||
return det > 0;
|
||||
}
|
||||
|
||||
void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op)
|
||||
{
|
||||
Triangle* n1, *n2, *n3, *n4;
|
||||
n1 = t.NeighborCCW(p);
|
||||
n2 = t.NeighborCW(p);
|
||||
n3 = ot.NeighborCCW(op);
|
||||
n4 = ot.NeighborCW(op);
|
||||
|
||||
bool ce1, ce2, ce3, ce4;
|
||||
ce1 = t.GetConstrainedEdgeCCW(p);
|
||||
ce2 = t.GetConstrainedEdgeCW(p);
|
||||
ce3 = ot.GetConstrainedEdgeCCW(op);
|
||||
ce4 = ot.GetConstrainedEdgeCW(op);
|
||||
|
||||
bool de1, de2, de3, de4;
|
||||
de1 = t.GetDelunayEdgeCCW(p);
|
||||
de2 = t.GetDelunayEdgeCW(p);
|
||||
de3 = ot.GetDelunayEdgeCCW(op);
|
||||
de4 = ot.GetDelunayEdgeCW(op);
|
||||
|
||||
t.Legalize(p, op);
|
||||
ot.Legalize(op, p);
|
||||
|
||||
// Remap delaunay_edge
|
||||
ot.SetDelunayEdgeCCW(p, de1);
|
||||
t.SetDelunayEdgeCW(p, de2);
|
||||
t.SetDelunayEdgeCCW(op, de3);
|
||||
ot.SetDelunayEdgeCW(op, de4);
|
||||
|
||||
// Remap constrained_edge
|
||||
ot.SetConstrainedEdgeCCW(p, ce1);
|
||||
t.SetConstrainedEdgeCW(p, ce2);
|
||||
t.SetConstrainedEdgeCCW(op, ce3);
|
||||
ot.SetConstrainedEdgeCW(op, ce4);
|
||||
|
||||
// Remap neighbors
|
||||
// XXX: might optimize the markNeighbor by keeping track of
|
||||
// what side should be assigned to what neighbor after the
|
||||
// rotation. Now mark neighbor does lots of testing to find
|
||||
// the right side.
|
||||
t.ClearNeighbors();
|
||||
ot.ClearNeighbors();
|
||||
if (n1) ot.MarkNeighbor(*n1);
|
||||
if (n2) t.MarkNeighbor(*n2);
|
||||
if (n3) t.MarkNeighbor(*n3);
|
||||
if (n4) ot.MarkNeighbor(*n4);
|
||||
t.MarkNeighbor(ot);
|
||||
}
|
||||
|
||||
void Sweep::FillBasin(SweepContext& tcx, Node& node)
|
||||
{
|
||||
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
||||
tcx.basin.left_node = node.next->next;
|
||||
} else {
|
||||
tcx.basin.left_node = node.next;
|
||||
}
|
||||
|
||||
// Find the bottom and right node
|
||||
tcx.basin.bottom_node = tcx.basin.left_node;
|
||||
while (tcx.basin.bottom_node->next
|
||||
&& tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) {
|
||||
tcx.basin.bottom_node = tcx.basin.bottom_node->next;
|
||||
}
|
||||
if (tcx.basin.bottom_node == tcx.basin.left_node) {
|
||||
// No valid basin
|
||||
return;
|
||||
}
|
||||
|
||||
tcx.basin.right_node = tcx.basin.bottom_node;
|
||||
while (tcx.basin.right_node->next
|
||||
&& tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) {
|
||||
tcx.basin.right_node = tcx.basin.right_node->next;
|
||||
}
|
||||
if (tcx.basin.right_node == tcx.basin.bottom_node) {
|
||||
// No valid basins
|
||||
return;
|
||||
}
|
||||
|
||||
tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x;
|
||||
tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y;
|
||||
|
||||
FillBasinReq(tcx, tcx.basin.bottom_node);
|
||||
}
|
||||
|
||||
void Sweep::FillBasinReq(SweepContext& tcx, Node* node)
|
||||
{
|
||||
// if shallow stop filling
|
||||
if (IsShallow(tcx, *node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Fill(tcx, *node);
|
||||
|
||||
if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) {
|
||||
return;
|
||||
} else if (node->prev == tcx.basin.left_node) {
|
||||
Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point);
|
||||
if (o == CW) {
|
||||
return;
|
||||
}
|
||||
node = node->next;
|
||||
} else if (node->next == tcx.basin.right_node) {
|
||||
Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point);
|
||||
if (o == CCW) {
|
||||
return;
|
||||
}
|
||||
node = node->prev;
|
||||
} else {
|
||||
// Continue with the neighbor node with lowest Y value
|
||||
if (node->prev->point->y < node->next->point->y) {
|
||||
node = node->prev;
|
||||
} else {
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
FillBasinReq(tcx, node);
|
||||
}
|
||||
|
||||
bool Sweep::IsShallow(SweepContext& tcx, Node& node)
|
||||
{
|
||||
double height;
|
||||
|
||||
if (tcx.basin.left_highest) {
|
||||
height = tcx.basin.left_node->point->y - node.point->y;
|
||||
} else {
|
||||
height = tcx.basin.right_node->point->y - node.point->y;
|
||||
}
|
||||
|
||||
// if shallow stop filling
|
||||
if (tcx.basin.width > height) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||
{
|
||||
if (tcx.edge_event.right) {
|
||||
FillRightAboveEdgeEvent(tcx, edge, node);
|
||||
} else {
|
||||
FillLeftAboveEdgeEvent(tcx, edge, node);
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||
{
|
||||
while (node->next->point->x < edge->p->x) {
|
||||
// Check if next node is below the edge
|
||||
if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) {
|
||||
FillRightBelowEdgeEvent(tcx, edge, *node);
|
||||
} else {
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||
{
|
||||
if (node.point->x < edge->p->x) {
|
||||
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
||||
// Concave
|
||||
FillRightConcaveEdgeEvent(tcx, edge, node);
|
||||
} else{
|
||||
// Convex
|
||||
FillRightConvexEdgeEvent(tcx, edge, node);
|
||||
// Retry this one
|
||||
FillRightBelowEdgeEvent(tcx, edge, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||
{
|
||||
Fill(tcx, *node.next);
|
||||
if (node.next->point != edge->p) {
|
||||
// Next above or below edge?
|
||||
if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) {
|
||||
// Below
|
||||
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
||||
// Next is concave
|
||||
FillRightConcaveEdgeEvent(tcx, edge, node);
|
||||
} else {
|
||||
// Next is convex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||
{
|
||||
// Next concave or convex?
|
||||
if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) {
|
||||
// Concave
|
||||
FillRightConcaveEdgeEvent(tcx, edge, *node.next);
|
||||
} else{
|
||||
// Convex
|
||||
// Next above or below edge?
|
||||
if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) {
|
||||
// Below
|
||||
FillRightConvexEdgeEvent(tcx, edge, *node.next);
|
||||
} else{
|
||||
// Above
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
||||
{
|
||||
while (node->prev->point->x > edge->p->x) {
|
||||
// Check if next node is below the edge
|
||||
if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) {
|
||||
FillLeftBelowEdgeEvent(tcx, edge, *node);
|
||||
} else {
|
||||
node = node->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||
{
|
||||
if (node.point->x > edge->p->x) {
|
||||
if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
|
||||
// Concave
|
||||
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
||||
} else {
|
||||
// Convex
|
||||
FillLeftConvexEdgeEvent(tcx, edge, node);
|
||||
// Retry this one
|
||||
FillLeftBelowEdgeEvent(tcx, edge, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||
{
|
||||
// Next concave or convex?
|
||||
if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) {
|
||||
// Concave
|
||||
FillLeftConcaveEdgeEvent(tcx, edge, *node.prev);
|
||||
} else{
|
||||
// Convex
|
||||
// Next above or below edge?
|
||||
if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) {
|
||||
// Below
|
||||
FillLeftConvexEdgeEvent(tcx, edge, *node.prev);
|
||||
} else{
|
||||
// Above
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||
{
|
||||
Fill(tcx, *node.prev);
|
||||
if (node.prev->point != edge->p) {
|
||||
// Next above or below edge?
|
||||
if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) {
|
||||
// Below
|
||||
if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
|
||||
// Next is concave
|
||||
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
||||
} else{
|
||||
// Next is convex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p)
|
||||
{
|
||||
Triangle& ot = t->NeighborAcross(p);
|
||||
Point& op = *ot.OppositePoint(*t, p);
|
||||
|
||||
if (&ot == NULL) {
|
||||
// If we want to integrate the fillEdgeEvent do it here
|
||||
// With current implementation we should never get here
|
||||
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
|
||||
// Lets rotate shared edge one vertex CW
|
||||
RotateTrianglePair(*t, p, ot, op);
|
||||
tcx.MapTriangleToNodes(*t);
|
||||
tcx.MapTriangleToNodes(ot);
|
||||
|
||||
if (p == eq && op == ep) {
|
||||
if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) {
|
||||
t->MarkConstrainedEdge(&ep, &eq);
|
||||
ot.MarkConstrainedEdge(&ep, &eq);
|
||||
Legalize(tcx, *t);
|
||||
Legalize(tcx, ot);
|
||||
} else {
|
||||
// XXX: I think one of the triangles should be legalized here?
|
||||
}
|
||||
} else {
|
||||
Orientation o = Orient2d(eq, op, ep);
|
||||
t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op);
|
||||
FlipEdgeEvent(tcx, ep, eq, t, p);
|
||||
}
|
||||
} else {
|
||||
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
||||
FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP);
|
||||
EdgeEvent(tcx, ep, eq, t, p);
|
||||
}
|
||||
}
|
||||
|
||||
Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op)
|
||||
{
|
||||
if (o == CCW) {
|
||||
// ot is not crossing edge after flip
|
||||
int edge_index = ot.EdgeIndex(&p, &op);
|
||||
ot.delaunay_edge[edge_index] = true;
|
||||
Legalize(tcx, ot);
|
||||
ot.ClearDelunayEdges();
|
||||
return t;
|
||||
}
|
||||
|
||||
// t is not crossing edge after flip
|
||||
int edge_index = t.EdgeIndex(&p, &op);
|
||||
|
||||
t.delaunay_edge[edge_index] = true;
|
||||
Legalize(tcx, t);
|
||||
t.ClearDelunayEdges();
|
||||
return ot;
|
||||
}
|
||||
|
||||
Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op)
|
||||
{
|
||||
Orientation o2d = Orient2d(eq, op, ep);
|
||||
if (o2d == CW) {
|
||||
// Right
|
||||
return *ot.PointCCW(op);
|
||||
} else if (o2d == CCW) {
|
||||
// Left
|
||||
return *ot.PointCW(op);
|
||||
}
|
||||
|
||||
//throw new RuntimeException("[Unsupported] Opposing point on constrained edge");
|
||||
assert(0);
|
||||
|
||||
// Never executed, due tu assert( 0 ). Just to avoid compil warning
|
||||
return ep;
|
||||
}
|
||||
|
||||
void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
|
||||
Triangle& t, Point& p)
|
||||
{
|
||||
Triangle& ot = t.NeighborAcross(p);
|
||||
Point& op = *ot.OppositePoint(t, p);
|
||||
|
||||
if (&t.NeighborAcross(p) == NULL) {
|
||||
// If we want to integrate the fillEdgeEvent do it here
|
||||
// With current implementation we should never get here
|
||||
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
|
||||
// flip with new edge op->eq
|
||||
FlipEdgeEvent(tcx, eq, op, &ot, op);
|
||||
// TODO: Actually I just figured out that it should be possible to
|
||||
// improve this by getting the next ot and op before the the above
|
||||
// flip and continue the flipScanEdgeEvent here
|
||||
// set new ot and op here and loop back to inScanArea test
|
||||
// also need to set a new flip_triangle first
|
||||
// Turns out at first glance that this is somewhat complicated
|
||||
// so it will have to wait.
|
||||
} else{
|
||||
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
||||
FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
|
||||
}
|
||||
}
|
||||
|
||||
Sweep::~Sweep() {
|
||||
|
||||
// Clean up memory
|
||||
for( unsigned i = 0; i < nodes_.size(); i++ )
|
||||
{
|
||||
delete nodes_[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/**
|
||||
* Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and
|
||||
* Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
|
||||
* International Journal of Geographical Information Science
|
||||
*
|
||||
* "FlipScan" Constrained Edge Algorithm invented by Thomas Åhlén, thahlen@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef SWEEP_H
|
||||
#define SWEEP_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
class SweepContext;
|
||||
struct Node;
|
||||
struct Point;
|
||||
struct Edge;
|
||||
class Triangle;
|
||||
|
||||
class Sweep
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Triangulate
|
||||
*
|
||||
* @param tcx
|
||||
*/
|
||||
void Triangulate(SweepContext& tcx);
|
||||
|
||||
/**
|
||||
* Destructor - clean up memory
|
||||
*/
|
||||
~Sweep();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Start sweeping the Y-sorted point set from bottom to top
|
||||
*
|
||||
* @param tcx
|
||||
*/
|
||||
void SweepPoints(SweepContext& tcx);
|
||||
|
||||
/**
|
||||
* Find closes node to the left of the new point and
|
||||
* create a new triangle. If needed new holes and basins
|
||||
* will be filled to.
|
||||
*
|
||||
* @param tcx
|
||||
* @param point
|
||||
* @return
|
||||
*/
|
||||
Node& PointEvent(SweepContext& tcx, Point& point);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param tcx
|
||||
* @param edge
|
||||
* @param node
|
||||
*/
|
||||
void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
|
||||
|
||||
void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point);
|
||||
|
||||
/**
|
||||
* Creates a new front triangle and legalize it
|
||||
*
|
||||
* @param tcx
|
||||
* @param point
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node);
|
||||
|
||||
/**
|
||||
* Adds a triangle to the advancing front to fill a hole.
|
||||
* @param tcx
|
||||
* @param node - middle node, that is the bottom of the hole
|
||||
*/
|
||||
void Fill(SweepContext& tcx, Node& node);
|
||||
|
||||
/**
|
||||
* Returns true if triangle was legalized
|
||||
*/
|
||||
bool Legalize(SweepContext& tcx, Triangle& t);
|
||||
|
||||
/**
|
||||
* <b>Requirement</b>:<br>
|
||||
* 1. a,b and c form a triangle.<br>
|
||||
* 2. a and d is know to be on opposite side of bc<br>
|
||||
* <pre>
|
||||
* a
|
||||
* +
|
||||
* / \
|
||||
* / \
|
||||
* b/ \c
|
||||
* +-------+
|
||||
* / d \
|
||||
* / \
|
||||
* </pre>
|
||||
* <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
|
||||
* a,b and c<br>
|
||||
* d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
|
||||
* This preknowledge gives us a way to optimize the incircle test
|
||||
* @param pa - triangle point, opposite d
|
||||
* @param pb - triangle point
|
||||
* @param pc - triangle point
|
||||
* @param pd - point opposite a
|
||||
* @return true if d is inside circle, false if on circle edge
|
||||
*/
|
||||
bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd);
|
||||
|
||||
/**
|
||||
* Rotates a triangle pair one vertex CW
|
||||
*<pre>
|
||||
* n2 n2
|
||||
* P +-----+ P +-----+
|
||||
* | t /| |\ t |
|
||||
* | / | | \ |
|
||||
* n1| / |n3 n1| \ |n3
|
||||
* | / | after CW | \ |
|
||||
* |/ oT | | oT \|
|
||||
* +-----+ oP +-----+
|
||||
* n4 n4
|
||||
* </pre>
|
||||
*/
|
||||
void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op);
|
||||
|
||||
/**
|
||||
* Fills holes in the Advancing Front
|
||||
*
|
||||
*
|
||||
* @param tcx
|
||||
* @param n
|
||||
*/
|
||||
void FillAdvancingFront(SweepContext& tcx, Node& n);
|
||||
|
||||
// Decision-making about when to Fill hole.
|
||||
// Contributed by ToolmakerSteve2
|
||||
bool LargeHole_DontFill(Node* node);
|
||||
bool AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb);
|
||||
bool AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb);
|
||||
double Angle(Point& origin, Point& pa, Point& pb);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node - middle node
|
||||
* @return the angle between 3 front nodes
|
||||
*/
|
||||
double HoleAngle(Node& node);
|
||||
|
||||
/**
|
||||
* The basin angle is decided against the horizontal line [1,0]
|
||||
*/
|
||||
double BasinAngle(Node& node);
|
||||
|
||||
/**
|
||||
* Fills a basin that has formed on the Advancing Front to the right
|
||||
* of given node.<br>
|
||||
* First we decide a left,bottom and right node that forms the
|
||||
* boundaries of the basin. Then we do a reqursive fill.
|
||||
*
|
||||
* @param tcx
|
||||
* @param node - starting node, this or next node will be left node
|
||||
*/
|
||||
void FillBasin(SweepContext& tcx, Node& node);
|
||||
|
||||
/**
|
||||
* Recursive algorithm to fill a Basin with triangles
|
||||
*
|
||||
* @param tcx
|
||||
* @param node - bottom_node
|
||||
*/
|
||||
void FillBasinReq(SweepContext& tcx, Node* node);
|
||||
|
||||
bool IsShallow(SweepContext& tcx, Node& node);
|
||||
|
||||
bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq);
|
||||
|
||||
void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
|
||||
|
||||
void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
|
||||
|
||||
void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
|
||||
|
||||
void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
|
||||
|
||||
void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
|
||||
|
||||
void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
|
||||
|
||||
void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
|
||||
|
||||
void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
|
||||
|
||||
void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
|
||||
|
||||
void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p);
|
||||
|
||||
/**
|
||||
* After a flip we have two triangles and know that only one will still be
|
||||
* intersecting the edge. So decide which to contiune with and legalize the other
|
||||
*
|
||||
* @param tcx
|
||||
* @param o - should be the result of an orient2d( eq, op, ep )
|
||||
* @param t - triangle 1
|
||||
* @param ot - triangle 2
|
||||
* @param p - a point shared by both triangles
|
||||
* @param op - another point shared by both triangles
|
||||
* @return returns the triangle still intersecting the edge
|
||||
*/
|
||||
Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op);
|
||||
|
||||
/**
|
||||
* When we need to traverse from one triangle to the next we need
|
||||
* the point in current triangle that is the opposite point to the next
|
||||
* triangle.
|
||||
*
|
||||
* @param ep
|
||||
* @param eq
|
||||
* @param ot
|
||||
* @param op
|
||||
* @return
|
||||
*/
|
||||
Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op);
|
||||
|
||||
/**
|
||||
* Scan part of the FlipScan algorithm<br>
|
||||
* When a triangle pair isn't flippable we will scan for the next
|
||||
* point that is inside the flip triangle scan area. When found
|
||||
* we generate a new flipEdgeEvent
|
||||
*
|
||||
* @param tcx
|
||||
* @param ep - last point on the edge we are traversing
|
||||
* @param eq - first point on the edge we are traversing
|
||||
* @param flipTriangle - the current triangle sharing the point eq with edge
|
||||
* @param t
|
||||
* @param p
|
||||
*/
|
||||
void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p);
|
||||
|
||||
void FinalizationPolygon(SweepContext& tcx);
|
||||
|
||||
std::vector<Node*> nodes_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "sweep_context.h"
|
||||
#include <algorithm>
|
||||
#include "advancing_front.h"
|
||||
|
||||
namespace p2t {
|
||||
|
||||
SweepContext::SweepContext(std::vector<Point*> polyline) :
|
||||
front_(0),
|
||||
head_(0),
|
||||
tail_(0),
|
||||
af_head_(0),
|
||||
af_middle_(0),
|
||||
af_tail_(0)
|
||||
{
|
||||
basin = Basin();
|
||||
edge_event = EdgeEvent();
|
||||
|
||||
points_ = polyline;
|
||||
|
||||
InitEdges(points_);
|
||||
}
|
||||
|
||||
void SweepContext::AddHole(std::vector<Point*> polyline)
|
||||
{
|
||||
InitEdges(polyline);
|
||||
for(unsigned int i = 0; i < polyline.size(); i++) {
|
||||
points_.push_back(polyline[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void SweepContext::AddPoint(Point* point) {
|
||||
points_.push_back(point);
|
||||
}
|
||||
|
||||
std::vector<Triangle*> SweepContext::GetTriangles()
|
||||
{
|
||||
return triangles_;
|
||||
}
|
||||
|
||||
std::list<Triangle*> SweepContext::GetMap()
|
||||
{
|
||||
return map_;
|
||||
}
|
||||
|
||||
void SweepContext::InitTriangulation()
|
||||
{
|
||||
double xmax(points_[0]->x), xmin(points_[0]->x);
|
||||
double ymax(points_[0]->y), ymin(points_[0]->y);
|
||||
|
||||
// Calculate bounds.
|
||||
for (unsigned int i = 0; i < points_.size(); i++) {
|
||||
Point& p = *points_[i];
|
||||
if (p.x > xmax)
|
||||
xmax = p.x;
|
||||
if (p.x < xmin)
|
||||
xmin = p.x;
|
||||
if (p.y > ymax)
|
||||
ymax = p.y;
|
||||
if (p.y < ymin)
|
||||
ymin = p.y;
|
||||
}
|
||||
|
||||
double dx = kAlpha * (xmax - xmin);
|
||||
double dy = kAlpha * (ymax - ymin);
|
||||
head_ = new Point(xmax + dx, ymin - dy);
|
||||
tail_ = new Point(xmin - dx, ymin - dy);
|
||||
|
||||
// Sort points along y-axis
|
||||
std::sort(points_.begin(), points_.end(), cmp);
|
||||
|
||||
}
|
||||
|
||||
void SweepContext::InitEdges(std::vector<Point*> polyline)
|
||||
{
|
||||
int num_points = polyline.size();
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
int j = i < num_points - 1 ? i + 1 : 0;
|
||||
edge_list.push_back(new Edge(*polyline[i], *polyline[j]));
|
||||
}
|
||||
}
|
||||
|
||||
Point* SweepContext::GetPoint(const int& index)
|
||||
{
|
||||
return points_[index];
|
||||
}
|
||||
|
||||
void SweepContext::AddToMap(Triangle* triangle)
|
||||
{
|
||||
map_.push_back(triangle);
|
||||
}
|
||||
|
||||
Node& SweepContext::LocateNode(Point& point)
|
||||
{
|
||||
// TODO implement search tree
|
||||
return *front_->LocateNode(point.x);
|
||||
}
|
||||
|
||||
void SweepContext::CreateAdvancingFront(std::vector<Node*> nodes)
|
||||
{
|
||||
|
||||
(void) nodes;
|
||||
// Initial triangle
|
||||
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
|
||||
|
||||
map_.push_back(triangle);
|
||||
|
||||
af_head_ = new Node(*triangle->GetPoint(1), *triangle);
|
||||
af_middle_ = new Node(*triangle->GetPoint(0), *triangle);
|
||||
af_tail_ = new Node(*triangle->GetPoint(2));
|
||||
front_ = new AdvancingFront(*af_head_, *af_tail_);
|
||||
|
||||
// TODO: More intuitive if head is middles next and not previous?
|
||||
// so swap head and tail
|
||||
af_head_->next = af_middle_;
|
||||
af_middle_->next = af_tail_;
|
||||
af_middle_->prev = af_head_;
|
||||
af_tail_->prev = af_middle_;
|
||||
}
|
||||
|
||||
void SweepContext::RemoveNode(Node* node)
|
||||
{
|
||||
delete node;
|
||||
}
|
||||
|
||||
void SweepContext::MapTriangleToNodes(Triangle& t)
|
||||
{
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!t.GetNeighbor(i)) {
|
||||
Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i)));
|
||||
if (n)
|
||||
n->triangle = &t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SweepContext::RemoveFromMap(Triangle* triangle)
|
||||
{
|
||||
map_.remove(triangle);
|
||||
}
|
||||
|
||||
void SweepContext::MeshClean(Triangle& triangle)
|
||||
{
|
||||
std::vector<Triangle *> triangles;
|
||||
triangles.push_back(&triangle);
|
||||
|
||||
while(!triangles.empty()){
|
||||
Triangle *t = triangles.back();
|
||||
triangles.pop_back();
|
||||
|
||||
if (t != NULL && !t->IsInterior()) {
|
||||
t->IsInterior(true);
|
||||
triangles_.push_back(t);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (!t->constrained_edge[i])
|
||||
triangles.push_back(t->GetNeighbor(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SweepContext::~SweepContext()
|
||||
{
|
||||
|
||||
// Clean up memory
|
||||
|
||||
delete head_;
|
||||
delete tail_;
|
||||
delete front_;
|
||||
delete af_head_;
|
||||
delete af_middle_;
|
||||
delete af_tail_;
|
||||
|
||||
typedef std::list<Triangle*> type_list;
|
||||
|
||||
for(type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) {
|
||||
Triangle* ptr = *iter;
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < edge_list.size(); i++) {
|
||||
delete edge_list[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SWEEP_CONTEXT_H
|
||||
#define SWEEP_CONTEXT_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
// Inital triangle factor, seed triangle will extend 30% of
|
||||
// PointSet width to both left and right.
|
||||
const double kAlpha = 0.3;
|
||||
|
||||
struct Point;
|
||||
class Triangle;
|
||||
struct Node;
|
||||
struct Edge;
|
||||
class AdvancingFront;
|
||||
|
||||
class SweepContext {
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
SweepContext(std::vector<Point*> polyline);
|
||||
/// Destructor
|
||||
~SweepContext();
|
||||
|
||||
void set_head(Point* p1);
|
||||
|
||||
Point* head();
|
||||
|
||||
void set_tail(Point* p1);
|
||||
|
||||
Point* tail();
|
||||
|
||||
int point_count();
|
||||
|
||||
Node& LocateNode(Point& point);
|
||||
|
||||
void RemoveNode(Node* node);
|
||||
|
||||
void CreateAdvancingFront(std::vector<Node*> nodes);
|
||||
|
||||
/// Try to map a node to all sides of this triangle that don't have a neighbor
|
||||
void MapTriangleToNodes(Triangle& t);
|
||||
|
||||
void AddToMap(Triangle* triangle);
|
||||
|
||||
Point* GetPoint(const int& index);
|
||||
|
||||
Point* GetPoints();
|
||||
|
||||
void RemoveFromMap(Triangle* triangle);
|
||||
|
||||
void AddHole(std::vector<Point*> polyline);
|
||||
|
||||
void AddPoint(Point* point);
|
||||
|
||||
AdvancingFront* front();
|
||||
|
||||
void MeshClean(Triangle& triangle);
|
||||
|
||||
std::vector<Triangle*> GetTriangles();
|
||||
std::list<Triangle*> GetMap();
|
||||
|
||||
std::vector<Edge*> edge_list;
|
||||
|
||||
struct Basin {
|
||||
Node* left_node;
|
||||
Node* bottom_node;
|
||||
Node* right_node;
|
||||
double width;
|
||||
bool left_highest;
|
||||
|
||||
Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
left_node = NULL;
|
||||
bottom_node = NULL;
|
||||
right_node = NULL;
|
||||
width = 0.0;
|
||||
left_highest = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct EdgeEvent {
|
||||
Edge* constrained_edge;
|
||||
bool right;
|
||||
|
||||
EdgeEvent() : constrained_edge(NULL), right(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
Basin basin;
|
||||
EdgeEvent edge_event;
|
||||
|
||||
private:
|
||||
|
||||
friend class Sweep;
|
||||
|
||||
std::vector<Triangle*> triangles_;
|
||||
std::list<Triangle*> map_;
|
||||
std::vector<Point*> points_;
|
||||
|
||||
// Advancing front
|
||||
AdvancingFront* front_;
|
||||
// head point used with advancing front
|
||||
Point* head_;
|
||||
// tail point used with advancing front
|
||||
Point* tail_;
|
||||
|
||||
Node *af_head_, *af_middle_, *af_tail_;
|
||||
|
||||
void InitTriangulation();
|
||||
void InitEdges(std::vector<Point*> polyline);
|
||||
|
||||
};
|
||||
|
||||
inline AdvancingFront* SweepContext::front()
|
||||
{
|
||||
return front_;
|
||||
}
|
||||
|
||||
inline int SweepContext::point_count()
|
||||
{
|
||||
return points_.size();
|
||||
}
|
||||
|
||||
inline void SweepContext::set_head(Point* p1)
|
||||
{
|
||||
head_ = p1;
|
||||
}
|
||||
|
||||
inline Point* SweepContext::head()
|
||||
{
|
||||
return head_;
|
||||
}
|
||||
|
||||
inline void SweepContext::set_tail(Point* p1)
|
||||
{
|
||||
tail_ = p1;
|
||||
}
|
||||
|
||||
inline Point* SweepContext::tail()
|
||||
{
|
||||
return tail_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||
* Copyright (C) 2012-2014 KiCad Developers, see CHANGELOG.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 polygons_defs.h
|
||||
* definitions to use boost::polygon in KiCad.
|
||||
*/
|
||||
|
||||
#ifndef _POLYGONS_DEFS_H_
|
||||
#define _POLYGONS_DEFS_H_
|
||||
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
|
||||
// Define some types used here from boost::polygon
|
||||
namespace bpl = boost::polygon; // bpl = boost polygon library
|
||||
using namespace bpl::operators; // +, -, =, ...
|
||||
|
||||
// Definitions needed by boost::polygon
|
||||
typedef int coordinate_type;
|
||||
|
||||
/**
|
||||
* KI_POLYGON defines a single polygon ( boost::polygon_data type.
|
||||
* When holes are created in a KPolygon, they are
|
||||
* linked to main outline by overlapping segments,
|
||||
* so there is always one polygon and one list of corners
|
||||
* coordinates are int
|
||||
*/
|
||||
typedef bpl::polygon_data<int> KI_POLYGON;
|
||||
|
||||
/**
|
||||
* KI_POLYGON_SET defines a set of single KI_POLYGON.
|
||||
* A KI_POLYGON_SET is used to store a set of polygons
|
||||
* when performing operations between 2 polygons
|
||||
* or 2 sets of polygons
|
||||
* The result of operations like and, xor... between 2 polygons
|
||||
* is always stored in a KI_POLYGON_SET, because these operations
|
||||
* can create many polygons
|
||||
*/
|
||||
typedef std::vector<KI_POLYGON> KI_POLYGON_SET;
|
||||
|
||||
/**
|
||||
* KI_POLY_POINT defines a point for boost::polygon.
|
||||
* KI_POLY_POINT store x and y coordinates (int)
|
||||
*/
|
||||
typedef bpl::point_data<int> KI_POLY_POINT;
|
||||
|
||||
/**
|
||||
* KI_POLYGON_WITH_HOLES defines a single polygon with holes
|
||||
* When holes are created in a KI_POLYGON_WITH_HOLES, they are
|
||||
* stored as separate single polygons,
|
||||
* KI_POLYGON_WITH_HOLES store always one polygon for the external outline
|
||||
* and one list of polygons (holes) which can be empty
|
||||
*/
|
||||
typedef bpl::polygon_with_holes_data<int> KI_POLYGON_WITH_HOLES;
|
||||
|
||||
/**
|
||||
* KI_POLYGON_WITH_HOLES_SET defines a set of KI_POLYGON_WITH_HOLES.
|
||||
* A KI_POLYGON_WITH_HOLES_SET is used to store a set of polygons with holes
|
||||
* when performing operations between 2 polygons
|
||||
* or 2 sets of polygons with holes
|
||||
* The result of operations like and, xor... between 2 polygons with holes
|
||||
* is always stored in a KI_POLYGON_WITH_HOLES_SET, because these operations
|
||||
* can create many separate polygons with holespolygons
|
||||
*/
|
||||
typedef std::vector<KI_POLYGON_WITH_HOLES> KI_POLYGON_WITH_HOLES_SET;
|
||||
|
||||
|
||||
#endif // #ifndef _POLYGONS_DEFS_H_
|
Loading…
Reference in New Issue