diff --git a/3d-viewer/3d_canvas.h b/3d-viewer/3d_canvas.h index c82f58a092..2f59126ee6 100644 --- a/3d-viewer/3d_canvas.h +++ b/3d-viewer/3d_canvas.h @@ -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 ); diff --git a/3d-viewer/3d_draw.cpp b/3d-viewer/3d_draw.cpp index 390298e05e..9fe8219876 100644 --- a/3d-viewer/3d_draw.cpp +++ b/3d-viewer/3d_draw.cpp @@ -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 diff --git a/3d-viewer/3d_draw_basic_functions.cpp b/3d-viewer/3d_draw_basic_functions.cpp index 216a61ac8a..f17bfa2a83 100644 --- a/3d-viewer/3d_draw_basic_functions.cpp +++ b/3d-viewer/3d_draw_basic_functions.cpp @@ -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 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 ) { diff --git a/3d-viewer/3d_draw_basic_functions.h b/3d-viewer/3d_draw_basic_functions.h index 243de7db54..1a90c4d6b5 100644 --- a/3d-viewer/3d_draw_basic_functions.h +++ b/3d-viewer/3d_draw_basic_functions.h @@ -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 ); diff --git a/3d-viewer/3d_draw_board_body.cpp b/3d-viewer/3d_draw_board_body.cpp index bde6004551..c5511f11bd 100644 --- a/3d-viewer/3d_draw_board_body.cpp +++ b/3d-viewer/3d_draw_board_body.cpp @@ -57,8 +57,6 @@ #include #include <3d_draw_basic_functions.h> #include -#include - #include #include @@ -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) diff --git a/3d-viewer/3d_draw_helper_functions.cpp b/3d-viewer/3d_draw_helper_functions.cpp index a726cc91c1..e9f79d7f6e 100644 --- a/3d-viewer/3d_draw_helper_functions.cpp +++ b/3d-viewer/3d_draw_helper_functions.cpp @@ -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 ); } } diff --git a/common/common_plotDXF_functions.cpp b/common/common_plotDXF_functions.cpp index 4567ee67ab..da5ef033ce 100644 --- a/common/common_plotDXF_functions.cpp +++ b/common/common_plotDXF_functions.cpp @@ -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& 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& 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& 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 ); diff --git a/common/convert_basic_shapes_to_polygon.cpp b/common/convert_basic_shapes_to_polygon.cpp index fbbd3c17a0..38c5b79bf4 100644 --- a/common/convert_basic_shapes_to_polygon.cpp +++ b/common/convert_basic_shapes_to_polygon.cpp @@ -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 ); } diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index b70f84e1f8..6c223d7fb3 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -4,9 +4,6 @@ * Copyright (C) 2015 CERN * @author Tomasz Wlostowski * - * 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 #include +#include +#include #include #include #include #include -#include -#include -#include +#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 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::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::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::max(); + int64_t x_min = std::numeric_limits::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::max(); - + int64_t x_min = std::numeric_limits::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; -} diff --git a/gerbview/class_gerber_draw_item.cpp b/gerbview/class_gerber_draw_item.cpp index b91539560c..0e36b73250 100644 --- a/gerbview/class_gerber_draw_item.cpp +++ b/gerbview/class_gerber_draw_item.cpp @@ -27,6 +27,7 @@ */ #include +#include #include #include #include diff --git a/include/convert_basic_shapes_to_polygon.h b/include/convert_basic_shapes_to_polygon.h index 23940ec42a..3e45baf6b6 100644 --- a/include/convert_basic_shapes_to_polygon.h +++ b/include/convert_basic_shapes_to_polygon.h @@ -33,8 +33,8 @@ #include #include #include +#include -#include /** * 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 ); diff --git a/include/geometry/shape_line_chain.h b/include/geometry/shape_line_chain.h index 16b80f32b5..8bf0410806 100644 --- a/include/geometry/shape_line_chain.h +++ b/include/geometry/shape_line_chain.h @@ -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 m_points; diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index 58f7e46beb..63921493d6 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -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 POLYGON; - - /** - * Class ITERATOR_TEMPLATE - * - * Base class for iterating over all vertices in a given SHAPE_POLY_SET - */ - template - 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 ITERATOR; - typedef ITERATOR_TEMPLATE 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( 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 Polyset; + typedef std::vector Polyset; Polyset m_polys; }; diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index e38ac4b0ec..f4623c251c 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -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 +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; } diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index d4e8d39ea3..f1058673b8 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -2741,8 +2741,8 @@ wxString BOARD::GetNextModuleReferenceWithPrefix( const wxString& aPrefix, * return true if success, false if a contour is not valid */ #include -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: diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index 6510b89514..3829f86a37 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -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 diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index d294e22316..23d8bcff8d 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -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; diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index a7aba66618..17537c1ea2 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -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 ); diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index c415baccae..1729049de7 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -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; /** diff --git a/pcbnew/class_pcb_text.h b/pcbnew/class_pcb_text.h index b1f1d31e7e..e1aeee7e4d 100644 --- a/pcbnew/class_pcb_text.h +++ b/pcbnew/class_pcb_text.h @@ -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; diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index fd37d95675..f2e66ae67b 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -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; diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index e9b6c27977..abec9aede3 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -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; -} \ No newline at end of file +} + + +/* 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 ); +} diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index 08d1259b5f..8bed725beb 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -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; }; diff --git a/pcbnew/exporters/export_vrml.cpp b/pcbnew/exporters/export_vrml.cpp index 40dba6eaaa..07c8b41be8 100644 --- a/pcbnew/exporters/export_vrml.cpp +++ b/pcbnew/exporters/export_vrml.cpp @@ -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; } } } diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index d701b8366e..fc46fc4599 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -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" ); diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 2e5bf804de..94e4c74df7 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -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 ); } diff --git a/pcbnew/onrightclick.cpp b/pcbnew/onrightclick.cpp index 52098031e5..e3571c36b3 100644 --- a/pcbnew/onrightclick.cpp +++ b/pcbnew/onrightclick.cpp @@ -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 ) ); diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 26e332ad6b..c23be9aa51 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -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 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::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(); + } } } } diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index d92f06830a..898551ea8d 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -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 diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index ec06a9ffcd..64843da411 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -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 ); } diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index bff7f5a5dd..99b600aa77 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -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] ) diff --git a/pcbnew/ratsnest_data.cpp b/pcbnew/ratsnest_data.cpp index 524c84529c..74c9cc1d52 100644 --- a/pcbnew/ratsnest_data.cpp +++ b/pcbnew/ratsnest_data.cpp @@ -44,8 +44,6 @@ #include #include -#include - #include #include #include @@ -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& 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; diff --git a/pcbnew/ratsnest_data.h b/pcbnew/ratsnest_data.h index b1bd137a12..4c8793f92b 100644 --- a/pcbnew/ratsnest_data.h +++ b/pcbnew/ratsnest_data.h @@ -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; diff --git a/pcbnew/specctra.h b/pcbnew/specctra.h index 540fcf9b13..06ab58753e 100644 --- a/pcbnew/specctra.h +++ b/pcbnew/specctra.h @@ -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 ); }; diff --git a/pcbnew/specctra_export.cpp b/pcbnew/specctra_export.cpp index 51155ef6e5..e1c75b535b 100644 --- a/pcbnew/specctra_export.cpp +++ b/pcbnew/specctra_export.cpp @@ -53,8 +53,6 @@ #include -#include - #include 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; diff --git a/pcbnew/zone_filling_algorithm.cpp b/pcbnew/zone_filling_algorithm.cpp index e56e786eb9..400d977a69 100644 --- a/pcbnew/zone_filling_algorithm.cpp +++ b/pcbnew/zone_filling_algorithm.cpp @@ -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 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; diff --git a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp index 274f66e3be..e779afb3be 100644 --- a/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp +++ b/pcbnew/zones_convert_brd_items_to_polygons_with_Boost.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -69,7 +70,7 @@ #include -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 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 ); } diff --git a/pcbnew/zones_convert_to_polygons_aux_functions.cpp b/pcbnew/zones_convert_to_polygons_aux_functions.cpp index e9af2b1a6c..bdd789bf3a 100644 --- a/pcbnew/zones_convert_to_polygons_aux_functions.cpp +++ b/pcbnew/zones_convert_to_polygons_aux_functions.cpp @@ -27,6 +27,7 @@ */ #include +#include #include #include #include @@ -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 ); } } } diff --git a/pcbnew/zones_polygons_insulated_copper_islands.cpp b/pcbnew/zones_polygons_insulated_copper_islands.cpp index bfb861e12e..5e72a64497 100644 --- a/pcbnew/zones_polygons_insulated_copper_islands.cpp +++ b/pcbnew/zones_polygons_insulated_copper_islands.cpp @@ -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; +} diff --git a/pcbnew/zones_polygons_test_connections.cpp b/pcbnew/zones_polygons_test_connections.cpp index 6306260699..3f45b0652c 100644 --- a/pcbnew/zones_polygons_test_connections.cpp +++ b/pcbnew/zones_polygons_test_connections.cpp @@ -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 } diff --git a/pcbnew/zones_test_and_combine_areas.cpp b/pcbnew/zones_test_and_combine_areas.cpp index 4f8c080305..4f2c61b7e4 100644 --- a/pcbnew/zones_test_and_combine_areas.cpp +++ b/pcbnew/zones_test_and_combine_areas.cpp @@ -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 ); diff --git a/polygon/CMakeLists.txt b/polygon/CMakeLists.txt index 2f09cbe9a9..26eade92c7 100644 --- a/polygon/CMakeLists.txt +++ b/polygon/CMakeLists.txt @@ -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}) diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp index 3229d4b98a..cccd65ad18 100644 --- a/polygon/PolyLine.cpp +++ b/polygon/PolyLine.cpp @@ -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* 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 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* 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 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 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 + +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 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; -} \ No newline at end of file diff --git a/polygon/PolyLine.h b/polygon/PolyLine.h index 5ac5afd3bf..600bdf1e0e 100644 --- a/polygon/PolyLine.h +++ b/polygon/PolyLine.h @@ -53,8 +53,8 @@ #include // for wxPoint definition #include // for LAYER_NUM definition #include // for EDA_RECT definition - -#include // fixme +#include +#include 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 & 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 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 diff --git a/polygon/poly2tri/common/shapes.cc b/polygon/poly2tri/common/shapes.cc new file mode 100644 index 0000000000..06eb1f80c5 --- /dev/null +++ b/polygon/poly2tri/common/shapes.cc @@ -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 + +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"; +} +} diff --git a/polygon/poly2tri/common/shapes.h b/polygon/poly2tri/common/shapes.h new file mode 100644 index 0000000000..c65f485eaf --- /dev/null +++ b/polygon/poly2tri/common/shapes.h @@ -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 +#include +#include +#include + +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_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 diff --git a/polygon/poly2tri/common/utils.h b/polygon/poly2tri/common/utils.h new file mode 100644 index 0000000000..3de9fb11bf --- /dev/null +++ b/polygon/poly2tri/common/utils.h @@ -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 +#include + +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
+ * Positive if CCW
+ * Negative if CW
+ * 0 if collinear
+ *
+ * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * 
+ */ +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 diff --git a/polygon/poly2tri/poly2tri.h b/polygon/poly2tri/poly2tri.h new file mode 100644 index 0000000000..487755e2e9 --- /dev/null +++ b/polygon/poly2tri/poly2tri.h @@ -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 + diff --git a/polygon/poly2tri/sweep/advancing_front.cc b/polygon/poly2tri/sweep/advancing_front.cc new file mode 100644 index 0000000000..019df4a6eb --- /dev/null +++ b/polygon/poly2tri/sweep/advancing_front.cc @@ -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() +{ +} + +} + diff --git a/polygon/poly2tri/sweep/advancing_front.h b/polygon/poly2tri/sweep/advancing_front.h new file mode 100644 index 0000000000..bab73d449c --- /dev/null +++ b/polygon/poly2tri/sweep/advancing_front.h @@ -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 diff --git a/polygon/poly2tri/sweep/cdt.cc b/polygon/poly2tri/sweep/cdt.cc new file mode 100644 index 0000000000..d7838257c7 --- /dev/null +++ b/polygon/poly2tri/sweep/cdt.cc @@ -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 polyline) +{ + sweep_context_ = new SweepContext(polyline); + sweep_ = new Sweep; +} + +void CDT::AddHole(std::vector polyline) +{ + sweep_context_->AddHole(polyline); +} + +void CDT::AddPoint(Point* point) { + sweep_context_->AddPoint(point); +} + +void CDT::Triangulate() +{ + sweep_->Triangulate(*sweep_context_); +} + +std::vector CDT::GetTriangles() +{ + return sweep_context_->GetTriangles(); +} + +std::list CDT::GetMap() +{ + return sweep_context_->GetMap(); +} + +CDT::~CDT() +{ + delete sweep_context_; + delete sweep_; +} + +} + diff --git a/polygon/poly2tri/sweep/cdt.h b/polygon/poly2tri/sweep/cdt.h new file mode 100644 index 0000000000..3e6f024086 --- /dev/null +++ b/polygon/poly2tri/sweep/cdt.h @@ -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 + * + */ + +namespace p2t { + +class CDT +{ +public: + + /** + * Constructor - add polyline with non repeating points + * + * @param polyline + */ + CDT(std::vector polyline); + + /** + * Destructor - clean up memory + */ + ~CDT(); + + /** + * Add a hole + * + * @param polyline + */ + void AddHole(std::vector 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 GetTriangles(); + + /** + * Get triangle map + */ + std::list GetMap(); + + private: + + /** + * Internals + */ + + SweepContext* sweep_context_; + Sweep* sweep_; + +}; + +} + +#endif diff --git a/polygon/poly2tri/sweep/sweep.cc b/polygon/poly2tri/sweep/sweep.cc new file mode 100644 index 0000000000..75e7adf9ee --- /dev/null +++ b/polygon/poly2tri/sweep/sweep.cc @@ -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 +#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]; + } + +} + +} + diff --git a/polygon/poly2tri/sweep/sweep.h b/polygon/poly2tri/sweep/sweep.h new file mode 100644 index 0000000000..07822d1f65 --- /dev/null +++ b/polygon/poly2tri/sweep/sweep.h @@ -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 + +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); + + /** + * Requirement:
+ * 1. a,b and c form a triangle.
+ * 2. a and d is know to be on opposite side of bc
+ *
+   *                a
+   *                +
+   *               / \
+   *              /   \
+   *            b/     \c
+   *            +-------+
+   *           /    d    \
+   *          /           \
+   * 
+ * Fact: d has to be in area B to have a chance to be inside the circle formed by + * a,b and c
+ * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
+ * 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 + *
+   *       n2                    n2
+   *  P +-----+             P +-----+
+   *    | t  /|               |\  t |
+   *    |   / |               | \   |
+   *  n1|  /  |n3           n1|  \  |n3
+   *    | /   |    after CW   |   \ |
+   *    |/ oT |               | oT \|
+   *    +-----+ oP            +-----+
+   *       n4                    n4
+   * 
+ */ + 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.
+ * 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
+ * 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 nodes_; + +}; + +} + +#endif diff --git a/polygon/poly2tri/sweep/sweep_context.cc b/polygon/poly2tri/sweep/sweep_context.cc new file mode 100644 index 0000000000..6c0b0447dd --- /dev/null +++ b/polygon/poly2tri/sweep/sweep_context.cc @@ -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 +#include "advancing_front.h" + +namespace p2t { + +SweepContext::SweepContext(std::vector 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 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 SweepContext::GetTriangles() +{ + return triangles_; +} + +std::list 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 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 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 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 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]; + } + +} + +} diff --git a/polygon/poly2tri/sweep/sweep_context.h b/polygon/poly2tri/sweep/sweep_context.h new file mode 100644 index 0000000000..1010c0e8a8 --- /dev/null +++ b/polygon/poly2tri/sweep/sweep_context.h @@ -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 +#include +#include + +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 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 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 polyline); + +void AddPoint(Point* point); + +AdvancingFront* front(); + +void MeshClean(Triangle& triangle); + +std::vector GetTriangles(); +std::list GetMap(); + +std::vector 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 triangles_; +std::list map_; +std::vector 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 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 diff --git a/polygon/polygons_defs.h b/polygon/polygons_defs.h new file mode 100644 index 0000000000..82bccfab77 --- /dev/null +++ b/polygon/polygons_defs.h @@ -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 + +// 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 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_SET; + +/** + * KI_POLY_POINT defines a point for boost::polygon. + * KI_POLY_POINT store x and y coordinates (int) + */ +typedef bpl::point_data 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 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_SET; + + +#endif // #ifndef _POLYGONS_DEFS_H_