summaryrefslogtreecommitdiff
path: root/polygon
diff options
context:
space:
mode:
Diffstat (limited to 'polygon')
-rw-r--r--polygon/CMakeLists.txt17
-rw-r--r--polygon/PolyLine.cpp1374
-rw-r--r--polygon/PolyLine.h490
-rw-r--r--polygon/SutherlandHodgmanClipPoly.h273
-rw-r--r--polygon/clipper.cpp4642
-rw-r--r--polygon/clipper.hpp404
-rw-r--r--polygon/determine_if_point_inside_polygon.odtbin0 -> 26791 bytes
-rw-r--r--polygon/math_for_graphics.cpp520
-rw-r--r--polygon/math_for_graphics.h71
-rw-r--r--polygon/poly2tri/common/shapes.cc500
-rw-r--r--polygon/poly2tri/common/shapes.h351
-rw-r--r--polygon/poly2tri/common/utils.h133
-rw-r--r--polygon/poly2tri/poly2tri.h39
-rw-r--r--polygon/poly2tri/sweep/advancing_front.cc109
-rw-r--r--polygon/poly2tri/sweep/advancing_front.h118
-rw-r--r--polygon/poly2tri/sweep/cdt.cc72
-rw-r--r--polygon/poly2tri/sweep/cdt.h105
-rw-r--r--polygon/poly2tri/sweep/sweep.cc817
-rw-r--r--polygon/poly2tri/sweep/sweep.h284
-rw-r--r--polygon/poly2tri/sweep/sweep_context.cc216
-rw-r--r--polygon/poly2tri/sweep/sweep_context.h186
-rw-r--r--polygon/polygon_test_point_inside.cpp171
-rw-r--r--polygon/polygon_test_point_inside.h59
-rw-r--r--polygon/polygons_defs.h89
24 files changed, 11040 insertions, 0 deletions
diff --git a/polygon/CMakeLists.txt b/polygon/CMakeLists.txt
new file mode 100644
index 0000000..2f09cbe
--- /dev/null
+++ b/polygon/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+include_directories(BEFORE ${INC_BEFORE})
+include_directories(
+ ${INC_AFTER}
+ )
+
+set(POLYGON_SRCS
+ math_for_graphics.cpp
+ PolyLine.cpp
+ polygon_test_point_inside.cpp
+ clipper.cpp
+)
+
+add_library(polygon STATIC ${POLYGON_SRCS})
+
+add_dependencies( polygon lib-dependencies )
+
diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp
new file mode 100644
index 0000000..1b99845
--- /dev/null
+++ b/polygon/PolyLine.cpp
@@ -0,0 +1,1374 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Few parts of this code come from FreePCB, released under the GNU General Public License V2.
+ * (see http://www.freepcb.com/ )
+ *
+ * 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 PolyLine.cpp
+ * @note implementation of CPolyLine class
+ */
+
+//
+// implementation for kicad, using clipper polygon clipping library
+// for transformations not handled (at least for Kicad) by boost::polygon
+//
+#include <cmath>
+#include <vector>
+#include <algorithm>
+
+#include <fctsys.h>
+#include <common.h> // KiROUND
+
+#include <PolyLine.h>
+#include <bezier_curves.h>
+#include <polygon_test_point_inside.h>
+#include <math_for_graphics.h>
+#include <polygon_test_point_inside.h>
+
+
+CPolyLine::CPolyLine()
+{
+ m_hatchStyle = NO_HATCH;
+ m_hatchPitch = 0;
+ m_layer = F_Cu;
+ m_flags = 0;
+}
+
+CPolyLine::CPolyLine( const CPolyLine& aCPolyLine)
+{
+ Copy( &aCPolyLine );
+ m_HatchLines = aCPolyLine.m_HatchLines; // vector <> copy
+}
+
+
+// destructor, removes display elements
+//
+CPolyLine::~CPolyLine()
+{
+ UnHatch();
+}
+
+/* Removes corners which create a null segment edge
+ * (i.e. when 2 successive corners are at the same location)
+ * returns the count of removed corners.
+ */
+ int CPolyLine::RemoveNullSegments()
+{
+ int removed = 0;
+ unsigned startcountour = 0;
+
+ for( unsigned icnt = 1; icnt < m_CornersList.GetCornersCount(); icnt ++ )
+ {
+ unsigned last = icnt-1;
+ if( m_CornersList[icnt].end_contour )
+ {
+ last = startcountour;
+ startcountour = icnt+1;
+ }
+
+ if( ( m_CornersList[last].x == m_CornersList[icnt].x ) &&
+ ( m_CornersList[last].y == m_CornersList[icnt].y ) )
+ {
+ DeleteCorner( icnt );
+ icnt--;
+ removed ++;
+ }
+
+ if( m_CornersList[icnt].end_contour )
+ {
+ startcountour = icnt+1;
+ icnt++;
+ }
+ }
+
+ return removed;
+}
+
+
+/* Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
+ * and removes null segments.
+ * param aNewPolygonList = a std::vector<CPolyLine*> reference where to store new CPolyLine
+ * needed by the normalization
+ * return the polygon count (always >= 1, because there is at least one polygon)
+ * There are new polygons only if the polygon count is > 1
+ */
+int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
+{
+ SHAPE_POLY_SET polySet = ConvertPolyListToPolySet( m_CornersList );
+
+ // We are expecting only one main outline, but this main outline can have holes
+ // if holes: combine holes and remove them from the main outline.
+ SHAPE_POLY_SET::POLYGON& outline = polySet.Polygon( 0 );
+ SHAPE_POLY_SET holesBuffer;
+
+ // Move holes stored in outline to holesBuffer:
+ // The first SHAPE_LINE_CHAIN is the main outline, others are holes
+ while( outline.size() > 1 )
+ {
+ holesBuffer.AddOutline( outline.back() );
+ outline.pop_back();
+ }
+
+ polySet.Simplify();
+
+ // If any hole, substract it to main outline
+ if( holesBuffer.OutlineCount() )
+ {
+ holesBuffer.Simplify();
+ polySet.BooleanSubtract( holesBuffer );
+ }
+
+ RemoveAllContours();
+
+ // Note: we can have more than outline, because a self intersecting outline will be
+ // broken to non intersecting polygons, and removing holes can also create a few polygons
+ for( int ii = 0; ii < polySet.OutlineCount(); ii++ )
+ {
+ CPolyLine* polyline = this;
+
+ if( ii > 0 )
+ {
+ polyline = new CPolyLine;
+ polyline->ImportSettings( this );
+ aNewPolygonList->push_back( polyline );
+ }
+
+ SHAPE_POLY_SET pnew;
+ pnew.NewOutline();
+ pnew.Polygon( 0 ) = polySet.CPolygon( ii );
+
+ polyline->m_CornersList = ConvertPolySetToPolyList( pnew );
+ polyline->RemoveNullSegments();
+ }
+
+ return polySet.OutlineCount();
+}
+
+/**
+ * Function ImportSettings
+ * Copy settings (layer, hatch styles) from aPoly
+ */
+void CPolyLine::ImportSettings( const CPolyLine * aPoly )
+{
+ SetLayer( aPoly->GetLayer() );
+ SetHatchStyle( aPoly->GetHatchStyle() );
+ SetHatchPitch( aPoly->GetHatchPitch() );
+}
+
+/* initialize a contour
+ * set layer, hatch style, and starting point
+ */
+void CPolyLine::Start( LAYER_NUM layer, int x, int y, int hatch )
+{
+ m_layer = layer;
+ SetHatchStyle( (enum HATCH_STYLE) hatch );
+ CPolyPt poly_pt( x, y );
+ poly_pt.end_contour = false;
+
+ m_CornersList.Append( poly_pt );
+}
+
+
+// add a corner to unclosed polyline
+//
+void CPolyLine::AppendCorner( int x, int y )
+{
+ UnHatch();
+ CPolyPt poly_pt( x, y );
+ poly_pt.end_contour = false;
+
+ // add entries for new corner
+ m_CornersList.Append( poly_pt );
+}
+
+// move corner of polyline
+//
+void CPolyLine::MoveCorner( int ic, int x, int y )
+{
+ UnHatch();
+ m_CornersList[ic].x = x;
+ m_CornersList[ic].y = y;
+ Hatch();
+}
+
+
+// delete corner and adjust arrays
+//
+void CPolyLine::DeleteCorner( int ic )
+{
+ UnHatch();
+ int icont = GetContour( ic );
+ int iend = GetContourEnd( icont );
+ bool closed = icont < GetContoursCount() - 1 || GetClosed();
+
+ if( !closed )
+ {
+ // open contour, must be last contour
+ m_CornersList.DeleteCorner( ic );
+ }
+ else
+ {
+ // closed contour
+ m_CornersList.DeleteCorner( ic );
+
+ if( ic == iend )
+ m_CornersList[ic - 1].end_contour = true;
+ }
+
+ if( closed && GetContourSize( icont ) < 3 )
+ {
+ // delete the entire contour
+ RemoveContour( icont );
+ }
+}
+
+
+/******************************************/
+void CPolyLine::RemoveContour( int icont )
+/******************************************/
+
+/**
+ * Function RemoveContour
+ * @param icont = contour number to remove
+ * remove a contour only if there is more than 1 contour
+ */
+{
+ UnHatch();
+ int istart = GetContourStart( icont );
+ int iend = GetContourEnd( icont );
+
+ int polycount = GetContoursCount();
+
+ if( icont == 0 && polycount == 1 )
+ {
+ // remove the only contour
+ wxASSERT( 0 );
+ }
+ else
+ {
+ // remove closed contour
+ for( int ic = iend; ic>=istart; ic-- )
+ {
+ m_CornersList.DeleteCorner( ic );
+ }
+ }
+
+ Hatch();
+}
+
+
+CPolyLine* CPolyLine::Chamfer( unsigned int aDistance )
+{
+ // Null segments create serious issues in calculations.
+ // remove them:
+ RemoveNullSegments();
+
+ CPolyLine* newPoly = new CPolyLine;
+
+ if( !aDistance )
+ {
+ newPoly->Copy( this );
+ return newPoly;
+ }
+
+ int polycount = GetContoursCount();
+
+ for( int contour = 0; contour < polycount; contour++ )
+ {
+ unsigned int startIndex = GetContourStart( contour );
+ unsigned int endIndex = GetContourEnd( contour );
+
+ for( unsigned int index = startIndex; index <= endIndex; index++ )
+ {
+ // Current vertex
+ int x1 = m_CornersList[index].x;
+ int y1 = m_CornersList[index].y;
+ double xa, ya; // Previous vertex
+ double xb, yb; // Next vertex
+
+ if( index == startIndex )
+ {
+ xa = m_CornersList[endIndex].x - x1;
+ ya = m_CornersList[endIndex].y - y1;
+ }
+ else
+ {
+ xa = m_CornersList[index - 1].x - x1;
+ ya = m_CornersList[index - 1].y - y1;
+ }
+
+ if( index == endIndex )
+ {
+ xb = m_CornersList[startIndex].x - x1;
+ yb = m_CornersList[startIndex].y - y1;
+ }
+ else
+ {
+ xb = m_CornersList[index + 1].x - x1;
+ yb = m_CornersList[index + 1].y - y1;
+ }
+
+ double lena = hypot( xa, ya );
+ double lenb = hypot( xb, yb );
+ double distance = aDistance;
+
+ // Chamfer one half of an edge at most
+ if( 0.5 * lena < distance )
+ distance = 0.5 * lena;
+
+ if( 0.5 * lenb < distance )
+ distance = 0.5 * lenb;
+
+ int nx1 = KiROUND( distance * xa / lena );
+ int ny1 = KiROUND( distance * ya / lena );
+
+ if( index == startIndex )
+ newPoly->Start( GetLayer(), x1 + nx1, y1 + ny1, GetHatchStyle() );
+ else
+ newPoly->AppendCorner( x1 + nx1, y1 + ny1 );
+
+ int nx2 = KiROUND( distance * xb / lenb );
+ int ny2 = KiROUND( distance * yb / lenb );
+ newPoly->AppendCorner( x1 + nx2, y1 + ny2 );
+ }
+
+ newPoly->CloseLastContour();
+ }
+
+ return newPoly;
+}
+
+
+CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
+{
+ // Null segments create serious issues in calculations.
+ // remove them:
+ RemoveNullSegments();
+
+ CPolyLine* newPoly = new CPolyLine;
+
+ if( !aRadius )
+ {
+ newPoly->Copy( this );
+ return newPoly;
+ }
+
+ int polycount = GetContoursCount();
+
+ for( int contour = 0; contour < polycount; contour++ )
+ {
+ unsigned int startIndex = GetContourStart( contour );
+ unsigned int endIndex = GetContourEnd( contour );
+
+ for( unsigned int index = startIndex; index <= endIndex; index++ )
+ {
+ // Current vertex
+ int x1 = m_CornersList[index].x;
+ int y1 = m_CornersList[index].y;
+ double xa, ya; // Previous vertex
+ double xb, yb; // Next vertex
+
+ if( index == startIndex )
+ {
+ xa = m_CornersList[endIndex].x - x1;
+ ya = m_CornersList[endIndex].y - y1;
+ }
+ else
+ {
+ xa = m_CornersList[index - 1].x - x1;
+ ya = m_CornersList[index - 1].y - y1;
+ }
+
+ if( index == endIndex )
+ {
+ xb = m_CornersList[startIndex].x - x1;
+ yb = m_CornersList[startIndex].y - y1;
+ }
+ else
+ {
+ xb = m_CornersList[index + 1].x - x1;
+ yb = m_CornersList[index + 1].y - y1;
+ }
+
+ double lena = hypot( xa, ya );
+ double lenb = hypot( xb, yb );
+ double cosine = ( xa * xb + ya * yb ) / ( lena * lenb );
+
+ double radius = aRadius;
+ double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 );
+
+ // Do nothing in case of parallel edges
+ if( !std::isfinite( denom ) )
+ continue;
+
+ // Limit rounding distance to one half of an edge
+ if( 0.5 * lena * denom < radius )
+ radius = 0.5 * lena * denom;
+
+ if( 0.5 * lenb * denom < radius )
+ radius = 0.5 * lenb * denom;
+
+ // Calculate fillet arc absolute center point (xc, yx)
+ double k = radius / sqrt( .5 * ( 1 - cosine ) );
+ double lenab = sqrt( ( xa / lena + xb / lenb ) * ( xa / lena + xb / lenb ) +
+ ( ya / lena + yb / lenb ) * ( ya / lena + yb / lenb ) );
+ double xc = x1 + k * ( xa / lena + xb / lenb ) / lenab;
+ double yc = y1 + k * ( ya / lena + yb / lenb ) / lenab;
+
+ // Calculate arc start and end vectors
+ k = radius / sqrt( 2 / ( 1 + cosine ) - 1 );
+ double xs = x1 + k * xa / lena - xc;
+ double ys = y1 + k * ya / lena - yc;
+ double xe = x1 + k * xb / lenb - xc;
+ double ye = y1 + k * yb / lenb - yc;
+
+ // Cosine of arc angle
+ double argument = ( xs * xe + ys * ye ) / ( radius * radius );
+
+ if( argument < -1 ) // Just in case...
+ argument = -1;
+ else if( argument > 1 )
+ argument = 1;
+
+ double arcAngle = acos( argument );
+
+ // Calculate the number of segments
+ double tempSegments = (double) aSegments * ( arcAngle / ( 2 * M_PI ) );
+
+ if( tempSegments - (int) tempSegments > 0 )
+ tempSegments++;
+
+ unsigned int segments = (unsigned int) tempSegments;
+
+ double deltaAngle = arcAngle / segments;
+ double startAngle = atan2( -ys, xs );
+
+ // Flip arc for inner corners
+ if( xa * yb - ya * xb <= 0 )
+ deltaAngle *= -1;
+
+ double nx = xc + xs;
+ double ny = yc + ys;
+
+ if( index == startIndex )
+ newPoly->Start( GetLayer(), KiROUND( nx ), KiROUND( ny ), GetHatchStyle() );
+ else
+ newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
+
+ for( unsigned int j = 0; j < segments; j++ )
+ {
+ nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius;
+ ny = yc - sin( startAngle + (j + 1) * deltaAngle ) * radius;
+ newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
+ }
+ }
+
+ newPoly->CloseLastContour();
+ }
+
+ return newPoly;
+}
+
+
+/******************************************/
+void CPolyLine::RemoveAllContours( void )
+/******************************************/
+
+/**
+ * function RemoveAllContours
+ * removes all corners from the list.
+ * Others params are not changed
+ */
+{
+ m_CornersList.RemoveAllContours();
+}
+
+
+/**
+ * Function InsertCorner
+ * insert a new corner between two existing corners
+ * @param ic = index for the insertion point: the corner is inserted AFTER ic
+ * @param x, y = coordinates corner to insert
+ */
+void CPolyLine::InsertCorner( int ic, int x, int y )
+{
+ UnHatch();
+
+ if( (unsigned) (ic) >= m_CornersList.GetCornersCount() )
+ {
+ m_CornersList.Append( CPolyPt( x, y ) );
+ }
+ else
+ {
+ m_CornersList.InsertCorner(ic, CPolyPt( x, y ) );
+ }
+
+ if( (unsigned) (ic + 1) < m_CornersList.GetCornersCount() )
+ {
+ if( m_CornersList[ic].end_contour )
+ {
+ m_CornersList[ic + 1].end_contour = true;
+ m_CornersList[ic].end_contour = false;
+ }
+ }
+
+ Hatch();
+}
+
+
+// undraw polyline by removing all graphic elements from display list
+void CPolyLine::UnHatch()
+{
+ m_HatchLines.clear();
+}
+
+
+const EDA_RECT CPolyLine::GetBoundingBox()
+{
+ int xmin = INT_MAX;
+ int ymin = INT_MAX;
+ int xmax = INT_MIN;
+ int ymax = INT_MIN;
+
+ for( unsigned i = 0; i< m_CornersList.GetCornersCount(); i++ )
+ {
+ xmin = std::min( xmin, m_CornersList[i].x );
+ xmax = std::max( xmax, m_CornersList[i].x );
+ ymin = std::min( ymin, m_CornersList[i].y );
+ ymax = std::max( ymax, m_CornersList[i].y );
+ }
+
+ EDA_RECT r;
+ r.SetOrigin( wxPoint( xmin, ymin ) );
+ r.SetEnd( wxPoint( xmax, ymax ) );
+
+ return r;
+}
+
+
+const EDA_RECT CPolyLine::GetBoundingBox( int icont )
+{
+ int xmin = INT_MAX;
+ int ymin = INT_MAX;
+ int xmax = INT_MIN;
+ int ymax = INT_MIN;
+ int istart = GetContourStart( icont );
+ int iend = GetContourEnd( icont );
+
+ for( int i = istart; i<=iend; i++ )
+ {
+ xmin = std::min( xmin, m_CornersList[i].x );
+ xmax = std::max( xmax, m_CornersList[i].x );
+ ymin = std::min( ymin, m_CornersList[i].y );
+ ymax = std::max( ymax, m_CornersList[i].y );
+ }
+
+ EDA_RECT r;
+ r.SetOrigin( wxPoint( xmin, ymin ) );
+ r.SetEnd( wxPoint( xmax, ymax ) );
+
+ return r;
+}
+
+
+int CPolyLine::GetContoursCount() const
+{
+ return m_CornersList.GetContoursCount();
+}
+
+
+
+int CPOLYGONS_LIST::GetContoursCount() const
+{
+ if( !m_cornersList.size() )
+ return 0;
+
+ // count the number of corners flagged end_contour
+ int ncont = 0;
+
+ for( unsigned ic = 0; ic < m_cornersList.size(); ic++ )
+ if( m_cornersList[ic].end_contour )
+ ncont++;
+
+ // The last corner can be not yet flagged end_contour.
+ // It was not counted, but the polygon exists, so count it
+ if( !m_cornersList[m_cornersList.size() - 1].end_contour )
+ ncont++;
+
+ return ncont;
+}
+
+
+int CPolyLine::GetContour( int ic )
+{
+ int ncont = 0;
+
+ for( int i = 0; i<ic; i++ )
+ {
+ if( m_CornersList[i].end_contour )
+ ncont++;
+ }
+
+ return ncont;
+}
+
+
+int CPolyLine::GetContourStart( int icont )
+{
+ if( icont == 0 )
+ return 0;
+
+ int ncont = 0;
+
+ for( unsigned i = 0; i<m_CornersList.GetCornersCount(); i++ )
+ {
+ if( m_CornersList[i].end_contour )
+ {
+ ncont++;
+
+ if( ncont == icont )
+ return i + 1;
+ }
+ }
+
+ wxASSERT( 0 );
+ return 0;
+}
+
+
+int CPolyLine::GetContourEnd( int icont )
+{
+ if( icont < 0 )
+ return 0;
+
+ if( icont == GetContoursCount() - 1 )
+ return m_CornersList.GetCornersCount() - 1;
+
+ int ncont = 0;
+
+ for( unsigned i = 0; i<m_CornersList.GetCornersCount(); i++ )
+ {
+ if( m_CornersList[i].end_contour )
+ {
+ if( ncont == icont )
+ return i;
+
+ ncont++;
+ }
+ }
+
+ wxASSERT( 0 );
+ return 0;
+}
+
+
+int CPolyLine::GetContourSize( int icont )
+{
+ return GetContourEnd( icont ) - GetContourStart( icont ) + 1;
+}
+
+
+bool CPolyLine::GetClosed()
+{
+ if( m_CornersList.GetCornersCount() == 0 )
+ return false;
+ else
+ return m_CornersList[m_CornersList.GetCornersCount() - 1].end_contour;
+}
+
+
+// Creates hatch lines inside the outline of the complex polygon
+//
+// sort function used in ::Hatch to sort points by descending wxPoint.x values
+bool sort_ends_by_descending_X( const wxPoint& ref, const wxPoint& tst )
+{
+ return tst.x < ref.x;
+}
+
+
+void CPolyLine::Hatch()
+{
+ m_HatchLines.clear();
+
+ if( m_hatchStyle == NO_HATCH || m_hatchPitch == 0 )
+ return;
+
+ if( !GetClosed() ) // If not closed, the poly is beeing created and not finalised. Not not hatch
+ return;
+
+ // define range for hatch lines
+ int min_x = m_CornersList[0].x;
+ int max_x = m_CornersList[0].x;
+ int min_y = m_CornersList[0].y;
+ int max_y = m_CornersList[0].y;
+
+ for( unsigned ic = 1; ic < m_CornersList.GetCornersCount(); ic++ )
+ {
+ if( m_CornersList[ic].x < min_x )
+ min_x = m_CornersList[ic].x;
+
+ if( m_CornersList[ic].x > max_x )
+ max_x = m_CornersList[ic].x;
+
+ if( m_CornersList[ic].y < min_y )
+ min_y = m_CornersList[ic].y;
+
+ if( m_CornersList[ic].y > max_y )
+ max_y = m_CornersList[ic].y;
+ }
+
+ // Calculate spacing between 2 hatch lines
+ int spacing;
+
+ if( m_hatchStyle == DIAGONAL_EDGE )
+ spacing = m_hatchPitch;
+ else
+ spacing = m_hatchPitch * 2;
+
+ // set the "length" of hatch lines (the lenght on horizontal axis)
+ double hatch_line_len = m_hatchPitch;
+
+ // To have a better look, give a slope depending on the layer
+ LAYER_NUM layer = GetLayer();
+ int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1
+ double slope = 0.707106 * slope_flag; // 45 degrees slope
+ int max_a, min_a;
+
+ if( slope_flag == 1 )
+ {
+ max_a = KiROUND( max_y - slope * min_x );
+ min_a = KiROUND( min_y - slope * max_x );
+ }
+ else
+ {
+ max_a = KiROUND( max_y - slope * max_x );
+ min_a = KiROUND( min_y - slope * min_x );
+ }
+
+ min_a = (min_a / spacing) * spacing;
+
+ // calculate an offset depending on layer number,
+ // for a better look of hatches on a multilayer board
+ int offset = (layer * 7) / 8;
+ min_a += offset;
+
+ // now calculate and draw hatch lines
+ int nc = m_CornersList.GetCornersCount();
+
+ // loop through hatch lines
+ #define MAXPTS 200 // Usually we store only few values per one hatch line
+ // depending on the compexity of the zone outline
+
+ static std::vector <wxPoint> pointbuffer;
+ pointbuffer.clear();
+ pointbuffer.reserve( MAXPTS + 2 );
+
+ for( int a = min_a; a < max_a; a += spacing )
+ {
+ // get intersection points for this hatch line
+
+ // Note: because we should have an even number of intersections with the
+ // current hatch line and the zone outline (a closed polygon,
+ // or a set of closed polygons), if an odd count is found
+ // we skip this line (should not occur)
+ pointbuffer.clear();
+ int i_start_contour = 0;
+
+ for( int ic = 0; ic<nc; ic++ )
+ {
+ double x, y, x2, y2;
+ int ok;
+
+ if( m_CornersList[ic].end_contour ||
+ ( ic == (int) (m_CornersList.GetCornersCount() - 1) ) )
+ {
+ ok = FindLineSegmentIntersection( a, slope,
+ m_CornersList[ic].x, m_CornersList[ic].y,
+ m_CornersList[i_start_contour].x,
+ m_CornersList[i_start_contour].y,
+ &x, &y, &x2, &y2 );
+ i_start_contour = ic + 1;
+ }
+ else
+ {
+ ok = FindLineSegmentIntersection( a, slope,
+ m_CornersList[ic].x, m_CornersList[ic].y,
+ m_CornersList[ic + 1].x, m_CornersList[ic + 1].y,
+ &x, &y, &x2, &y2 );
+ }
+
+ if( ok )
+ {
+ wxPoint point( KiROUND( x ), KiROUND( y ) );
+ pointbuffer.push_back( point );
+ }
+
+ if( ok == 2 )
+ {
+ wxPoint point( KiROUND( x2 ), KiROUND( y2 ) );
+ pointbuffer.push_back( point );
+ }
+
+ if( pointbuffer.size() >= MAXPTS ) // overflow
+ {
+ wxASSERT( 0 );
+ break;
+ }
+ }
+
+ // ensure we have found an even intersection points count
+ // because intersections are the ends of segments
+ // inside the polygon(s) and a segment has 2 ends.
+ // if not, this is a strange case (a bug ?) so skip this hatch
+ if( pointbuffer.size() % 2 != 0 )
+ continue;
+
+ // sort points in order of descending x (if more than 2) to
+ // ensure the starting point and the ending point of the same segment
+ // are stored one just after the other.
+ if( pointbuffer.size() > 2 )
+ sort( pointbuffer.begin(), pointbuffer.end(), sort_ends_by_descending_X );
+
+ // creates lines or short segments inside the complex polygon
+ for( unsigned ip = 0; ip < pointbuffer.size(); ip += 2 )
+ {
+ double dx = pointbuffer[ip + 1].x - pointbuffer[ip].x;
+
+ // Push only one line for diagonal hatch,
+ // or for small lines < twice the line len
+ // else push 2 small lines
+ if( m_hatchStyle == DIAGONAL_FULL || fabs( dx ) < 2 * hatch_line_len )
+ {
+ m_HatchLines.push_back( CSegment( pointbuffer[ip], pointbuffer[ip + 1] ) );
+ }
+ else
+ {
+ double dy = pointbuffer[ip + 1].y - pointbuffer[ip].y;
+ double slope = dy / dx;
+
+ if( dx > 0 )
+ dx = hatch_line_len;
+ else
+ dx = -hatch_line_len;
+
+ double x1 = pointbuffer[ip].x + dx;
+ double x2 = pointbuffer[ip + 1].x - dx;
+ double y1 = pointbuffer[ip].y + dx * slope;
+ double y2 = pointbuffer[ip + 1].y - dx * slope;
+
+ m_HatchLines.push_back( CSegment( pointbuffer[ip].x,
+ pointbuffer[ip].y,
+ KiROUND( x1 ), KiROUND( y1 ) ) );
+
+ m_HatchLines.push_back( CSegment( pointbuffer[ip + 1].x,
+ pointbuffer[ip + 1].y,
+ KiROUND( x2 ), KiROUND( y2 ) ) );
+ }
+ }
+ }
+}
+
+
+// test to see if a point is inside polyline
+//
+bool CPolyLine::TestPointInside( int px, int py )
+{
+ if( !GetClosed() )
+ {
+ wxASSERT( 0 );
+ }
+
+ // Test all polygons.
+ // Since the first is the main outline, and other are holes,
+ // if the tested point is inside only one contour, it is inside the whole polygon
+ // (in fact inside the main outline, and outside all holes).
+ // if inside 2 contours (the main outline + an hole), it is outside the poly.
+ int polycount = GetContoursCount();
+ bool inside = false;
+
+ for( int icont = 0; icont < polycount; icont++ )
+ {
+ int istart = GetContourStart( icont );
+ int iend = GetContourEnd( icont );
+
+ // test point inside the current polygon
+ if( TestPointInsidePolygon( m_CornersList, istart, iend, px, py ) )
+ inside = not inside;
+ }
+
+ return inside;
+}
+
+
+// copy data from another CPolyLine, but don't draw it
+void CPolyLine::Copy( const CPolyLine* src )
+{
+ UnHatch();
+ m_layer = src->m_layer;
+ m_hatchStyle = src->m_hatchStyle;
+ m_hatchPitch = src->m_hatchPitch;
+ m_flags = src->m_flags;
+ m_CornersList.RemoveAllContours();
+ m_CornersList.Append( src->m_CornersList );
+}
+
+
+/*
+ * return true if the corner aCornerIdx is on a hole inside the main outline
+ * and false if it is on the main outline
+ */
+bool CPolyLine::IsCutoutContour( int aCornerIdx )
+{
+ int ncont = GetContour( aCornerIdx );
+
+ if( ncont == 0 ) // the first contour is the main outline, not an hole
+ return false;
+
+ return true;
+}
+
+
+void CPolyLine::MoveOrigin( int x_off, int y_off )
+{
+ UnHatch();
+
+ for( int ic = 0; ic < GetCornersCount(); ic++ )
+ {
+ SetX( ic, GetX( ic ) + x_off );
+ SetY( ic, GetY( ic ) + y_off );
+ }
+
+ Hatch();
+}
+
+/*
+ * AppendArc:
+ * adds segments to current contour to approximate the given arc
+ */
+void CPolyLine::AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num )
+{
+ // get radius
+ double radius = ::Distance( xi, yi, xf, yf );
+
+ // get angles of start pint and end point
+ double th_i = atan2( (double) (yi - yc), (double) (xi - xc) );
+ double th_f = atan2( (double) (yf - yc), (double) (xf - xc) );
+ double th_d = (th_f - th_i) / (num - 1);
+ double theta = th_i;
+
+ // generate arc
+ for( int ic = 0; ic < num; ic++ )
+ {
+ int x = xc + KiROUND( radius * cos( theta ) );
+ int y = yc + KiROUND( radius * sin( theta ) );
+ AppendCorner( x, y );
+ theta += th_d;
+ }
+
+ CloseLastContour();
+}
+
+
+// Bezier Support
+void CPolyLine::AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3 )
+{
+ std::vector<wxPoint> bezier_points;
+
+ bezier_points = Bezier2Poly( x1, y1, x2, y2, x3, y3 );
+
+ for( unsigned int i = 0; i < bezier_points.size(); i++ )
+ AppendCorner( bezier_points[i].x, bezier_points[i].y );
+}
+
+
+void CPolyLine::AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 )
+{
+ std::vector<wxPoint> bezier_points;
+
+ bezier_points = Bezier2Poly( x1, y1, x2, y2, x3, y3, x4, y4 );
+
+ for( unsigned int i = 0; i < bezier_points.size(); i++ )
+ AppendCorner( bezier_points[i].x, bezier_points[i].y );
+}
+
+
+/*
+ * Function Distance
+ * Calculates the distance between a segment and a polygon (with holes):
+ * param aStart is the starting point of the segment.
+ * param aEnd is the ending point of the segment.
+ * param aWidth is the width of the segment.
+ * return distance between the segment and outline.
+ * 0 if segment intersects or is inside
+ */
+int CPolyLine::Distance( wxPoint aStart, wxPoint aEnd, int aWidth )
+{
+ // We calculate the min dist between the segment and each outline segment
+ // However, if the segment to test is inside the outline, and does not cross
+ // any edge, it can be seen outside the polygon.
+ // Therefore test if a segment end is inside ( testing only one end is enough )
+ if( TestPointInside( aStart.x, aStart.y ) )
+ return 0;
+
+ int distance = INT_MAX;
+ int polycount = GetContoursCount();
+
+ for( int icont = 0; icont < polycount; icont++ )
+ {
+ int ic_start = GetContourStart( icont );
+ int ic_end = GetContourEnd( icont );
+
+ // now test spacing between area outline and segment
+ for( int ic2 = ic_start; ic2 <= ic_end; ic2++ )
+ {
+ int bx1 = GetX( ic2 );
+ int by1 = GetY( ic2 );
+ int bx2, by2;
+
+ if( ic2 == ic_end )
+ {
+ bx2 = GetX( ic_start );
+ by2 = GetY( ic_start );
+ }
+ else
+ {
+ bx2 = GetX( ic2 + 1 );
+ by2 = GetY( ic2 + 1 );
+ }
+
+ int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
+ aStart.x, aStart.y, aEnd.x, aEnd.y,
+ aWidth,
+ 1, // min clearance, should be > 0
+ NULL, NULL );
+
+ if( distance > d )
+ distance = d;
+
+ if( distance <= 0 )
+ return 0;
+ }
+ }
+
+ return distance;
+}
+
+
+/*
+ * Function Distance
+ * Calculates the distance between a point and polygon (with holes):
+ * param aPoint is the coordinate of the point.
+ * return distance between the point and outline.
+ * 0 if the point is inside
+ */
+int CPolyLine::Distance( const wxPoint& aPoint )
+{
+ // We calculate the dist between the point and each outline segment
+ // If the point is inside the outline, the dist is 0.
+ if( TestPointInside( aPoint.x, aPoint.y ) )
+ return 0;
+
+ int distance = INT_MAX;
+ int polycount = GetContoursCount();
+
+ for( int icont = 0; icont < polycount; icont++ )
+ {
+ int ic_start = GetContourStart( icont );
+ int ic_end = GetContourEnd( icont );
+
+ // now test spacing between area outline and segment
+ for( int ic2 = ic_start; ic2 <= ic_end; ic2++ )
+ {
+ int bx1 = GetX( ic2 );
+ int by1 = GetY( ic2 );
+ int bx2, by2;
+
+ if( ic2 == ic_end )
+ {
+ bx2 = GetX( ic_start );
+ by2 = GetY( ic_start );
+ }
+ else
+ {
+ bx2 = GetX( ic2 + 1 );
+ by2 = GetY( ic2 + 1 );
+ }
+
+ int d = KiROUND( GetPointToLineSegmentDistance( aPoint.x, aPoint.y,
+ bx1, by1, bx2, by2 ) );
+
+ if( distance > d )
+ distance = d;
+
+ if( distance <= 0 )
+ return 0;
+ }
+ }
+
+ return distance;
+}
+
+
+/* test is the point aPos is near (< aDistMax ) a vertex
+ * return int = the index of the first corner of the vertex, or -1 if not found.
+ */
+int CPolyLine::HitTestForEdge( const wxPoint& aPos, int aDistMax ) const
+{
+ unsigned lim = m_CornersList.GetCornersCount();
+ int corner = -1; // Set to not found
+ unsigned first_corner_pos = 0;
+
+ for( unsigned item_pos = 0; item_pos < lim; item_pos++ )
+ {
+ unsigned end_segm = item_pos + 1;
+
+ /* the last corner of the current outline is tested
+ * the last segment of the current outline starts at current corner, and ends
+ * at the first corner of the outline
+ */
+ if( m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim )
+ {
+ unsigned tmp = first_corner_pos;
+ first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline
+ end_segm = tmp; // end_segm is the beginning of the current outline
+ }
+
+ // test the dist between segment and ref point
+ int dist = KiROUND( GetPointToLineSegmentDistance(
+ aPos.x, aPos.y,
+ m_CornersList.GetX( item_pos ),
+ m_CornersList.GetY( item_pos ),
+ m_CornersList.GetX( end_segm ),
+ m_CornersList.GetY( end_segm ) ) );
+
+ if( dist < aDistMax )
+ {
+ corner = item_pos;
+ aDistMax = dist;
+ }
+ }
+
+ return corner;
+}
+
+/* test is the point aPos is near (< aDistMax ) a corner
+ * return int = the index of corner of the, or -1 if not found.
+ */
+int CPolyLine::HitTestForCorner( const wxPoint& aPos, int aDistMax ) const
+{
+ int corner = -1; // Set to not found
+ wxPoint delta;
+ unsigned lim = m_CornersList.GetCornersCount();
+
+ for( unsigned item_pos = 0; item_pos < lim; item_pos++ )
+ {
+ delta.x = aPos.x - m_CornersList.GetX( item_pos );
+ delta.y = aPos.y - m_CornersList.GetY( item_pos );
+
+ // Calculate a distance:
+ int dist = std::max( abs( delta.x ), abs( delta.y ) );
+
+ if( dist < aDistMax ) // this corner is a candidate:
+ {
+ corner = item_pos;
+ aDistMax = dist;
+ }
+ }
+
+ return corner;
+}
+
+
+/**
+ * Function IsPolygonSelfIntersecting
+ * Test a CPolyLine for self-intersection of vertex (all contours).
+ *
+ * @return :
+ * false if no intersecting sides
+ * true if intersecting sides
+ * When a CPolyLine is self intersectic, it need to be normalized.
+ * (converted to non intersecting polygons)
+ */
+bool CPolyLine::IsPolygonSelfIntersecting()
+{
+ // first, check for sides intersecting other sides
+ int n_cont = GetContoursCount();
+
+ // make bounding rect for each contour
+ std::vector<EDA_RECT> cr;
+ cr.reserve( n_cont );
+
+ for( int icont = 0; icont<n_cont; icont++ )
+ cr.push_back( GetBoundingBox( icont ) );
+
+ for( int icont = 0; icont<n_cont; icont++ )
+ {
+ int is_start = GetContourStart( icont );
+ int is_end = GetContourEnd( icont );
+
+ for( int is = is_start; is<=is_end; is++ )
+ {
+ int is_prev = is - 1;
+
+ if( is_prev < is_start )
+ is_prev = is_end;
+
+ int is_next = is + 1;
+
+ if( is_next > is_end )
+ is_next = is_start;
+
+ int x1i = GetX( is );
+ int y1i = GetY( is );
+ int x1f = GetX( is_next );
+ int y1f = GetY( is_next );
+
+ // check for intersection with any other sides
+ for( int icont2 = icont; icont2 < n_cont; icont2++ )
+ {
+ if( !cr[icont].Intersects( cr[icont2] ) )
+ {
+ // rectangles don't overlap, do nothing
+ }
+ else
+ {
+ int is2_start = GetContourStart( icont2 );
+ int is2_end = GetContourEnd( icont2 );
+
+ for( int is2 = is2_start; is2<=is2_end; is2++ )
+ {
+ int is2_prev = is2 - 1;
+
+ if( is2_prev < is2_start )
+ is2_prev = is2_end;
+
+ int is2_next = is2 + 1;
+
+ if( is2_next > is2_end )
+ is2_next = is2_start;
+
+ if( icont != icont2
+ || ( is2 != is && is2 != is_prev && is2 != is_next &&
+ is != is2_prev && is != is2_next )
+ )
+ {
+ int x2i = GetX( is2 );
+ int y2i = GetY( is2 );
+ int x2f = GetX( is2_next );
+ int y2f = GetY( is2_next );
+ int ret = FindSegmentIntersections( x1i, y1i, x1f, y1f,
+ x2i, y2i, x2f, y2f );
+ if( ret )
+ {
+ // intersection between non-adjacent sides
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+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;
+
+ 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;
+}
+
+
+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;
+}
diff --git a/polygon/PolyLine.h b/polygon/PolyLine.h
new file mode 100644
index 0000000..b60cd37
--- /dev/null
+++ b/polygon/PolyLine.h
@@ -0,0 +1,490 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Few parts of this code come from FreePCB, released under the GNU General Public License V2.
+ * (see http://www.freepcb.com/ )
+ *
+ * Copyright (C) 2012-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2008-2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
+ * Copyright (C) 2008-2013 Wayne Stambaugh <stambaughw@verizon.net>
+ * Copyright (C) 2012-2015 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 PolyLine.h
+ * @note definition of CPolyLine class
+ */
+
+//
+// A polyline contains one or more contours, where each contour
+// is defined by a list of corners and side-styles
+// There may be multiple contours in a polyline.
+// The last contour may be open or closed, any others must be closed.
+// All of the corners and side-styles are concatenated into 2 arrays,
+// separated by setting the end_contour flag of the last corner of
+// each contour.
+//
+// When used for copper (or technical layers) areas, the first contour is the outer edge
+// of the area, subsequent ones are "holes" in the copper.
+
+#ifndef POLYLINE_H
+#define POLYLINE_H
+
+#include <vector>
+
+#include <wx/gdicmn.h> // for wxPoint definition
+#include <layers_id_colors_and_visibility.h> // for LAYER_NUM definition
+#include <class_eda_rect.h> // for EDA_RECT definition
+
+#include <geometry/shape_poly_set.h> // fixme
+
+class CSegment
+{
+public:
+ wxPoint m_Start;
+ wxPoint m_End;
+
+ CSegment() { };
+ CSegment( const wxPoint& aStart, const wxPoint& aEnd )
+ {
+ m_Start = aStart;
+ m_End = aEnd;
+ }
+
+ CSegment( int x0, int y0, int x1, int y1 )
+ {
+ m_Start.x = x0; m_Start.y = y0;
+ m_End.x = x1; m_End.y = y1;
+ }
+};
+
+class CPolyPt : public wxPoint
+{
+public:
+ CPolyPt( int aX = 0, int aY = 0, bool aEnd = false, int aUtility = 0 ) :
+ wxPoint( aX, aY ), end_contour( aEnd ), m_flags( aUtility )
+ {}
+
+ // / Pure copy constructor is here to dis-ambiguate from the
+ // / specialized CPolyPt( const wxPoint& ) constructor version below.
+ CPolyPt( const CPolyPt& aPt ) :
+ wxPoint( aPt.x, aPt.y ), end_contour( aPt.end_contour ), m_flags( aPt.m_flags )
+ {}
+
+ CPolyPt( const wxPoint& aPoint ) :
+ wxPoint( aPoint ), end_contour( false ), m_flags( 0 )
+ {}
+
+
+ bool end_contour;
+ int m_flags;
+
+ bool operator ==( const CPolyPt& cpt2 ) const
+ { return (x == cpt2.x) && (y == cpt2.y) && (end_contour == cpt2.end_contour); }
+
+ bool operator !=( CPolyPt& cpt2 ) const
+ { return (x != cpt2.x) || (y != cpt2.y) || (end_contour != cpt2.end_contour); }
+};
+
+/**
+ * CPOLYGONS_LIST handle a list of contours (polygons corners).
+ * Each corner is a CPolyPt item.
+ * The last cornet of each contour has its end_contour member = true
+ */
+class CPOLYGONS_LIST
+{
+private:
+ std::vector <CPolyPt> m_cornersList; // array of points for corners
+public:
+ CPOLYGONS_LIST() {};
+
+ CPolyPt& operator [](int aIdx) { return m_cornersList[aIdx]; }
+
+ // Accessor:
+ const std::vector <CPolyPt>& GetList() const {return m_cornersList;}
+
+ int GetX( int ic ) const { return m_cornersList[ic].x; }
+ void SetX( int ic, int aValue ) { m_cornersList[ic].x = aValue; }
+ int GetY( int ic ) const { return m_cornersList[ic].y; }
+ void SetY( int ic, int aValue ) { m_cornersList[ic].y = aValue; }
+
+ bool IsEndContour( int ic ) const
+ {
+ return m_cornersList[ic].end_contour;
+ }
+
+ const wxPoint& GetPos( int ic ) const { return m_cornersList[ic]; }
+ const CPolyPt& GetCorner( int ic ) const { return m_cornersList[ic]; }
+
+ // vector <> methods
+ void reserve( int aSize ) { m_cornersList.reserve( aSize ); }
+
+
+ void RemoveAllContours( void ) { m_cornersList.clear(); }
+ CPolyPt& GetLastCorner() { return m_cornersList.back(); }
+
+ unsigned GetCornersCount() const { return m_cornersList.size(); }
+
+ void DeleteCorner( int aIdx )
+ {
+ 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,
+ m_cornersList.begin() + aIdLastCorner + 1 );
+ }
+
+ void Append( const CPOLYGONS_LIST& aList )
+ {
+ m_cornersList.insert( m_cornersList.end(),
+ aList.m_cornersList.begin(),
+ aList.m_cornersList.end() );
+ }
+
+ void Append( const CPolyPt& aItem )
+ {
+ m_cornersList.push_back( aItem );
+ }
+
+ void Append( const wxPoint& aItem )
+ {
+ CPolyPt item( aItem );
+
+ m_cornersList.push_back( aItem );
+ }
+
+ void InsertCorner( int aPosition, const CPolyPt& aItem )
+ {
+ m_cornersList.insert( m_cornersList.begin() + aPosition + 1, aItem );
+ }
+
+ /**
+ * function AddCorner
+ * add a corner to the list
+ */
+ void AddCorner( const CPolyPt& aCorner )
+ {
+ m_cornersList.push_back( aCorner );
+ }
+
+ /**
+ * function CloseLastContour
+ * Set the .end_contour member of the last corner in list to true
+ */
+ void CloseLastContour()
+ {
+ if( m_cornersList.size() > 0 )
+ m_cornersList.back().end_contour = true;
+ }
+
+ /**
+ * Function GetContoursCount.
+ * @return the number of polygons stored in list
+ * (number of corners flagged "end_contour"
+ */
+ int GetContoursCount() const;
+};
+
+class CPolyLine
+{
+public:
+ enum HATCH_STYLE { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE }; // hatch styles
+
+ // constructors/destructor
+ CPolyLine();
+ CPolyLine( const CPolyLine& aCPolyLine);
+ ~CPolyLine();
+
+ /**
+ * Function ImportSettings
+ * Copy settings (layer, hatch styles) from aPoly
+ * @param aPoly is the CPolyLine to import settings
+ */
+ void ImportSettings( const CPolyLine* aPoly );
+
+ // functions for modifying the CPolyLine contours
+
+ /* initialize a contour
+ * set layer, hatch style, and starting point
+ */
+ void Start( LAYER_NUM layer, int x, int y, int hatch );
+
+ void AppendCorner( int x, int y );
+ void InsertCorner( int ic, int x, int y );
+
+ /**
+ * Function DeleteCorner
+ * remove the given corner. if it is the last point of a contour
+ * keep the controur closed by modifying the previous corner
+ * @param ic = the index of the corner to delete
+ */
+ void DeleteCorner( int ic );
+ void MoveCorner( int ic, int x, int y );
+
+ /**
+ * function CloseLastContour
+ * Set the .end_contour member of the last corner
+ * of the last contour to true
+ */
+ void CloseLastContour()
+ {
+ m_CornersList.CloseLastContour();
+ }
+
+ void RemoveContour( int icont );
+
+ /**
+ * Function IsPolygonSelfIntersecting
+ * Test a CPolyLine for self-intersection of vertex (all contours).
+ *
+ * @return :
+ * false if no intersecting sides
+ * true if intersecting sides
+ * When a CPolyLine is self intersectic, it need to be normalized.
+ * (converted to non intersecting polygons)
+ */
+ bool IsPolygonSelfIntersecting();
+
+ /**
+ * Function Chamfer
+ * returns a chamfered version of a polygon.
+ * @param aDistance is the chamfering distance.
+ * @return CPolyLine* - Pointer to new polygon.
+ */
+ CPolyLine* Chamfer( unsigned int aDistance );
+
+ /**
+ * Function Fillet
+ * returns a filleted version of a polygon.
+ * @param aRadius is the fillet radius.
+ * @param aSegments is the number of segments / fillet.
+ * @return CPolyLine* - Pointer to new polygon.
+ */
+ CPolyLine* Fillet( unsigned int aRadius, unsigned int aSegments );
+
+ /**
+ * Function RemoveNullSegments
+ * Removes corners which create a null segment edge
+ * (i.e. when 2 successive corners are at the same location)
+ * @return the count of removed corners.
+ */
+ int RemoveNullSegments();
+
+ void RemoveAllContours( void );
+
+ // Remove or create hatch
+ void UnHatch();
+ void Hatch();
+
+ // Transform functions
+ void MoveOrigin( int x_off, int y_off );
+
+ // misc. functions
+ /**
+ * @return the full bounding box of polygons
+ */
+ const EDA_RECT GetBoundingBox();
+
+ /**
+ * @return the bounding box of a given polygon
+ * @param icont = the index of the polygon contour
+ * (0 = main contour, 1 ... n = other contours, usually holes)
+ */
+ const EDA_RECT GetBoundingBox( int icont );
+
+ void Copy( const CPolyLine* src );
+ bool TestPointInside( int x, int y );
+
+ /**
+ * @return true if the corner aCornerIdx is on a hole inside the main outline
+ * and false if it is on the main outline
+ */
+ bool IsCutoutContour( int aCornerIdx );
+
+ /**
+ * Function AppendArc.
+ * Adds segments to current contour to approximate the given arc
+ */
+ void AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num );
+
+ // access functions
+ void SetLayer( LAYER_NUM aLayer ) { m_layer = aLayer; }
+ LAYER_NUM GetLayer() const { return m_layer; }
+
+ int GetCornersCount() const
+ {
+ return m_CornersList.GetCornersCount();
+ }
+
+ /**
+ * @return true if the last corner in corners list is flagged end_contour
+ */
+ bool GetClosed();
+
+ /**
+ * Function GetContoursCount.
+ * @return the number of polygons stored in list
+ * (number of corners flagged "end_contour"
+ */
+ int GetContoursCount() const;
+
+ /**
+ * Function GetContour.
+ * @return the contour number containing the corner ic
+ * @param ic = the index of the corner in the corner list
+ */
+ int GetContour( int ic );
+
+ /**
+ * Function GetContourStart.
+ * @return the index of the first corner (in corners list) of a contour
+ * @param icont = the index of the contour
+ */
+ int GetContourStart( int icont );
+
+ /**
+ * Function GetContourEnd.
+ * @return the index of the last corner (in corners list) of a contour
+ * @param icont = the index of the contour
+ */
+ int GetContourEnd( int icont );
+
+ /**
+ * Function GetContourSize.
+ * @return the corners count of a contour
+ * @param icont = the index of the contour
+ */
+ int GetContourSize( int icont );
+
+ int GetX( int ic ) const { return m_CornersList.GetX( ic ); }
+ int GetY( int ic ) const { return m_CornersList.GetY( ic ); }
+
+ /**
+ * Function IsEndContour.
+ * @return true if a corner is flagged end_contour
+ * @param ic= the index (in corners list) of the corner
+ */
+ bool IsEndContour( int ic ) const { return m_CornersList.IsEndContour( ic ); }
+
+ const wxPoint& GetPos( int ic ) const { return m_CornersList.GetPos( ic ); }
+
+ int GetHatchPitch() const { return m_hatchPitch; }
+ static int GetDefaultHatchPitchMils() { return 20; } // default hatch pitch value in mils
+
+ enum HATCH_STYLE GetHatchStyle() const { return m_hatchStyle; }
+ void SetHatch( int aHatchStyle, int aHatchPitch, bool aRebuildHatch )
+ {
+ SetHatchPitch( aHatchPitch );
+ m_hatchStyle = (enum HATCH_STYLE) aHatchStyle;
+
+ if( aRebuildHatch )
+ Hatch();
+ }
+
+ void SetX( int ic, int x )
+ {
+ m_CornersList.SetX( ic, x );
+ }
+
+ void SetY( int ic, int y )
+ {
+ m_CornersList.SetY( ic, y );
+ }
+
+ void SetHatchStyle( enum HATCH_STYLE style )
+ {
+ m_hatchStyle = style;
+ }
+
+ void SetHatchPitch( int pitch ) { m_hatchPitch = pitch; }
+
+ /**
+ * Function NormalizeAreaOutlines
+ * Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
+ * Removes null segments.
+ * @param aNewPolygonList = a std::vector<CPolyLine*> reference where to store new CPolyLine
+ * needed by the normalization
+ * @return the polygon count (always >= 1, because there is at least one polygon)
+ * There are new polygons only if the polygon count is > 1
+ */
+ int NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList );
+
+ // Bezier Support
+ void AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3 );
+ void AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );
+
+ /**
+ * Function Distance
+ * Calculates the distance between a point and the zone:
+ * @param aPoint the coordinate of the point.
+ * @return int = distance between the point and outline.
+ * 0 if the point is inside
+ */
+ int Distance( const wxPoint& aPoint );
+
+ /**
+ * Function Distance
+ * Calculates the distance between a segment and the zone:
+ * @param aStart the starting point of the segment.
+ * @param aEnd the ending point of the segment.
+ * @param aWidth the width of the segment.
+ * @return int = distance between the segment and outline.
+ * 0 if segment intersects or is inside
+ */
+ int Distance( wxPoint aStart, wxPoint aEnd, int aWidth );
+
+ /**
+ * Function HitTestForEdge
+ * test is the point aPos is near (< aDistMax ) a vertex
+ * @param aPos = the reference point
+ * @param aDistMax = the max distance between a vertex and the reference point
+ * @return int = the index of the first corner of the vertex, or -1 if not found.
+ */
+ int HitTestForEdge( const wxPoint& aPos, int aDistMax ) const;
+
+ /**
+ * Function HitTestForCorner
+ * test is the point aPos is near (< aDistMax ) a corner
+ * @param aPos = the reference point
+ * @param aDistMax = the max distance between a vertex and the corner
+ * @return int = the index of corner of the, or -1 if not found.
+ */
+ int HitTestForCorner( const wxPoint& aPos, int aDistMax ) const;
+
+private:
+ LAYER_NUM m_layer; // layer to draw on
+ enum HATCH_STYLE m_hatchStyle; // hatch style, see enum above
+ int m_hatchPitch; // for DIAGONAL_EDGE hatched outlines, basic distance between 2 hatch lines
+ // and the len of eacvh segment
+ // for DIAGONAL_FULL, the pitch is twice this value
+ int m_flags; // a flag used in some calculations
+public:
+ CPOLYGONS_LIST m_CornersList; // array of points for corners
+ std::vector <CSegment> m_HatchLines; // hatch lines showing the polygon area
+};
+
+const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList );
+const CPOLYGONS_LIST ConvertPolySetToPolyList( const SHAPE_POLY_SET& aPolyset );
+
+#endif // #ifndef POLYLINE_H
diff --git a/polygon/SutherlandHodgmanClipPoly.h b/polygon/SutherlandHodgmanClipPoly.h
new file mode 100644
index 0000000..506ee0f
--- /dev/null
+++ b/polygon/SutherlandHodgmanClipPoly.h
@@ -0,0 +1,273 @@
+/********************************************************************************
+* Copyright (C) 2004 Sjaak Priester
+*
+* This 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 file 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 Tinter; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+********************************************************************************/
+
+// SutherlandHodgman
+// Class to perform polygon clipping against an upright rectangular boundary window.
+// Implementation of Sutherland-Hodgman algorithm (1974).
+//
+// Version 1.0 (C) 2004, Sjaak Priester, Amsterdam.
+// mailto:sjaak@sjaakpriester.nl
+// http://www.sjaakpriester.nl
+
+#ifndef __SUTHERLAND_HODGMAN_H__
+#define __SUTHERLAND_HODGMAN_H__
+
+
+#include <vector>
+#include <functional>
+
+#ifndef _GDIPLUS_H
+
+// I designed this with GDI+ in mind. However, this particular code doesn't
+// use GDI+ at all, only some of it's variable types.
+// These definitions are substitutes for those of GDI+.
+typedef double REAL;
+class PointF
+{
+public:
+ REAL X;
+ REAL Y;
+
+ PointF() : X( 0 )
+ , Y( 0 ) { }
+ PointF( const PointF& p ) : X( p.X )
+ , Y( p.Y ) { }
+ PointF( REAL x, REAL y ) : X( x )
+ , Y( y ) { }
+ PointF operator+( const PointF& p ) const { return PointF( X + p.X, Y + p.Y ); }
+ PointF operator-( const PointF& p ) const { return PointF( X - p.X, Y - p.Y ); }
+ bool Equals( const PointF& p ) { return (X == p.X) && (Y == p.Y); }
+};
+
+class RectF
+{
+public:
+ REAL X;
+ REAL Y;
+ REAL Width;
+ REAL Height;
+
+ RectF() { X = 0, Y = 0, Height = 0, Width = 0; }
+ RectF( const RectF& r )
+ {
+ X = r.X; Y = r.Y; Height = r.Height, Width = r.Width;
+ }
+
+
+ RectF( REAL x, REAL y, REAL w, REAL h ) : X( x ), Y( y ),Width( w ), Height( h )
+ { }
+ REAL GetLeft() const { return X; }
+ REAL GetTop() const { return Y; }
+ REAL GetRight() const { return X + Width; }
+ REAL GetBottom() const { return Y + Height; }
+};
+
+#endif // _GDIPLUS_H
+
+typedef std::vector<PointF> pointVector;
+typedef std::vector<PointF>::iterator pointIterator;
+typedef std::vector<PointF>::const_iterator cpointIterator;
+
+class SutherlandHodgman
+{
+public:
+
+ // Constructor. Parameter is the boundary rectangle.
+ // SutherlandHodgman expects a 'normalized' boundary rectangle, meaning
+ // that boundaries.GetRight() > boundaries.GetLeft() and
+ // boundaries.GetBottom() > boundaries.GetTop().
+ // In other words: boundary.Width > 0 and boundaries.Height > 0.
+ // If this is violated, nothing will be output.
+ SutherlandHodgman( RectF& boundaries ) :
+ m_stageBottom( m_stageOut, boundaries.GetBottom() )
+ , /* Initialize each stage */ m_stageLeft( m_stageBottom, boundaries.GetLeft() )
+ , /* with its next stage and */ m_stageTop( m_stageLeft, boundaries.GetTop() )
+ , /* the boundary position. */ m_stageRight( m_stageTop, boundaries.GetRight() )
+ {
+ }
+
+
+ void Clip( pointVector& input, pointVector& clipped )
+ {
+ clipped.clear();
+ m_stageOut.SetDestination( &clipped );
+
+ // Clip each input vertex.
+ for( cpointIterator it = input.begin(); it != input.end(); ++it )
+ m_stageRight.HandleVertex( *it );
+
+ // Do the final step.
+ m_stageRight.Finalize();
+ }
+
+
+private:
+
+ // Implementation of a horizontal boundary (top or bottom).
+ // Comp is a std::binary_function object, comparing its two parameters, f.i. std::less.
+
+ template <class Comp>
+
+ class BoundaryHor
+ {
+public:
+ BoundaryHor( REAL y ) : m_Y( y ) { }
+ bool IsInside( const PointF& pnt ) const
+ {
+ return Comp ()( pnt.Y, m_Y );
+ } // return true if pnt.Y is at the inside of the boundary
+ PointF Intersect( const PointF& p0, const PointF& p1 ) const // return intersection point of line p0...p1 with boundary
+ { // assumes p0...p1 is not strictly horizontal
+ PointF d = p1 - p0;
+ REAL xslope = d.X / d.Y;
+
+ PointF r;
+
+ r.Y = m_Y;
+ r.X = p0.X + xslope * (m_Y - p0.Y);
+ return r;
+ }
+
+
+private:
+ REAL m_Y;
+ };
+
+ // Implementation of a vertical boundary (left or right).
+ template <class Comp>
+ class BoundaryVert
+ {
+public:
+ BoundaryVert( REAL x ) : m_X( x )
+ { }
+ bool IsInside( const PointF& pnt ) const
+ {
+ return Comp() ( pnt.X, m_X );
+ }
+ PointF Intersect( const PointF& p0, const PointF& p1 ) const // assumes p0...p1 is not strictly vertical
+ {
+ PointF d = p1 - p0;
+ REAL yslope = d.Y / d.X;
+
+ PointF r;
+
+ r.X = m_X;
+ r.Y = p0.Y + yslope * (m_X - p0.X);
+ return r;
+ }
+
+
+private:
+ REAL m_X;
+ };
+
+ // This template class is the workhorse of the algorithm. It handles the clipping against one boundary.
+ // Boundary is either BoundaryHor or BoundaryVert, Stage is the next ClipStage, or the output stage.
+ template <class Boundary, class Stage>
+ class ClipStage : private Boundary
+ {
+public:
+ ClipStage( Stage& nextStage, REAL position ) :
+ Boundary( position ) , m_NextStage( nextStage ), m_bFirst( true ), m_bPreviousInside( false )
+ { }
+
+ // Function to handle one vertex
+ void HandleVertex( const PointF& pntCurrent )
+ {
+ bool bCurrentInside = this->IsInside( pntCurrent ); // See if vertex is inside the boundary.
+
+ if( m_bFirst ) // If this is the first vertex...
+ {
+ m_pntFirst = pntCurrent; // ... just remember it,...
+
+ m_bFirst = false;
+ }
+ else // Common cases, not the first vertex.
+ {
+ if( bCurrentInside ) // If this vertex is inside...
+ {
+ if( !m_bPreviousInside ) // ... and the previous one was outside
+ m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) );
+
+ // ... first output the intersection point.
+
+ m_NextStage.HandleVertex( pntCurrent ); // Output the current vertex.
+ }
+ else if( m_bPreviousInside ) // If this vertex is outside, and the previous one was inside...
+ m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) );
+
+ // ... output the intersection point.
+
+ // If neither current vertex nor the previous one are inside, output nothing.
+ }
+ m_pntPrevious = pntCurrent; // Be prepared for next vertex.
+ m_bPreviousInside = bCurrentInside;
+ }
+
+
+ void Finalize()
+ {
+ HandleVertex( m_pntFirst ); // Close the polygon.
+ m_NextStage.Finalize(); // Delegate to the next stage.
+ }
+
+
+private:
+ Stage& m_NextStage; // the next stage
+ bool m_bFirst; // true if no vertices have been handled
+ PointF m_pntFirst; // the first vertex
+ PointF m_pntPrevious; // the previous vertex
+ bool m_bPreviousInside; // true if the previous vertex was inside the Boundary
+ };
+
+ class OutputStage
+ {
+public:
+ OutputStage() : m_pDest( 0 ) { }
+ void SetDestination( pointVector* pDest ) { m_pDest = pDest; }
+ void HandleVertex( const PointF& pnt ) { m_pDest->push_back( pnt ); } // Append the vertex to the output container.
+ void Finalize() { } // Do nothing.
+private:
+ pointVector* m_pDest;
+ };
+
+ // These typedefs define the four boundaries. In keeping up with the GDI/GDI+ interpretation of
+ // rectangles, we include the left and top boundaries, but not the right and bottom boundaries.
+ // In other words: a vertex on the left boundary is considered to be inside, but a vertex
+ // on the right boundary is considered to be outside.
+ typedef BoundaryVert<std::less<REAL> > BoundaryRight;
+ typedef BoundaryHor<std::greater_equal<REAL> > BoundaryTop;
+ typedef BoundaryVert<std::greater_equal<REAL> > BoundaryLeft;
+ typedef BoundaryHor<std::less<REAL> > BoundaryBottom;
+
+ // Next typedefs define the four stages. First template parameter is the boundary,
+ // second template parameter is the next stage.
+ typedef ClipStage<BoundaryBottom, OutputStage> ClipBottom;
+ typedef ClipStage<BoundaryLeft, ClipBottom> ClipLeft;
+ typedef ClipStage<BoundaryTop, ClipLeft> ClipTop;
+ typedef ClipStage<BoundaryRight, ClipTop> ClipRight;
+
+ // Our data members.
+ OutputStage m_stageOut;
+ ClipBottom m_stageBottom;
+ ClipLeft m_stageLeft;
+ ClipTop m_stageTop;
+ ClipRight m_stageRight;
+};
+
+#endif
diff --git a/polygon/clipper.cpp b/polygon/clipper.cpp
new file mode 100644
index 0000000..6d6ce6e
--- /dev/null
+++ b/polygon/clipper.cpp
@@ -0,0 +1,4642 @@
+/*******************************************************************************
+* *
+* Author : Angus Johnson *
+* Version : 6.4.0 *
+* Date : 2 July 2015 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2015 *
+* *
+* License: *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt *
+* *
+* Attributions: *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping" *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
+* http://portal.acm.org/citation.cfm?id=129906 *
+* *
+* Computer graphics and geometric modeling: implementation and algorithms *
+* By Max K. Agoston *
+* Springer; 1 edition (January 4, 2005) *
+* http://books.google.com/books?q=vatti+clipping+agoston *
+* *
+* See also: *
+* "Polygon Offsetting by Computing Winding Numbers" *
+* Paper no. DETC2005-85513 pp. 565-575 *
+* ASME 2005 International Design Engineering Technical Conferences *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
+* September 24-28, 2005 , Long Beach, California, USA *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
+* *
+*******************************************************************************/
+
+/*******************************************************************************
+* *
+* This is a translation of the Delphi Clipper library and the naming style *
+* used has retained a Delphi flavour. *
+* *
+*******************************************************************************/
+
+#include "clipper.hpp"
+#include <cmath>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+#include <functional>
+
+namespace ClipperLib {
+
+static double const pi = 3.141592653589793238;
+static double const two_pi = pi *2;
+static double const def_arc_tolerance = 0.25;
+
+enum Direction { dRightToLeft, dLeftToRight };
+
+static int const Unassigned = -1; //edge not currently 'owning' a solution
+static int const Skip = -2; //edge that would otherwise close a path
+
+#define HORIZONTAL (-1.0E+40)
+#define TOLERANCE (1.0e-20)
+#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
+
+struct TEdge {
+ IntPoint Bot;
+ IntPoint Curr; //current (updated for every new scanbeam)
+ IntPoint Top;
+ double Dx;
+ PolyType PolyTyp;
+ EdgeSide Side; //side only refers to current side of solution poly
+ int WindDelta; //1 or -1 depending on winding direction
+ int WindCnt;
+ int WindCnt2; //winding count of the opposite polytype
+ int OutIdx;
+ TEdge *Next;
+ TEdge *Prev;
+ TEdge *NextInLML;
+ TEdge *NextInAEL;
+ TEdge *PrevInAEL;
+ TEdge *NextInSEL;
+ TEdge *PrevInSEL;
+};
+
+struct IntersectNode {
+ TEdge *Edge1;
+ TEdge *Edge2;
+ IntPoint Pt;
+};
+
+struct LocalMinimum {
+ cInt Y;
+ TEdge *LeftBound;
+ TEdge *RightBound;
+};
+
+struct OutPt;
+
+//OutRec: contains a path in the clipping solution. Edges in the AEL will
+//carry a pointer to an OutRec when they are part of the clipping solution.
+struct OutRec {
+ int Idx;
+ bool IsHole;
+ bool IsOpen;
+ OutRec *FirstLeft; //see comments in clipper.pas
+ PolyNode *PolyNd;
+ OutPt *Pts;
+ OutPt *BottomPt;
+};
+
+struct OutPt {
+ int Idx;
+ IntPoint Pt;
+ OutPt *Next;
+ OutPt *Prev;
+};
+
+struct Join {
+ OutPt *OutPt1;
+ OutPt *OutPt2;
+ IntPoint OffPt;
+};
+
+struct LocMinSorter
+{
+ inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2)
+ {
+ return locMin2.Y < locMin1.Y;
+ }
+};
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+inline cInt Round(double val)
+{
+ if ((val < 0)) return static_cast<cInt>(val - 0.5);
+ else return static_cast<cInt>(val + 0.5);
+}
+//------------------------------------------------------------------------------
+
+inline cInt Abs(cInt val)
+{
+ return val < 0 ? -val : val;
+}
+
+//------------------------------------------------------------------------------
+// PolyTree methods ...
+//------------------------------------------------------------------------------
+
+void PolyTree::Clear()
+{
+ for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i)
+ delete AllNodes[i];
+ AllNodes.resize(0);
+ Childs.resize(0);
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyTree::GetFirst() const
+{
+ if (!Childs.empty())
+ return Childs[0];
+ else
+ return 0;
+}
+//------------------------------------------------------------------------------
+
+int PolyTree::Total() const
+{
+ int result = (int)AllNodes.size();
+ //with negative offsets, ignore the hidden outer polygon ...
+ if (result > 0 && Childs[0] != AllNodes[0]) result--;
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// PolyNode methods ...
+//------------------------------------------------------------------------------
+
+PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false)
+{
+ // Avoid uninitialized vars
+ m_endtype = etClosedPolygon;
+ m_jointype = jtSquare;
+}
+//------------------------------------------------------------------------------
+
+int PolyNode::ChildCount() const
+{
+ return (int)Childs.size();
+}
+//------------------------------------------------------------------------------
+
+void PolyNode::AddChild(PolyNode& child)
+{
+ unsigned cnt = (unsigned)Childs.size();
+ Childs.push_back(&child);
+ child.Parent = this;
+ child.Index = cnt;
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNext() const
+{
+ if (!Childs.empty())
+ return Childs[0];
+ else
+ return GetNextSiblingUp();
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNextSiblingUp() const
+{
+ if (!Parent) //protects against PolyTree.GetNextSiblingUp()
+ return 0;
+ else if (Index == Parent->Childs.size() - 1)
+ return Parent->GetNextSiblingUp();
+ else
+ return Parent->Childs[Index + 1];
+}
+//------------------------------------------------------------------------------
+
+bool PolyNode::IsHole() const
+{
+ bool result = true;
+ PolyNode* node = Parent;
+ while (node)
+ {
+ result = !result;
+ node = node->Parent;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool PolyNode::IsOpen() const
+{
+ return m_IsOpen;
+}
+//------------------------------------------------------------------------------
+
+#ifndef use_int32
+
+//------------------------------------------------------------------------------
+// Int128 class (enables safe math on signed 64bit integers)
+// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1
+// Int128 val2((long64)9223372036854775807);
+// Int128 val3 = val1 * val2;
+// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37)
+//------------------------------------------------------------------------------
+
+class Int128
+{
+ public:
+ ulong64 lo;
+ long64 hi;
+
+ Int128(long64 _lo = 0)
+ {
+ lo = (ulong64)_lo;
+ if (_lo < 0) hi = -1; else hi = 0;
+ }
+
+
+ Int128(const Int128 &val): lo(val.lo), hi(val.hi){}
+
+ Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){}
+
+ Int128& operator = (const long64 &val)
+ {
+ lo = (ulong64)val;
+ if (val < 0) hi = -1; else hi = 0;
+ return *this;
+ }
+
+ bool operator == (const Int128 &val) const
+ {return (hi == val.hi && lo == val.lo);}
+
+ bool operator != (const Int128 &val) const
+ { return !(*this == val);}
+
+ bool operator > (const Int128 &val) const
+ {
+ if (hi != val.hi)
+ return hi > val.hi;
+ else
+ return lo > val.lo;
+ }
+
+ bool operator < (const Int128 &val) const
+ {
+ if (hi != val.hi)
+ return hi < val.hi;
+ else
+ return lo < val.lo;
+ }
+
+ bool operator >= (const Int128 &val) const
+ { return !(*this < val);}
+
+ bool operator <= (const Int128 &val) const
+ { return !(*this > val);}
+
+ Int128& operator += (const Int128 &rhs)
+ {
+ hi += rhs.hi;
+ lo += rhs.lo;
+ if (lo < rhs.lo) hi++;
+ return *this;
+ }
+
+ Int128 operator + (const Int128 &rhs) const
+ {
+ Int128 result(*this);
+ result+= rhs;
+ return result;
+ }
+
+ Int128& operator -= (const Int128 &rhs)
+ {
+ *this += -rhs;
+ return *this;
+ }
+
+ Int128 operator - (const Int128 &rhs) const
+ {
+ Int128 result(*this);
+ result -= rhs;
+ return result;
+ }
+
+ Int128 operator-() const //unary negation
+ {
+ if (lo == 0)
+ return Int128(-hi, 0);
+ else
+ return Int128(~hi, ~lo + 1);
+ }
+
+ operator double() const
+ {
+ const double shift64 = 18446744073709551616.0; //2^64
+ if (hi < 0)
+ {
+ if (lo == 0) return (double)hi * shift64;
+ else return -(double)(~lo + ~hi * shift64);
+ }
+ else
+ return (double)(lo + hi * shift64);
+ }
+
+};
+//------------------------------------------------------------------------------
+
+Int128 Int128Mul (long64 lhs, long64 rhs)
+{
+ bool negate = (lhs < 0) != (rhs < 0);
+
+ if (lhs < 0) lhs = -lhs;
+ ulong64 int1Hi = ulong64(lhs) >> 32;
+ ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF);
+
+ if (rhs < 0) rhs = -rhs;
+ ulong64 int2Hi = ulong64(rhs) >> 32;
+ ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF);
+
+ //nb: see comments in clipper.pas
+ ulong64 a = int1Hi * int2Hi;
+ ulong64 b = int1Lo * int2Lo;
+ ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi;
+
+ Int128 tmp;
+ tmp.hi = long64(a + (c >> 32));
+ tmp.lo = long64(c << 32);
+ tmp.lo += long64(b);
+ if (tmp.lo < b) tmp.hi++;
+ if (negate) tmp = -tmp;
+ return tmp;
+};
+#endif
+
+//------------------------------------------------------------------------------
+// Miscellaneous global functions
+//------------------------------------------------------------------------------
+
+bool Orientation(const Path &poly)
+{
+ return Area(poly) >= 0;
+}
+//------------------------------------------------------------------------------
+
+double Area(const Path &poly)
+{
+ int size = (int)poly.size();
+ if (size < 3) return 0;
+
+ double a = 0;
+ for (int i = 0, j = size -1; i < size; ++i)
+ {
+ a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y);
+ j = i;
+ }
+ return -a * 0.5;
+}
+//------------------------------------------------------------------------------
+
+double Area(const OutPt *op)
+{
+ const OutPt *startOp = op;
+ if (!op) return 0;
+ double a = 0;
+ do {
+ a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y);
+ op = op->Next;
+ } while (op != startOp);
+ return a * 0.5;
+}
+//------------------------------------------------------------------------------
+
+double Area(const OutRec &outRec)
+{
+ return Area(outRec.Pts);
+}
+//------------------------------------------------------------------------------
+
+bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
+{
+ OutPt *pp2 = pp;
+ do
+ {
+ if (pp2->Pt == Pt) return true;
+ pp2 = pp2->Next;
+ }
+ while (pp2 != pp);
+ return false;
+}
+//------------------------------------------------------------------------------
+
+//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
+//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
+int PointInPolygon(const IntPoint &pt, const Path &path)
+{
+ //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+ int result = 0;
+ size_t cnt = path.size();
+ if (cnt < 3) return 0;
+ IntPoint ip = path[0];
+ for(size_t i = 1; i <= cnt; ++i)
+ {
+ IntPoint ipNext = (i == cnt ? path[0] : path[i]);
+ if (ipNext.Y == pt.Y)
+ {
+ if ((ipNext.X == pt.X) || (ip.Y == pt.Y &&
+ ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1;
+ }
+ if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y))
+ {
+ if (ip.X >= pt.X)
+ {
+ if (ipNext.X > pt.X) result = 1 - result;
+ else
+ {
+ double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
+ (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+ }
+ } else
+ {
+ if (ipNext.X > pt.X)
+ {
+ double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
+ (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+ }
+ }
+ }
+ ip = ipNext;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+int PointInPolygon (const IntPoint &pt, OutPt *op)
+{
+ //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+ int result = 0;
+ OutPt* startOp = op;
+ for(;;)
+ {
+ if (op->Next->Pt.Y == pt.Y)
+ {
+ if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y &&
+ ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1;
+ }
+ if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y))
+ {
+ if (op->Pt.X >= pt.X)
+ {
+ if (op->Next->Pt.X > pt.X) result = 1 - result;
+ else
+ {
+ double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -
+ (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;
+ }
+ } else
+ {
+ if (op->Next->Pt.X > pt.X)
+ {
+ double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -
+ (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;
+ }
+ }
+ }
+ op = op->Next;
+ if (startOp == op) break;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2)
+{
+ OutPt* op = OutPt1;
+ do
+ {
+ //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
+ int res = PointInPolygon(op->Pt, OutPt2);
+ if (res >= 0) return res > 0;
+ op = op->Next;
+ }
+ while (op != OutPt1);
+ return true;
+}
+//----------------------------------------------------------------------
+
+bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range)
+{
+#ifndef use_int32
+ if (UseFullInt64Range)
+ return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) ==
+ Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y);
+ else
+#endif
+ return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) ==
+ (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y);
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+ const IntPoint pt3, bool UseFullInt64Range)
+{
+#ifndef use_int32
+ if (UseFullInt64Range)
+ return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y);
+ else
+#endif
+ return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y);
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+ const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range)
+{
+#ifndef use_int32
+ if (UseFullInt64Range)
+ return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y);
+ else
+#endif
+ return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsHorizontal(TEdge &e)
+{
+ return e.Dx == HORIZONTAL;
+}
+//------------------------------------------------------------------------------
+
+inline double GetDx(const IntPoint pt1, const IntPoint pt2)
+{
+ return (pt1.Y == pt2.Y) ?
+ HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y);
+}
+//---------------------------------------------------------------------------
+
+inline void SetDx(TEdge &e)
+{
+ cInt dy = (e.Top.Y - e.Bot.Y);
+ if (dy == 0) e.Dx = HORIZONTAL;
+ else e.Dx = (double)(e.Top.X - e.Bot.X) / dy;
+}
+//---------------------------------------------------------------------------
+
+inline void SwapSides(TEdge &Edge1, TEdge &Edge2)
+{
+ EdgeSide Side = Edge1.Side;
+ Edge1.Side = Edge2.Side;
+ Edge2.Side = Side;
+}
+//------------------------------------------------------------------------------
+
+inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2)
+{
+ int OutIdx = Edge1.OutIdx;
+ Edge1.OutIdx = Edge2.OutIdx;
+ Edge2.OutIdx = OutIdx;
+}
+//------------------------------------------------------------------------------
+
+inline cInt TopX(TEdge &edge, const cInt currentY)
+{
+ return ( currentY == edge.Top.Y ) ?
+ edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y));
+}
+//------------------------------------------------------------------------------
+
+void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
+{
+#ifdef use_xyz
+ ip.Z = 0;
+#endif
+
+ double b1, b2;
+ if (Edge1.Dx == Edge2.Dx)
+ {
+ ip.Y = Edge1.Curr.Y;
+ ip.X = TopX(Edge1, ip.Y);
+ return;
+ }
+ else if (Edge1.Dx == 0)
+ {
+ ip.X = Edge1.Bot.X;
+ if (IsHorizontal(Edge2))
+ ip.Y = Edge2.Bot.Y;
+ else
+ {
+ b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx);
+ ip.Y = Round(ip.X / Edge2.Dx + b2);
+ }
+ }
+ else if (Edge2.Dx == 0)
+ {
+ ip.X = Edge2.Bot.X;
+ if (IsHorizontal(Edge1))
+ ip.Y = Edge1.Bot.Y;
+ else
+ {
+ b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx);
+ ip.Y = Round(ip.X / Edge1.Dx + b1);
+ }
+ }
+ else
+ {
+ b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx;
+ b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx;
+ double q = (b2-b1) / (Edge1.Dx - Edge2.Dx);
+ ip.Y = Round(q);
+ if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))
+ ip.X = Round(Edge1.Dx * q + b1);
+ else
+ ip.X = Round(Edge2.Dx * q + b2);
+ }
+
+ if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y)
+ {
+ if (Edge1.Top.Y > Edge2.Top.Y)
+ ip.Y = Edge1.Top.Y;
+ else
+ ip.Y = Edge2.Top.Y;
+ if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))
+ ip.X = TopX(Edge1, ip.Y);
+ else
+ ip.X = TopX(Edge2, ip.Y);
+ }
+ //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
+ if (ip.Y > Edge1.Curr.Y)
+ {
+ ip.Y = Edge1.Curr.Y;
+ //use the more vertical edge to derive X ...
+ if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx))
+ ip.X = TopX(Edge2, ip.Y); else
+ ip.X = TopX(Edge1, ip.Y);
+ }
+}
+//------------------------------------------------------------------------------
+
+void ReversePolyPtLinks(OutPt *pp)
+{
+ if (!pp) return;
+ OutPt *pp1, *pp2;
+ pp1 = pp;
+ do {
+ pp2 = pp1->Next;
+ pp1->Next = pp1->Prev;
+ pp1->Prev = pp2;
+ pp1 = pp2;
+ } while( pp1 != pp );
+}
+//------------------------------------------------------------------------------
+
+void DisposeOutPts(OutPt*& pp)
+{
+ if (pp == 0) return;
+ pp->Prev->Next = 0;
+ while( pp )
+ {
+ OutPt *tmpPp = pp;
+ pp = pp->Next;
+ delete tmpPp;
+ }
+}
+//------------------------------------------------------------------------------
+
+inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt)
+{
+ std::memset(e, 0, sizeof(TEdge));
+ e->Next = eNext;
+ e->Prev = ePrev;
+ e->Curr = Pt;
+ e->OutIdx = Unassigned;
+}
+//------------------------------------------------------------------------------
+
+void InitEdge2(TEdge& e, PolyType Pt)
+{
+ if (e.Curr.Y >= e.Next->Curr.Y)
+ {
+ e.Bot = e.Curr;
+ e.Top = e.Next->Curr;
+ } else
+ {
+ e.Top = e.Curr;
+ e.Bot = e.Next->Curr;
+ }
+ SetDx(e);
+ e.PolyTyp = Pt;
+}
+//------------------------------------------------------------------------------
+
+TEdge* RemoveEdge(TEdge* e)
+{
+ //removes e from double_linked_list (but without removing from memory)
+ e->Prev->Next = e->Next;
+ e->Next->Prev = e->Prev;
+ TEdge* result = e->Next;
+ e->Prev = 0; //flag as removed (see ClipperBase.Clear)
+ return result;
+}
+//------------------------------------------------------------------------------
+
+inline void ReverseHorizontal(TEdge &e)
+{
+ //swap horizontal edges' Top and Bottom x's so they follow the natural
+ //progression of the bounds - ie so their xbots will align with the
+ //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
+ std::swap(e.Top.X, e.Bot.X);
+#ifdef use_xyz
+ std::swap(e.Top.Z, e.Bot.Z);
+#endif
+}
+//------------------------------------------------------------------------------
+
+void SwapPoints(IntPoint &pt1, IntPoint &pt2)
+{
+ IntPoint tmp = pt1;
+ pt1 = pt2;
+ pt2 = tmp;
+}
+//------------------------------------------------------------------------------
+
+bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a,
+ IntPoint pt2b, IntPoint &pt1, IntPoint &pt2)
+{
+ //precondition: segments are Collinear.
+ if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y))
+ {
+ if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b);
+ if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b);
+ if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a;
+ if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b;
+ return pt1.X < pt2.X;
+ } else
+ {
+ if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b);
+ if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b);
+ if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a;
+ if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b;
+ return pt1.Y > pt2.Y;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2)
+{
+ OutPt *p = btmPt1->Prev;
+ while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev;
+ double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt));
+ p = btmPt1->Next;
+ while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next;
+ double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt));
+
+ p = btmPt2->Prev;
+ while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev;
+ double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt));
+ p = btmPt2->Next;
+ while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next;
+ double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt));
+
+ if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) &&
+ std::min(dx1p, dx1n) == std::min(dx2p, dx2n))
+ return Area(btmPt1) > 0; //if otherwise identical use orientation
+ else
+ return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
+}
+//------------------------------------------------------------------------------
+
+OutPt* GetBottomPt(OutPt *pp)
+{
+ OutPt* dups = 0;
+ OutPt* p = pp->Next;
+ while (p != pp)
+ {
+ if (p->Pt.Y > pp->Pt.Y)
+ {
+ pp = p;
+ dups = 0;
+ }
+ else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X)
+ {
+ if (p->Pt.X < pp->Pt.X)
+ {
+ dups = 0;
+ pp = p;
+ } else
+ {
+ if (p->Next != pp && p->Prev != pp) dups = p;
+ }
+ }
+ p = p->Next;
+ }
+ if (dups)
+ {
+ //there appears to be at least 2 vertices at BottomPt so ...
+ while (dups != p)
+ {
+ if (!FirstIsBottomPt(p, dups)) pp = dups;
+ dups = dups->Next;
+ while (dups->Pt != pp->Pt) dups = dups->Next;
+ }
+ }
+ return pp;
+}
+//------------------------------------------------------------------------------
+
+bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,
+ const IntPoint pt2, const IntPoint pt3)
+{
+ if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2))
+ return false;
+ else if (pt1.X != pt3.X)
+ return (pt2.X > pt1.X) == (pt2.X < pt3.X);
+ else
+ return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y);
+}
+//------------------------------------------------------------------------------
+
+bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
+{
+ if (seg1a > seg1b) std::swap(seg1a, seg1b);
+ if (seg2a > seg2b) std::swap(seg2a, seg2b);
+ return (seg1a < seg2b) && (seg2a < seg1b);
+}
+
+//------------------------------------------------------------------------------
+// ClipperBase class methods ...
+//------------------------------------------------------------------------------
+
+ClipperBase::ClipperBase() //constructor
+{
+ m_CurrentLM = m_MinimaList.begin(); //begin() == end() here
+ m_UseFullRange = false;
+
+ // Avoid uninitialized vars
+ m_PreserveCollinear = false;
+ m_HasOpenPaths = false;
+ m_ActiveEdges = NULL;
+}
+//------------------------------------------------------------------------------
+
+ClipperBase::~ClipperBase() //destructor
+{
+ Clear();
+}
+//------------------------------------------------------------------------------
+
+void RangeTest(const IntPoint& Pt, bool& useFullRange)
+{
+ if (useFullRange)
+ {
+ if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)
+ throw clipperException("Coordinate outside allowed range");
+ }
+ else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange)
+ {
+ useFullRange = true;
+ RangeTest(Pt, useFullRange);
+ }
+}
+//------------------------------------------------------------------------------
+
+TEdge* FindNextLocMin(TEdge* E)
+{
+ for (;;)
+ {
+ while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next;
+ if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break;
+ while (IsHorizontal(*E->Prev)) E = E->Prev;
+ TEdge* E2 = E;
+ while (IsHorizontal(*E)) E = E->Next;
+ if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz.
+ if (E2->Prev->Bot.X < E->Bot.X) E = E2;
+ break;
+ }
+ return E;
+}
+//------------------------------------------------------------------------------
+
+TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
+{
+ TEdge *Result = E;
+ TEdge *Horz = 0;
+
+ if (E->OutIdx == Skip)
+ {
+ //if edges still remain in the current bound beyond the skip edge then
+ //create another LocMin and call ProcessBound once more
+ if (NextIsForward)
+ {
+ while (E->Top.Y == E->Next->Bot.Y) E = E->Next;
+ //don't include top horizontals when parsing a bound a second time,
+ //they will be contained in the opposite bound ...
+ while (E != Result && IsHorizontal(*E)) E = E->Prev;
+ }
+ else
+ {
+ while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev;
+ while (E != Result && IsHorizontal(*E)) E = E->Next;
+ }
+
+ if (E == Result)
+ {
+ if (NextIsForward) Result = E->Next;
+ else Result = E->Prev;
+ }
+ else
+ {
+ //there are more edges in the bound beyond result starting with E
+ if (NextIsForward)
+ E = Result->Next;
+ else
+ E = Result->Prev;
+ MinimaList::value_type locMin;
+ locMin.Y = E->Bot.Y;
+ locMin.LeftBound = 0;
+ locMin.RightBound = E;
+ E->WindDelta = 0;
+ Result = ProcessBound(E, NextIsForward);
+ m_MinimaList.push_back(locMin);
+ }
+ return Result;
+ }
+
+ TEdge *EStart;
+
+ if (IsHorizontal(*E))
+ {
+ //We need to be careful with open paths because this may not be a
+ //true local minima (ie E may be following a skip edge).
+ //Also, consecutive horz. edges may start heading left before going right.
+ if (NextIsForward)
+ EStart = E->Prev;
+ else
+ EStart = E->Next;
+ if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge
+ {
+ if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X)
+ ReverseHorizontal(*E);
+ }
+ else if (EStart->Bot.X != E->Bot.X)
+ ReverseHorizontal(*E);
+ }
+
+ EStart = E;
+ if (NextIsForward)
+ {
+ while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip)
+ Result = Result->Next;
+ if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip)
+ {
+ //nb: at the top of a bound, horizontals are added to the bound
+ //only when the preceding edge attaches to the horizontal's left vertex
+ //unless a Skip edge is encountered when that becomes the top divide
+ Horz = Result;
+ while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev;
+ if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;
+ }
+ while (E != Result)
+ {
+ E->NextInLML = E->Next;
+ if (IsHorizontal(*E) && E != EStart &&
+ E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
+ E = E->Next;
+ }
+ if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X)
+ ReverseHorizontal(*E);
+ Result = Result->Next; //move to the edge just beyond current bound
+ } else
+ {
+ while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip)
+ Result = Result->Prev;
+ if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip)
+ {
+ Horz = Result;
+ while (IsHorizontal(*Horz->Next)) Horz = Horz->Next;
+ if (Horz->Next->Top.X == Result->Prev->Top.X ||
+ Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;
+ }
+
+ while (E != Result)
+ {
+ E->NextInLML = E->Prev;
+ if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X)
+ ReverseHorizontal(*E);
+ E = E->Prev;
+ }
+ if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X)
+ ReverseHorizontal(*E);
+ Result = Result->Prev; //move to the edge just beyond current bound
+ }
+
+ return Result;
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
+{
+#ifdef use_lines
+ if (!Closed && PolyTyp == ptClip)
+ throw clipperException("AddPath: Open paths must be subject.");
+#else
+ if (!Closed)
+ throw clipperException("AddPath: Open paths have been disabled.");
+#endif
+
+ int highI = (int)pg.size() -1;
+ if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI;
+ while (highI > 0 && (pg[highI] == pg[highI -1])) --highI;
+ if ((Closed && highI < 2) || (!Closed && highI < 1)) return false;
+
+ //create a new edge array ...
+ TEdge *edges = new TEdge [highI +1];
+
+ bool IsFlat = true;
+ //1. Basic (first) edge initialization ...
+ try
+ {
+ edges[1].Curr = pg[1];
+ RangeTest(pg[0], m_UseFullRange);
+ RangeTest(pg[highI], m_UseFullRange);
+ InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]);
+ InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]);
+ for (int i = highI - 1; i >= 1; --i)
+ {
+ RangeTest(pg[i], m_UseFullRange);
+ InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
+ }
+ }
+ catch(...)
+ {
+ delete [] edges;
+ throw; //range test fails
+ }
+ TEdge *eStart = &edges[0];
+
+ //2. Remove duplicate vertices, and (when closed) collinear edges ...
+ TEdge *E = eStart, *eLoopStop = eStart;
+ for (;;)
+ {
+ //nb: allows matching start and end points when not Closed ...
+ if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart))
+ {
+ if (E == E->Next) break;
+ if (E == eStart) eStart = E->Next;
+ E = RemoveEdge(E);
+ eLoopStop = E;
+ continue;
+ }
+ if (E->Prev == E->Next)
+ break; //only two vertices
+ else if (Closed &&
+ SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) &&
+ (!m_PreserveCollinear ||
+ !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr)))
+ {
+ //Collinear edges are allowed for open paths but in closed paths
+ //the default is to merge adjacent collinear edges into a single edge.
+ //However, if the PreserveCollinear property is enabled, only overlapping
+ //collinear edges (ie spikes) will be removed from closed paths.
+ if (E == eStart) eStart = E->Next;
+ E = RemoveEdge(E);
+ E = E->Prev;
+ eLoopStop = E;
+ continue;
+ }
+ E = E->Next;
+ if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break;
+ }
+
+ if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next)))
+ {
+ delete [] edges;
+ return false;
+ }
+
+ if (!Closed)
+ {
+ m_HasOpenPaths = true;
+ eStart->Prev->OutIdx = Skip;
+ }
+
+ //3. Do second stage of edge initialization ...
+ E = eStart;
+ do
+ {
+ InitEdge2(*E, PolyTyp);
+ E = E->Next;
+ if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false;
+ }
+ while (E != eStart);
+
+ //4. Finally, add edge bounds to LocalMinima list ...
+
+ //Totally flat paths must be handled differently when adding them
+ //to LocalMinima list to avoid endless loops etc ...
+ if (IsFlat)
+ {
+ if (Closed)
+ {
+ delete [] edges;
+ return false;
+ }
+ E->Prev->OutIdx = Skip;
+ MinimaList::value_type locMin;
+ locMin.Y = E->Bot.Y;
+ locMin.LeftBound = 0;
+ locMin.RightBound = E;
+ locMin.RightBound->Side = esRight;
+ locMin.RightBound->WindDelta = 0;
+ for (;;)
+ {
+ if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
+ if (E->Next->OutIdx == Skip) break;
+ E->NextInLML = E->Next;
+ E = E->Next;
+ }
+ m_MinimaList.push_back(locMin);
+ m_edges.push_back(edges);
+ return true;
+ }
+
+ m_edges.push_back(edges);
+ bool leftBoundIsForward;
+ TEdge* EMin = 0;
+
+ //workaround to avoid an endless loop in the while loop below when
+ //open paths have matching start and end points ...
+ if (E->Prev->Bot == E->Prev->Top) E = E->Next;
+
+ for (;;)
+ {
+ E = FindNextLocMin(E);
+ if (E == EMin) break;
+ else if (!EMin) EMin = E;
+
+ //E and E.Prev now share a local minima (left aligned if horizontal).
+ //Compare their slopes to find which starts which bound ...
+ MinimaList::value_type locMin;
+ locMin.Y = E->Bot.Y;
+ if (E->Dx < E->Prev->Dx)
+ {
+ locMin.LeftBound = E->Prev;
+ locMin.RightBound = E;
+ leftBoundIsForward = false; //Q.nextInLML = Q.prev
+ } else
+ {
+ locMin.LeftBound = E;
+ locMin.RightBound = E->Prev;
+ leftBoundIsForward = true; //Q.nextInLML = Q.next
+ }
+
+ if (!Closed) locMin.LeftBound->WindDelta = 0;
+ else if (locMin.LeftBound->Next == locMin.RightBound)
+ locMin.LeftBound->WindDelta = -1;
+ else locMin.LeftBound->WindDelta = 1;
+ locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta;
+
+ E = ProcessBound(locMin.LeftBound, leftBoundIsForward);
+ if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward);
+
+ TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward);
+ if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward);
+
+ if (locMin.LeftBound->OutIdx == Skip)
+ locMin.LeftBound = 0;
+ else if (locMin.RightBound->OutIdx == Skip)
+ locMin.RightBound = 0;
+ m_MinimaList.push_back(locMin);
+ if (!leftBoundIsForward) E = E2;
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed)
+{
+ bool result = false;
+ for (Paths::size_type i = 0; i < ppg.size(); ++i)
+ if (AddPath(ppg[i], PolyTyp, Closed)) result = true;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Clear()
+{
+ DisposeLocalMinimaList();
+ for (EdgeList::size_type i = 0; i < m_edges.size(); ++i)
+ {
+ TEdge* edges = m_edges[i];
+ delete [] edges;
+ }
+ m_edges.clear();
+ m_UseFullRange = false;
+ m_HasOpenPaths = false;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Reset()
+{
+ m_CurrentLM = m_MinimaList.begin();
+ if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process
+ std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter());
+
+ m_Scanbeam = ScanbeamList(); //clears/resets priority_queue
+ //reset all edges ...
+ for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
+ {
+ InsertScanbeam(lm->Y);
+ TEdge* e = lm->LeftBound;
+ if (e)
+ {
+ e->Curr = e->Bot;
+ e->Side = esLeft;
+ e->OutIdx = Unassigned;
+ }
+
+ e = lm->RightBound;
+ if (e)
+ {
+ e->Curr = e->Bot;
+ e->Side = esRight;
+ e->OutIdx = Unassigned;
+ }
+ }
+ m_ActiveEdges = 0;
+ m_CurrentLM = m_MinimaList.begin();
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeLocalMinimaList()
+{
+ m_MinimaList.clear();
+ m_CurrentLM = m_MinimaList.begin();
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin)
+{
+ if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false;
+ locMin = &(*m_CurrentLM);
+ ++m_CurrentLM;
+ return true;
+}
+//------------------------------------------------------------------------------
+
+IntRect ClipperBase::GetBounds()
+{
+ IntRect result;
+ MinimaList::iterator lm = m_MinimaList.begin();
+ if (lm == m_MinimaList.end())
+ {
+ result.left = result.top = result.right = result.bottom = 0;
+ return result;
+ }
+ result.left = lm->LeftBound->Bot.X;
+ result.top = lm->LeftBound->Bot.Y;
+ result.right = lm->LeftBound->Bot.X;
+ result.bottom = lm->LeftBound->Bot.Y;
+ while (lm != m_MinimaList.end())
+ {
+ //todo - needs fixing for open paths
+ result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y);
+ TEdge* e = lm->LeftBound;
+ for (;;) {
+ TEdge* bottomE = e;
+ while (e->NextInLML)
+ {
+ if (e->Bot.X < result.left) result.left = e->Bot.X;
+ if (e->Bot.X > result.right) result.right = e->Bot.X;
+ e = e->NextInLML;
+ }
+ result.left = std::min(result.left, e->Bot.X);
+ result.right = std::max(result.right, e->Bot.X);
+ result.left = std::min(result.left, e->Top.X);
+ result.right = std::max(result.right, e->Top.X);
+ result.top = std::min(result.top, e->Top.Y);
+ if (bottomE == lm->LeftBound) e = lm->RightBound;
+ else break;
+ }
+ ++lm;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::InsertScanbeam(const cInt Y)
+{
+ m_Scanbeam.push(Y);
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::PopScanbeam(cInt &Y)
+{
+ if (m_Scanbeam.empty()) return false;
+ Y = m_Scanbeam.top();
+ m_Scanbeam.pop();
+ while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates.
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeAllOutRecs(){
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ DisposeOutRec(i);
+ m_PolyOuts.clear();
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeOutRec(PolyOutList::size_type index)
+{
+ OutRec *outRec = m_PolyOuts[index];
+ if (outRec->Pts) DisposeOutPts(outRec->Pts);
+ delete outRec;
+ m_PolyOuts[index] = 0;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DeleteFromAEL(TEdge *e)
+{
+ TEdge* AelPrev = e->PrevInAEL;
+ TEdge* AelNext = e->NextInAEL;
+ if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted
+ if (AelPrev) AelPrev->NextInAEL = AelNext;
+ else m_ActiveEdges = AelNext;
+ if (AelNext) AelNext->PrevInAEL = AelPrev;
+ e->NextInAEL = 0;
+ e->PrevInAEL = 0;
+}
+//------------------------------------------------------------------------------
+
+OutRec* ClipperBase::CreateOutRec()
+{
+ OutRec* result = new OutRec;
+ result->IsHole = false;
+ result->IsOpen = false;
+ result->FirstLeft = 0;
+ result->Pts = 0;
+ result->BottomPt = 0;
+ result->PolyNd = 0;
+ m_PolyOuts.push_back(result);
+ result->Idx = (int)m_PolyOuts.size() - 1;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2)
+{
+ //check that one or other edge hasn't already been removed from AEL ...
+ if (Edge1->NextInAEL == Edge1->PrevInAEL ||
+ Edge2->NextInAEL == Edge2->PrevInAEL) return;
+
+ if (Edge1->NextInAEL == Edge2)
+ {
+ TEdge* Next = Edge2->NextInAEL;
+ if (Next) Next->PrevInAEL = Edge1;
+ TEdge* Prev = Edge1->PrevInAEL;
+ if (Prev) Prev->NextInAEL = Edge2;
+ Edge2->PrevInAEL = Prev;
+ Edge2->NextInAEL = Edge1;
+ Edge1->PrevInAEL = Edge2;
+ Edge1->NextInAEL = Next;
+ }
+ else if (Edge2->NextInAEL == Edge1)
+ {
+ TEdge* Next = Edge1->NextInAEL;
+ if (Next) Next->PrevInAEL = Edge2;
+ TEdge* Prev = Edge2->PrevInAEL;
+ if (Prev) Prev->NextInAEL = Edge1;
+ Edge1->PrevInAEL = Prev;
+ Edge1->NextInAEL = Edge2;
+ Edge2->PrevInAEL = Edge1;
+ Edge2->NextInAEL = Next;
+ }
+ else
+ {
+ TEdge* Next = Edge1->NextInAEL;
+ TEdge* Prev = Edge1->PrevInAEL;
+ Edge1->NextInAEL = Edge2->NextInAEL;
+ if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1;
+ Edge1->PrevInAEL = Edge2->PrevInAEL;
+ if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1;
+ Edge2->NextInAEL = Next;
+ if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2;
+ Edge2->PrevInAEL = Prev;
+ if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2;
+ }
+
+ if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1;
+ else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e)
+{
+ if (!e->NextInLML)
+ throw clipperException("UpdateEdgeIntoAEL: invalid call");
+
+ e->NextInLML->OutIdx = e->OutIdx;
+ TEdge* AelPrev = e->PrevInAEL;
+ TEdge* AelNext = e->NextInAEL;
+ if (AelPrev) AelPrev->NextInAEL = e->NextInLML;
+ else m_ActiveEdges = e->NextInLML;
+ if (AelNext) AelNext->PrevInAEL = e->NextInLML;
+ e->NextInLML->Side = e->Side;
+ e->NextInLML->WindDelta = e->WindDelta;
+ e->NextInLML->WindCnt = e->WindCnt;
+ e->NextInLML->WindCnt2 = e->WindCnt2;
+ e = e->NextInLML;
+ e->Curr = e->Bot;
+ e->PrevInAEL = AelPrev;
+ e->NextInAEL = AelNext;
+ if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y);
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::LocalMinimaPending()
+{
+ return (m_CurrentLM != m_MinimaList.end());
+}
+
+//------------------------------------------------------------------------------
+// TClipper methods ...
+//------------------------------------------------------------------------------
+
+Clipper::Clipper(int initOptions) : ClipperBase() //constructor
+{
+ m_ExecuteLocked = false;
+ m_UseFullRange = false;
+ m_ReverseOutput = ((initOptions & ioReverseSolution) != 0);
+ m_StrictSimple = ((initOptions & ioStrictlySimple) != 0);
+ m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0);
+ m_HasOpenPaths = false;
+#ifdef use_xyz
+ m_ZFill = 0;
+#endif
+
+ // Avoid uninitialized vars
+ m_ClipType = ctIntersection;
+ m_SortedEdges = NULL;
+ m_ClipFillType = pftEvenOdd;
+ m_SubjFillType = pftEvenOdd;
+ m_UsingPolyTree = true;
+}
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+void Clipper::ZFillFunction(ZFillCallback zFillFunc)
+{
+ m_ZFill = zFillFunc;
+}
+//------------------------------------------------------------------------------
+#endif
+
+bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType)
+{
+ return Execute(clipType, solution, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType)
+{
+ return Execute(clipType, polytree, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, Paths &solution,
+ PolyFillType subjFillType, PolyFillType clipFillType)
+{
+ if( m_ExecuteLocked ) return false;
+ if (m_HasOpenPaths)
+ throw clipperException("Error: PolyTree struct is needed for open path clipping.");
+ m_ExecuteLocked = true;
+ solution.resize(0);
+ m_SubjFillType = subjFillType;
+ m_ClipFillType = clipFillType;
+ m_ClipType = clipType;
+ m_UsingPolyTree = false;
+ bool succeeded = ExecuteInternal();
+ if (succeeded) BuildResult(solution);
+ DisposeAllOutRecs();
+ m_ExecuteLocked = false;
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, PolyTree& polytree,
+ PolyFillType subjFillType, PolyFillType clipFillType)
+{
+ if( m_ExecuteLocked ) return false;
+ m_ExecuteLocked = true;
+ m_SubjFillType = subjFillType;
+ m_ClipFillType = clipFillType;
+ m_ClipType = clipType;
+ m_UsingPolyTree = true;
+ bool succeeded = ExecuteInternal();
+ if (succeeded) BuildResult2(polytree);
+ DisposeAllOutRecs();
+ m_ExecuteLocked = false;
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixHoleLinkage(OutRec &outrec)
+{
+ //skip OutRecs that (a) contain outermost polygons or
+ //(b) already have the correct owner/child linkage ...
+ if (!outrec.FirstLeft ||
+ (outrec.IsHole != outrec.FirstLeft->IsHole &&
+ outrec.FirstLeft->Pts)) return;
+
+ OutRec* orfl = outrec.FirstLeft;
+ while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts))
+ orfl = orfl->FirstLeft;
+ outrec.FirstLeft = orfl;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ExecuteInternal()
+{
+ bool succeeded = true;
+ try {
+ Reset();
+ m_Maxima = MaximaList();
+ m_SortedEdges = 0;
+
+ succeeded = true;
+ cInt botY, topY;
+ if (!PopScanbeam(botY)) return false;
+ InsertLocalMinimaIntoAEL(botY);
+ while (PopScanbeam(topY) || LocalMinimaPending())
+ {
+ ProcessHorizontals();
+ ClearGhostJoins();
+ if (!ProcessIntersections(topY))
+ {
+ succeeded = false;
+ break;
+ }
+ ProcessEdgesAtTopOfScanbeam(topY);
+ botY = topY;
+ InsertLocalMinimaIntoAEL(botY);
+ }
+ }
+ catch(...)
+ {
+ succeeded = false;
+ }
+
+ if (succeeded)
+ {
+ //fix orientations ...
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec *outRec = m_PolyOuts[i];
+ if (!outRec->Pts || outRec->IsOpen) continue;
+ if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0))
+ ReversePolyPtLinks(outRec->Pts);
+ }
+
+ if (!m_Joins.empty()) JoinCommonEdges();
+
+ //unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec *outRec = m_PolyOuts[i];
+ if (!outRec->Pts) continue;
+ if (outRec->IsOpen)
+ FixupOutPolyline(*outRec);
+ else
+ FixupOutPolygon(*outRec);
+ }
+
+ if (m_StrictSimple) DoSimplePolygons();
+ }
+
+ ClearJoins();
+ ClearGhostJoins();
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetWindingCount(TEdge &edge)
+{
+ TEdge *e = edge.PrevInAEL;
+ //find the edge of the same polytype that immediately preceeds 'edge' in AEL
+ while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL;
+ if (!e)
+ {
+ if (edge.WindDelta == 0)
+ {
+ PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType);
+ edge.WindCnt = (pft == pftNegative ? -1 : 1);
+ }
+ else
+ edge.WindCnt = edge.WindDelta;
+ edge.WindCnt2 = 0;
+ e = m_ActiveEdges; //ie get ready to calc WindCnt2
+ }
+ else if (edge.WindDelta == 0 && m_ClipType != ctUnion)
+ {
+ edge.WindCnt = 1;
+ edge.WindCnt2 = e->WindCnt2;
+ e = e->NextInAEL; //ie get ready to calc WindCnt2
+ }
+ else if (IsEvenOddFillType(edge))
+ {
+ //EvenOdd filling ...
+ if (edge.WindDelta == 0)
+ {
+ //are we inside a subj polygon ...
+ bool Inside = true;
+ TEdge *e2 = e->PrevInAEL;
+ while (e2)
+ {
+ if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0)
+ Inside = !Inside;
+ e2 = e2->PrevInAEL;
+ }
+ edge.WindCnt = (Inside ? 0 : 1);
+ }
+ else
+ {
+ edge.WindCnt = edge.WindDelta;
+ }
+ edge.WindCnt2 = e->WindCnt2;
+ e = e->NextInAEL; //ie get ready to calc WindCnt2
+ }
+ else
+ {
+ //nonZero, Positive or Negative filling ...
+ if (e->WindCnt * e->WindDelta < 0)
+ {
+ //prev edge is 'decreasing' WindCount (WC) toward zero
+ //so we're outside the previous polygon ...
+ if (Abs(e->WindCnt) > 1)
+ {
+ //outside prev poly but still inside another.
+ //when reversing direction of prev poly use the same WC
+ if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;
+ //otherwise continue to 'decrease' WC ...
+ else edge.WindCnt = e->WindCnt + edge.WindDelta;
+ }
+ else
+ //now outside all polys of same polytype so set own WC ...
+ edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
+ } else
+ {
+ //prev edge is 'increasing' WindCount (WC) away from zero
+ //so we're inside the previous polygon ...
+ if (edge.WindDelta == 0)
+ edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1);
+ //if wind direction is reversing prev then use same WC
+ else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;
+ //otherwise add to WC ...
+ else edge.WindCnt = e->WindCnt + edge.WindDelta;
+ }
+ edge.WindCnt2 = e->WindCnt2;
+ e = e->NextInAEL; //ie get ready to calc WindCnt2
+ }
+
+ //update WindCnt2 ...
+ if (IsEvenOddAltFillType(edge))
+ {
+ //EvenOdd filling ...
+ while (e != &edge)
+ {
+ if (e->WindDelta != 0)
+ edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0);
+ e = e->NextInAEL;
+ }
+ } else
+ {
+ //nonZero, Positive or Negative filling ...
+ while ( e != &edge )
+ {
+ edge.WindCnt2 += e->WindDelta;
+ e = e->NextInAEL;
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddFillType(const TEdge& edge) const
+{
+ if (edge.PolyTyp == ptSubject)
+ return m_SubjFillType == pftEvenOdd; else
+ return m_ClipFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const
+{
+ if (edge.PolyTyp == ptSubject)
+ return m_ClipFillType == pftEvenOdd; else
+ return m_SubjFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsContributing(const TEdge& edge) const
+{
+ PolyFillType pft, pft2;
+ if (edge.PolyTyp == ptSubject)
+ {
+ pft = m_SubjFillType;
+ pft2 = m_ClipFillType;
+ } else
+ {
+ pft = m_ClipFillType;
+ pft2 = m_SubjFillType;
+ }
+
+ switch(pft)
+ {
+ case pftEvenOdd:
+ //return false if a subj line has been flagged as inside a subj polygon
+ if (edge.WindDelta == 0 && edge.WindCnt != 1) return false;
+ break;
+ case pftNonZero:
+ if (Abs(edge.WindCnt) != 1) return false;
+ break;
+ case pftPositive:
+ if (edge.WindCnt != 1) return false;
+ break;
+ default: //pftNegative
+ if (edge.WindCnt != -1) return false;
+ }
+
+ switch(m_ClipType)
+ {
+ case ctIntersection:
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 != 0);
+ case pftPositive:
+ return (edge.WindCnt2 > 0);
+ default:
+ return (edge.WindCnt2 < 0);
+ }
+ break;
+ case ctUnion:
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 == 0);
+ case pftPositive:
+ return (edge.WindCnt2 <= 0);
+ default:
+ return (edge.WindCnt2 >= 0);
+ }
+ break;
+ case ctDifference:
+ if (edge.PolyTyp == ptSubject)
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 == 0);
+ case pftPositive:
+ return (edge.WindCnt2 <= 0);
+ default:
+ return (edge.WindCnt2 >= 0);
+ }
+ else
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 != 0);
+ case pftPositive:
+ return (edge.WindCnt2 > 0);
+ default:
+ return (edge.WindCnt2 < 0);
+ }
+ break;
+ case ctXor:
+ if (edge.WindDelta == 0) //XOr always contributing unless open
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 == 0);
+ case pftPositive:
+ return (edge.WindCnt2 <= 0);
+ default:
+ return (edge.WindCnt2 >= 0);
+ }
+ else
+ return true;
+ break;
+ default:
+ return true;
+ }
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)
+{
+ OutPt* result;
+ TEdge *e, *prevE;
+ if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx ))
+ {
+ result = AddOutPt(e1, Pt);
+ e2->OutIdx = e1->OutIdx;
+ e1->Side = esLeft;
+ e2->Side = esRight;
+ e = e1;
+ if (e->PrevInAEL == e2)
+ prevE = e2->PrevInAEL;
+ else
+ prevE = e->PrevInAEL;
+ } else
+ {
+ result = AddOutPt(e2, Pt);
+ e1->OutIdx = e2->OutIdx;
+ e1->Side = esRight;
+ e2->Side = esLeft;
+ e = e2;
+ if (e->PrevInAEL == e1)
+ prevE = e1->PrevInAEL;
+ else
+ prevE = e->PrevInAEL;
+ }
+
+ if (prevE && prevE->OutIdx >= 0)
+ {
+ cInt xPrev = TopX(*prevE, Pt.Y);
+ cInt xE = TopX(*e, Pt.Y);
+ if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) &&
+ SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange))
+ {
+ OutPt* outPt = AddOutPt(prevE, Pt);
+ AddJoin(result, outPt, e->Top);
+ }
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)
+{
+ AddOutPt( e1, Pt );
+ if (e2->WindDelta == 0) AddOutPt(e2, Pt);
+ if( e1->OutIdx == e2->OutIdx )
+ {
+ e1->OutIdx = Unassigned;
+ e2->OutIdx = Unassigned;
+ }
+ else if (e1->OutIdx < e2->OutIdx)
+ AppendPolygon(e1, e2);
+ else
+ AppendPolygon(e2, e1);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddEdgeToSEL(TEdge *edge)
+{
+ //SEL pointers in PEdge are reused to build a list of horizontal edges.
+ //However, we don't need to worry about order with horizontal edge processing.
+ if( !m_SortedEdges )
+ {
+ m_SortedEdges = edge;
+ edge->PrevInSEL = 0;
+ edge->NextInSEL = 0;
+ }
+ else
+ {
+ edge->NextInSEL = m_SortedEdges;
+ edge->PrevInSEL = 0;
+ m_SortedEdges->PrevInSEL = edge;
+ m_SortedEdges = edge;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::PopEdgeFromSEL(TEdge *&edge)
+{
+ if (!m_SortedEdges) return false;
+ edge = m_SortedEdges;
+ DeleteFromSEL(m_SortedEdges);
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::CopyAELToSEL()
+{
+ TEdge* e = m_ActiveEdges;
+ m_SortedEdges = e;
+ while ( e )
+ {
+ e->PrevInSEL = e->PrevInAEL;
+ e->NextInSEL = e->NextInAEL;
+ e = e->NextInAEL;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt)
+{
+ Join* j = new Join;
+ j->OutPt1 = op1;
+ j->OutPt2 = op2;
+ j->OffPt = OffPt;
+ m_Joins.push_back(j);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearJoins()
+{
+ for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+ delete m_Joins[i];
+ m_Joins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearGhostJoins()
+{
+ for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++)
+ delete m_GhostJoins[i];
+ m_GhostJoins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt)
+{
+ Join* j = new Join;
+ j->OutPt1 = op;
+ j->OutPt2 = 0;
+ j->OffPt = OffPt;
+ m_GhostJoins.push_back(j);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
+{
+ const LocalMinimum *lm;
+ while (PopLocalMinima(botY, lm))
+ {
+ TEdge* lb = lm->LeftBound;
+ TEdge* rb = lm->RightBound;
+
+ OutPt *Op1 = 0;
+ if (!lb)
+ {
+ //nb: don't insert LB into either AEL or SEL
+ InsertEdgeIntoAEL(rb, 0);
+ SetWindingCount(*rb);
+ if (IsContributing(*rb))
+ Op1 = AddOutPt(rb, rb->Bot);
+ }
+ else if (!rb)
+ {
+ InsertEdgeIntoAEL(lb, 0);
+ SetWindingCount(*lb);
+ if (IsContributing(*lb))
+ Op1 = AddOutPt(lb, lb->Bot);
+ InsertScanbeam(lb->Top.Y);
+ }
+ else
+ {
+ InsertEdgeIntoAEL(lb, 0);
+ InsertEdgeIntoAEL(rb, lb);
+ SetWindingCount( *lb );
+ rb->WindCnt = lb->WindCnt;
+ rb->WindCnt2 = lb->WindCnt2;
+ if (IsContributing(*lb))
+ Op1 = AddLocalMinPoly(lb, rb, lb->Bot);
+ InsertScanbeam(lb->Top.Y);
+ }
+
+ if (rb)
+ {
+ if (IsHorizontal(*rb))
+ {
+ AddEdgeToSEL(rb);
+ if (rb->NextInLML)
+ InsertScanbeam(rb->NextInLML->Top.Y);
+ }
+ else InsertScanbeam( rb->Top.Y );
+ }
+
+ if (!lb || !rb) continue;
+
+ //if any output polygons share an edge, they'll need joining later ...
+ if (Op1 && IsHorizontal(*rb) &&
+ m_GhostJoins.size() > 0 && (rb->WindDelta != 0))
+ {
+ for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i)
+ {
+ Join* jr = m_GhostJoins[i];
+ //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
+ //the 'ghost' join to a real join ready for later ...
+ if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X))
+ AddJoin(jr->OutPt1, Op1, jr->OffPt);
+ }
+ }
+
+ if (lb->OutIdx >= 0 && lb->PrevInAEL &&
+ lb->PrevInAEL->Curr.X == lb->Bot.X &&
+ lb->PrevInAEL->OutIdx >= 0 &&
+ SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) &&
+ (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0))
+ {
+ OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot);
+ AddJoin(Op1, Op2, lb->Top);
+ }
+
+ if(lb->NextInAEL != rb)
+ {
+
+ if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 &&
+ SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) &&
+ (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0))
+ {
+ OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot);
+ AddJoin(Op1, Op2, rb->Top);
+ }
+
+ TEdge* e = lb->NextInAEL;
+ if (e)
+ {
+ while( e != rb )
+ {
+ //nb: For calculating winding counts etc, IntersectEdges() assumes
+ //that param1 will be to the Right of param2 ABOVE the intersection ...
+ IntersectEdges(rb , e , lb->Curr); //order important here
+ e = e->NextInAEL;
+ }
+ }
+ }
+
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DeleteFromSEL(TEdge *e)
+{
+ TEdge* SelPrev = e->PrevInSEL;
+ TEdge* SelNext = e->NextInSEL;
+ if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted
+ if( SelPrev ) SelPrev->NextInSEL = SelNext;
+ else m_SortedEdges = SelNext;
+ if( SelNext ) SelNext->PrevInSEL = SelPrev;
+ e->NextInSEL = 0;
+ e->PrevInSEL = 0;
+}
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2)
+{
+ if (pt.Z != 0 || !m_ZFill) return;
+ else if (pt == e1.Bot) pt.Z = e1.Bot.Z;
+ else if (pt == e1.Top) pt.Z = e1.Top.Z;
+ else if (pt == e2.Bot) pt.Z = e2.Bot.Z;
+ else if (pt == e2.Top) pt.Z = e2.Top.Z;
+ else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt);
+}
+//------------------------------------------------------------------------------
+#endif
+
+void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)
+{
+ bool e1Contributing = ( e1->OutIdx >= 0 );
+ bool e2Contributing = ( e2->OutIdx >= 0 );
+
+#ifdef use_xyz
+ SetZ(Pt, *e1, *e2);
+#endif
+
+#ifdef use_lines
+ //if either edge is on an OPEN path ...
+ if (e1->WindDelta == 0 || e2->WindDelta == 0)
+ {
+ //ignore subject-subject open path intersections UNLESS they
+ //are both open paths, AND they are both 'contributing maximas' ...
+ if (e1->WindDelta == 0 && e2->WindDelta == 0) return;
+
+ //if intersecting a subj line with a subj poly ...
+ else if (e1->PolyTyp == e2->PolyTyp &&
+ e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion)
+ {
+ if (e1->WindDelta == 0)
+ {
+ if (e2Contributing)
+ {
+ AddOutPt(e1, Pt);
+ if (e1Contributing) e1->OutIdx = Unassigned;
+ }
+ }
+ else
+ {
+ if (e1Contributing)
+ {
+ AddOutPt(e2, Pt);
+ if (e2Contributing) e2->OutIdx = Unassigned;
+ }
+ }
+ }
+ else if (e1->PolyTyp != e2->PolyTyp)
+ {
+ //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ...
+ if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 &&
+ (m_ClipType != ctUnion || e2->WindCnt2 == 0))
+ {
+ AddOutPt(e1, Pt);
+ if (e1Contributing) e1->OutIdx = Unassigned;
+ }
+ else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) &&
+ (m_ClipType != ctUnion || e1->WindCnt2 == 0))
+ {
+ AddOutPt(e2, Pt);
+ if (e2Contributing) e2->OutIdx = Unassigned;
+ }
+ }
+ return;
+ }
+#endif
+
+ //update winding counts...
+ //assumes that e1 will be to the Right of e2 ABOVE the intersection
+ if ( e1->PolyTyp == e2->PolyTyp )
+ {
+ if ( IsEvenOddFillType( *e1) )
+ {
+ int oldE1WindCnt = e1->WindCnt;
+ e1->WindCnt = e2->WindCnt;
+ e2->WindCnt = oldE1WindCnt;
+ } else
+ {
+ if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt;
+ else e1->WindCnt += e2->WindDelta;
+ if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt;
+ else e2->WindCnt -= e1->WindDelta;
+ }
+ } else
+ {
+ if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta;
+ else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0;
+ if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta;
+ else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0;
+ }
+
+ PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;
+ if (e1->PolyTyp == ptSubject)
+ {
+ e1FillType = m_SubjFillType;
+ e1FillType2 = m_ClipFillType;
+ } else
+ {
+ e1FillType = m_ClipFillType;
+ e1FillType2 = m_SubjFillType;
+ }
+ if (e2->PolyTyp == ptSubject)
+ {
+ e2FillType = m_SubjFillType;
+ e2FillType2 = m_ClipFillType;
+ } else
+ {
+ e2FillType = m_ClipFillType;
+ e2FillType2 = m_SubjFillType;
+ }
+
+ cInt e1Wc, e2Wc;
+ switch (e1FillType)
+ {
+ case pftPositive: e1Wc = e1->WindCnt; break;
+ case pftNegative: e1Wc = -e1->WindCnt; break;
+ default: e1Wc = Abs(e1->WindCnt);
+ }
+ switch(e2FillType)
+ {
+ case pftPositive: e2Wc = e2->WindCnt; break;
+ case pftNegative: e2Wc = -e2->WindCnt; break;
+ default: e2Wc = Abs(e2->WindCnt);
+ }
+
+ if ( e1Contributing && e2Contributing )
+ {
+ if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
+ (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) )
+ {
+ AddLocalMaxPoly(e1, e2, Pt);
+ }
+ else
+ {
+ AddOutPt(e1, Pt);
+ AddOutPt(e2, Pt);
+ SwapSides( *e1 , *e2 );
+ SwapPolyIndexes( *e1 , *e2 );
+ }
+ }
+ else if ( e1Contributing )
+ {
+ if (e2Wc == 0 || e2Wc == 1)
+ {
+ AddOutPt(e1, Pt);
+ SwapSides(*e1, *e2);
+ SwapPolyIndexes(*e1, *e2);
+ }
+ }
+ else if ( e2Contributing )
+ {
+ if (e1Wc == 0 || e1Wc == 1)
+ {
+ AddOutPt(e2, Pt);
+ SwapSides(*e1, *e2);
+ SwapPolyIndexes(*e1, *e2);
+ }
+ }
+ else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1))
+ {
+ //neither edge is currently contributing ...
+
+ cInt e1Wc2, e2Wc2;
+ switch (e1FillType2)
+ {
+ case pftPositive: e1Wc2 = e1->WindCnt2; break;
+ case pftNegative : e1Wc2 = -e1->WindCnt2; break;
+ default: e1Wc2 = Abs(e1->WindCnt2);
+ }
+ switch (e2FillType2)
+ {
+ case pftPositive: e2Wc2 = e2->WindCnt2; break;
+ case pftNegative: e2Wc2 = -e2->WindCnt2; break;
+ default: e2Wc2 = Abs(e2->WindCnt2);
+ }
+
+ if (e1->PolyTyp != e2->PolyTyp)
+ {
+ AddLocalMinPoly(e1, e2, Pt);
+ }
+ else if (e1Wc == 1 && e2Wc == 1)
+ switch( m_ClipType ) {
+ case ctIntersection:
+ if (e1Wc2 > 0 && e2Wc2 > 0)
+ AddLocalMinPoly(e1, e2, Pt);
+ break;
+ case ctUnion:
+ if ( e1Wc2 <= 0 && e2Wc2 <= 0 )
+ AddLocalMinPoly(e1, e2, Pt);
+ break;
+ case ctDifference:
+ if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
+ ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
+ AddLocalMinPoly(e1, e2, Pt);
+ break;
+ case ctXor:
+ AddLocalMinPoly(e1, e2, Pt);
+ }
+ else
+ SwapSides( *e1, *e2 );
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetHoleState(TEdge *e, OutRec *outrec)
+{
+ TEdge *e2 = e->PrevInAEL;
+ TEdge *eTmp = 0;
+ while (e2)
+ {
+ if (e2->OutIdx >= 0 && e2->WindDelta != 0)
+ {
+ if (!eTmp) eTmp = e2;
+ else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0;
+ }
+ e2 = e2->PrevInAEL;
+ }
+ if (!eTmp)
+ {
+ outrec->FirstLeft = 0;
+ outrec->IsHole = false;
+ }
+ else
+ {
+ outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx];
+ outrec->IsHole = !outrec->FirstLeft->IsHole;
+ }
+}
+//------------------------------------------------------------------------------
+
+OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2)
+{
+ //work out which polygon fragment has the correct hole state ...
+ if (!outRec1->BottomPt)
+ outRec1->BottomPt = GetBottomPt(outRec1->Pts);
+ if (!outRec2->BottomPt)
+ outRec2->BottomPt = GetBottomPt(outRec2->Pts);
+ OutPt *OutPt1 = outRec1->BottomPt;
+ OutPt *OutPt2 = outRec2->BottomPt;
+ if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1;
+ else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2;
+ else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1;
+ else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2;
+ else if (OutPt1->Next == OutPt1) return outRec2;
+ else if (OutPt2->Next == OutPt2) return outRec1;
+ else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1;
+ else return outRec2;
+}
+//------------------------------------------------------------------------------
+
+bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2)
+{
+ do
+ {
+ outRec1 = outRec1->FirstLeft;
+ if (outRec1 == outRec2) return true;
+ } while (outRec1);
+ return false;
+}
+//------------------------------------------------------------------------------
+
+OutRec* Clipper::GetOutRec(int Idx)
+{
+ OutRec* outrec = m_PolyOuts[Idx];
+ while (outrec != m_PolyOuts[outrec->Idx])
+ outrec = m_PolyOuts[outrec->Idx];
+ return outrec;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
+{
+ //get the start and ends of both output polygons ...
+ OutRec *outRec1 = m_PolyOuts[e1->OutIdx];
+ OutRec *outRec2 = m_PolyOuts[e2->OutIdx];
+
+ OutRec *holeStateRec;
+ if (OutRec1RightOfOutRec2(outRec1, outRec2))
+ holeStateRec = outRec2;
+ else if (OutRec1RightOfOutRec2(outRec2, outRec1))
+ holeStateRec = outRec1;
+ else
+ holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+ //get the start and ends of both output polygons and
+ //join e2 poly onto e1 poly and delete pointers to e2 ...
+
+ OutPt* p1_lft = outRec1->Pts;
+ OutPt* p1_rt = p1_lft->Prev;
+ OutPt* p2_lft = outRec2->Pts;
+ OutPt* p2_rt = p2_lft->Prev;
+
+ //join e2 poly onto e1 poly and delete pointers to e2 ...
+ if( e1->Side == esLeft )
+ {
+ if( e2->Side == esLeft )
+ {
+ //z y x a b c
+ ReversePolyPtLinks(p2_lft);
+ p2_lft->Next = p1_lft;
+ p1_lft->Prev = p2_lft;
+ p1_rt->Next = p2_rt;
+ p2_rt->Prev = p1_rt;
+ outRec1->Pts = p2_rt;
+ } else
+ {
+ //x y z a b c
+ p2_rt->Next = p1_lft;
+ p1_lft->Prev = p2_rt;
+ p2_lft->Prev = p1_rt;
+ p1_rt->Next = p2_lft;
+ outRec1->Pts = p2_lft;
+ }
+ } else
+ {
+ if( e2->Side == esRight )
+ {
+ //a b c z y x
+ ReversePolyPtLinks(p2_lft);
+ p1_rt->Next = p2_rt;
+ p2_rt->Prev = p1_rt;
+ p2_lft->Next = p1_lft;
+ p1_lft->Prev = p2_lft;
+ } else
+ {
+ //a b c x y z
+ p1_rt->Next = p2_lft;
+ p2_lft->Prev = p1_rt;
+ p1_lft->Prev = p2_rt;
+ p2_rt->Next = p1_lft;
+ }
+ }
+
+ outRec1->BottomPt = 0;
+ if (holeStateRec == outRec2)
+ {
+ if (outRec2->FirstLeft != outRec1)
+ outRec1->FirstLeft = outRec2->FirstLeft;
+ outRec1->IsHole = outRec2->IsHole;
+ }
+ outRec2->Pts = 0;
+ outRec2->BottomPt = 0;
+ outRec2->FirstLeft = outRec1;
+
+ int OKIdx = e1->OutIdx;
+ int ObsoleteIdx = e2->OutIdx;
+
+ e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly
+ e2->OutIdx = Unassigned;
+
+ TEdge* e = m_ActiveEdges;
+ while( e )
+ {
+ if( e->OutIdx == ObsoleteIdx )
+ {
+ e->OutIdx = OKIdx;
+ e->Side = e1->Side;
+ break;
+ }
+ e = e->NextInAEL;
+ }
+
+ outRec2->Idx = outRec1->Idx;
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
+{
+ if( e->OutIdx < 0 )
+ {
+ OutRec *outRec = CreateOutRec();
+ outRec->IsOpen = (e->WindDelta == 0);
+ OutPt* newOp = new OutPt;
+ outRec->Pts = newOp;
+ newOp->Idx = outRec->Idx;
+ newOp->Pt = pt;
+ newOp->Next = newOp;
+ newOp->Prev = newOp;
+ if (!outRec->IsOpen)
+ SetHoleState(e, outRec);
+ e->OutIdx = outRec->Idx;
+ return newOp;
+ } else
+ {
+ OutRec *outRec = m_PolyOuts[e->OutIdx];
+ //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
+ OutPt* op = outRec->Pts;
+
+ bool ToFront = (e->Side == esLeft);
+ if (ToFront && (pt == op->Pt)) return op;
+ else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;
+
+ OutPt* newOp = new OutPt;
+ newOp->Idx = outRec->Idx;
+ newOp->Pt = pt;
+ newOp->Next = op;
+ newOp->Prev = op->Prev;
+ newOp->Prev->Next = newOp;
+ op->Prev = newOp;
+ if (ToFront) outRec->Pts = newOp;
+ return newOp;
+ }
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::GetLastOutPt(TEdge *e)
+{
+ OutRec *outRec = m_PolyOuts[e->OutIdx];
+ if (e->Side == esLeft)
+ return outRec->Pts;
+ else
+ return outRec->Pts->Prev;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessHorizontals()
+{
+ TEdge* horzEdge;
+ while (PopEdgeFromSEL(horzEdge))
+ ProcessHorizontal(horzEdge);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMinima(TEdge *e)
+{
+ return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMaxima(TEdge *e, const cInt Y)
+{
+ return e && e->Top.Y == Y && !e->NextInLML;
+}
+//------------------------------------------------------------------------------
+
+inline bool IsIntermediate(TEdge *e, const cInt Y)
+{
+ return e->Top.Y == Y && e->NextInLML;
+}
+//------------------------------------------------------------------------------
+
+TEdge *GetMaximaPair(TEdge *e)
+{
+ if ((e->Next->Top == e->Top) && !e->Next->NextInLML)
+ return e->Next;
+ else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML)
+ return e->Prev;
+ else return 0;
+}
+//------------------------------------------------------------------------------
+
+TEdge *GetMaximaPairEx(TEdge *e)
+{
+ //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal)
+ TEdge* result = GetMaximaPair(e);
+ if (result && (result->OutIdx == Skip ||
+ (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2)
+{
+ if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return;
+ if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return;
+
+ if( Edge1->NextInSEL == Edge2 )
+ {
+ TEdge* Next = Edge2->NextInSEL;
+ if( Next ) Next->PrevInSEL = Edge1;
+ TEdge* Prev = Edge1->PrevInSEL;
+ if( Prev ) Prev->NextInSEL = Edge2;
+ Edge2->PrevInSEL = Prev;
+ Edge2->NextInSEL = Edge1;
+ Edge1->PrevInSEL = Edge2;
+ Edge1->NextInSEL = Next;
+ }
+ else if( Edge2->NextInSEL == Edge1 )
+ {
+ TEdge* Next = Edge1->NextInSEL;
+ if( Next ) Next->PrevInSEL = Edge2;
+ TEdge* Prev = Edge2->PrevInSEL;
+ if( Prev ) Prev->NextInSEL = Edge1;
+ Edge1->PrevInSEL = Prev;
+ Edge1->NextInSEL = Edge2;
+ Edge2->PrevInSEL = Edge1;
+ Edge2->NextInSEL = Next;
+ }
+ else
+ {
+ TEdge* Next = Edge1->NextInSEL;
+ TEdge* Prev = Edge1->PrevInSEL;
+ Edge1->NextInSEL = Edge2->NextInSEL;
+ if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1;
+ Edge1->PrevInSEL = Edge2->PrevInSEL;
+ if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1;
+ Edge2->NextInSEL = Next;
+ if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2;
+ Edge2->PrevInSEL = Prev;
+ if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2;
+ }
+
+ if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1;
+ else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2;
+}
+//------------------------------------------------------------------------------
+
+TEdge* GetNextInAEL(TEdge *e, Direction dir)
+{
+ return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL;
+}
+//------------------------------------------------------------------------------
+
+void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right)
+{
+ if (HorzEdge.Bot.X < HorzEdge.Top.X)
+ {
+ Left = HorzEdge.Bot.X;
+ Right = HorzEdge.Top.X;
+ Dir = dLeftToRight;
+ } else
+ {
+ Left = HorzEdge.Top.X;
+ Right = HorzEdge.Bot.X;
+ Dir = dRightToLeft;
+ }
+}
+//------------------------------------------------------------------------
+
+/*******************************************************************************
+* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or *
+* Bottom of a scanbeam) are processed as if layered. The order in which HEs *
+* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] *
+* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), *
+* and with other non-horizontal edges [*]. Once these intersections are *
+* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into *
+* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. *
+*******************************************************************************/
+
+void Clipper::ProcessHorizontal(TEdge *horzEdge)
+{
+ Direction dir;
+ cInt horzLeft, horzRight;
+ bool IsOpen = (horzEdge->WindDelta == 0);
+
+ GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
+
+ TEdge* eLastHorz = horzEdge, *eMaxPair = 0;
+ while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML))
+ eLastHorz = eLastHorz->NextInLML;
+ if (!eLastHorz->NextInLML)
+ eMaxPair = GetMaximaPair(eLastHorz);
+
+ MaximaList::const_iterator maxIt;
+ MaximaList::const_reverse_iterator maxRit;
+ if (m_Maxima.size() > 0)
+ {
+ //get the first maxima in range (X) ...
+ if (dir == dLeftToRight)
+ {
+ maxIt = m_Maxima.begin();
+ while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++;
+ if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X)
+ maxIt = m_Maxima.end();
+ }
+ else
+ {
+ maxRit = m_Maxima.rbegin();
+ while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++;
+ if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X)
+ maxRit = m_Maxima.rend();
+ }
+ }
+
+ OutPt* op1 = 0;
+
+ for (;;) //loop through consec. horizontal edges
+ {
+
+ bool IsLastHorz = (horzEdge == eLastHorz);
+ TEdge* e = GetNextInAEL(horzEdge, dir);
+ while(e)
+ {
+
+ //this code block inserts extra coords into horizontal edges (in output
+ //polygons) whereever maxima touch these horizontal edges. This helps
+ //'simplifying' polygons (ie if the Simplify property is set).
+ if (m_Maxima.size() > 0)
+ {
+ if (dir == dLeftToRight)
+ {
+ while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X)
+ {
+ if (horzEdge->OutIdx >= 0 && !IsOpen)
+ AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y));
+ maxIt++;
+ }
+ }
+ else
+ {
+ while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X)
+ {
+ if (horzEdge->OutIdx >= 0 && !IsOpen)
+ AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y));
+ maxRit++;
+ }
+ }
+ };
+
+ if ((dir == dLeftToRight && e->Curr.X > horzRight) ||
+ (dir == dRightToLeft && e->Curr.X < horzLeft)) break;
+
+ //Also break if we've got to the end of an intermediate horizontal edge ...
+ //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
+ if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML &&
+ e->Dx < horzEdge->NextInLML->Dx) break;
+
+ if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times
+ {
+ op1 = AddOutPt(horzEdge, e->Curr);
+ TEdge* eNextHorz = m_SortedEdges;
+ while (eNextHorz)
+ {
+ if (eNextHorz->OutIdx >= 0 &&
+ HorzSegmentsOverlap(horzEdge->Bot.X,
+ horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
+ {
+ OutPt* op2 = GetLastOutPt(eNextHorz);
+ AddJoin(op2, op1, eNextHorz->Top);
+ }
+ eNextHorz = eNextHorz->NextInSEL;
+ }
+ AddGhostJoin(op1, horzEdge->Bot);
+ }
+
+ //OK, so far we're still in range of the horizontal Edge but make sure
+ //we're at the last of consec. horizontals when matching with eMaxPair
+ if(e == eMaxPair && IsLastHorz)
+ {
+ if (horzEdge->OutIdx >= 0)
+ AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);
+ DeleteFromAEL(horzEdge);
+ DeleteFromAEL(eMaxPair);
+ return;
+ }
+
+ if(dir == dLeftToRight)
+ {
+ IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
+ IntersectEdges(horzEdge, e, Pt);
+ }
+ else
+ {
+ IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
+ IntersectEdges( e, horzEdge, Pt);
+ }
+ TEdge* eNext = GetNextInAEL(e, dir);
+ SwapPositionsInAEL( horzEdge, e );
+ e = eNext;
+ } //end while(e)
+
+ //Break out of loop if HorzEdge.NextInLML is not also horizontal ...
+ if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break;
+
+ UpdateEdgeIntoAEL(horzEdge);
+ if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);
+ GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
+
+ } //end for (;;)
+
+ if (horzEdge->OutIdx >= 0 && !op1)
+ {
+ op1 = GetLastOutPt(horzEdge);
+ TEdge* eNextHorz = m_SortedEdges;
+ while (eNextHorz)
+ {
+ if (eNextHorz->OutIdx >= 0 &&
+ HorzSegmentsOverlap(horzEdge->Bot.X,
+ horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
+ {
+ OutPt* op2 = GetLastOutPt(eNextHorz);
+ AddJoin(op2, op1, eNextHorz->Top);
+ }
+ eNextHorz = eNextHorz->NextInSEL;
+ }
+ AddGhostJoin(op1, horzEdge->Top);
+ }
+
+ if (horzEdge->NextInLML)
+ {
+ if(horzEdge->OutIdx >= 0)
+ {
+ op1 = AddOutPt( horzEdge, horzEdge->Top);
+ UpdateEdgeIntoAEL(horzEdge);
+ if (horzEdge->WindDelta == 0) return;
+ //nb: HorzEdge is no longer horizontal here
+ TEdge* ePrev = horzEdge->PrevInAEL;
+ TEdge* eNext = horzEdge->NextInAEL;
+ if (ePrev && ePrev->Curr.X == horzEdge->Bot.X &&
+ ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 &&
+ (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&
+ SlopesEqual(*horzEdge, *ePrev, m_UseFullRange)))
+ {
+ OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot);
+ AddJoin(op1, op2, horzEdge->Top);
+ }
+ else if (eNext && eNext->Curr.X == horzEdge->Bot.X &&
+ eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 &&
+ eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&
+ SlopesEqual(*horzEdge, *eNext, m_UseFullRange))
+ {
+ OutPt* op2 = AddOutPt(eNext, horzEdge->Bot);
+ AddJoin(op1, op2, horzEdge->Top);
+ }
+ }
+ else
+ UpdateEdgeIntoAEL(horzEdge);
+ }
+ else
+ {
+ if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top);
+ DeleteFromAEL(horzEdge);
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ProcessIntersections(const cInt topY)
+{
+ if( !m_ActiveEdges ) return true;
+ try {
+ BuildIntersectList(topY);
+ size_t IlSize = m_IntersectList.size();
+ if (IlSize == 0) return true;
+ if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList();
+ else return false;
+ }
+ catch(...)
+ {
+ m_SortedEdges = 0;
+ DisposeIntersectNodes();
+ throw clipperException("ProcessIntersections error");
+ }
+ m_SortedEdges = 0;
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeIntersectNodes()
+{
+ for (size_t i = 0; i < m_IntersectList.size(); ++i )
+ delete m_IntersectList[i];
+ m_IntersectList.clear();
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildIntersectList(const cInt topY)
+{
+ if ( !m_ActiveEdges ) return;
+
+ //prepare for sorting ...
+ TEdge* e = m_ActiveEdges;
+ m_SortedEdges = e;
+ while( e )
+ {
+ e->PrevInSEL = e->PrevInAEL;
+ e->NextInSEL = e->NextInAEL;
+ e->Curr.X = TopX( *e, topY );
+ e = e->NextInAEL;
+ }
+
+ //bubblesort ...
+ bool isModified;
+ do
+ {
+ isModified = false;
+ e = m_SortedEdges;
+ while( e->NextInSEL )
+ {
+ TEdge *eNext = e->NextInSEL;
+ IntPoint Pt;
+ if(e->Curr.X > eNext->Curr.X)
+ {
+ IntersectPoint(*e, *eNext, Pt);
+ if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY);
+ IntersectNode * newNode = new IntersectNode;
+ newNode->Edge1 = e;
+ newNode->Edge2 = eNext;
+ newNode->Pt = Pt;
+ m_IntersectList.push_back(newNode);
+
+ SwapPositionsInSEL(e, eNext);
+ isModified = true;
+ }
+ else
+ e = eNext;
+ }
+ if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0;
+ else break;
+ }
+ while ( isModified );
+ m_SortedEdges = 0; //important
+}
+//------------------------------------------------------------------------------
+
+
+void Clipper::ProcessIntersectList()
+{
+ for (size_t i = 0; i < m_IntersectList.size(); ++i)
+ {
+ IntersectNode* iNode = m_IntersectList[i];
+ {
+ IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt);
+ SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 );
+ }
+ delete iNode;
+ }
+ m_IntersectList.clear();
+}
+//------------------------------------------------------------------------------
+
+bool IntersectListSort(IntersectNode* node1, IntersectNode* node2)
+{
+ return node2->Pt.Y < node1->Pt.Y;
+}
+//------------------------------------------------------------------------------
+
+inline bool EdgesAdjacent(const IntersectNode &inode)
+{
+ return (inode.Edge1->NextInSEL == inode.Edge2) ||
+ (inode.Edge1->PrevInSEL == inode.Edge2);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::FixupIntersectionOrder()
+{
+ //pre-condition: intersections are sorted Bottom-most first.
+ //Now it's crucial that intersections are made only between adjacent edges,
+ //so to ensure this the order of intersections may need adjusting ...
+ CopyAELToSEL();
+ std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort);
+ size_t cnt = m_IntersectList.size();
+ for (size_t i = 0; i < cnt; ++i)
+ {
+ if (!EdgesAdjacent(*m_IntersectList[i]))
+ {
+ size_t j = i + 1;
+ while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++;
+ if (j == cnt) return false;
+ std::swap(m_IntersectList[i], m_IntersectList[j]);
+ }
+ SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2);
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DoMaxima(TEdge *e)
+{
+ TEdge* eMaxPair = GetMaximaPairEx(e);
+ if (!eMaxPair)
+ {
+ if (e->OutIdx >= 0)
+ AddOutPt(e, e->Top);
+ DeleteFromAEL(e);
+ return;
+ }
+
+ TEdge* eNext = e->NextInAEL;
+ while(eNext && eNext != eMaxPair)
+ {
+ IntersectEdges(e, eNext, e->Top);
+ SwapPositionsInAEL(e, eNext);
+ eNext = e->NextInAEL;
+ }
+
+ if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned)
+ {
+ DeleteFromAEL(e);
+ DeleteFromAEL(eMaxPair);
+ }
+ else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 )
+ {
+ if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top);
+ DeleteFromAEL(e);
+ DeleteFromAEL(eMaxPair);
+ }
+#ifdef use_lines
+ else if (e->WindDelta == 0)
+ {
+ if (e->OutIdx >= 0)
+ {
+ AddOutPt(e, e->Top);
+ e->OutIdx = Unassigned;
+ }
+ DeleteFromAEL(e);
+
+ if (eMaxPair->OutIdx >= 0)
+ {
+ AddOutPt(eMaxPair, e->Top);
+ eMaxPair->OutIdx = Unassigned;
+ }
+ DeleteFromAEL(eMaxPair);
+ }
+#endif
+ else throw clipperException("DoMaxima error");
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
+{
+ TEdge* e = m_ActiveEdges;
+ while( e )
+ {
+ //1. process maxima, treating them as if they're 'bent' horizontal edges,
+ // but exclude maxima with horizontal edges. nb: e can't be a horizontal.
+ bool IsMaximaEdge = IsMaxima(e, topY);
+
+ if(IsMaximaEdge)
+ {
+ TEdge* eMaxPair = GetMaximaPairEx(e);
+ IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair));
+ }
+
+ if(IsMaximaEdge)
+ {
+ if (m_StrictSimple) m_Maxima.push_back(e->Top.X);
+ TEdge* ePrev = e->PrevInAEL;
+ DoMaxima(e);
+ if( !ePrev ) e = m_ActiveEdges;
+ else e = ePrev->NextInAEL;
+ }
+ else
+ {
+ //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
+ if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML))
+ {
+ UpdateEdgeIntoAEL(e);
+ if (e->OutIdx >= 0)
+ AddOutPt(e, e->Bot);
+ AddEdgeToSEL(e);
+ }
+ else
+ {
+ e->Curr.X = TopX( *e, topY );
+ e->Curr.Y = topY;
+ }
+
+ //When StrictlySimple and 'e' is being touched by another edge, then
+ //make sure both edges have a vertex here ...
+ if (m_StrictSimple)
+ {
+ TEdge* ePrev = e->PrevInAEL;
+ if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
+ (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0))
+ {
+ IntPoint pt = e->Curr;
+#ifdef use_xyz
+ SetZ(pt, *ePrev, *e);
+#endif
+ OutPt* op = AddOutPt(ePrev, pt);
+ OutPt* op2 = AddOutPt(e, pt);
+ AddJoin(op, op2, pt); //StrictlySimple (type-3) join
+ }
+ }
+
+ e = e->NextInAEL;
+ }
+ }
+
+ //3. Process horizontals at the Top of the scanbeam ...
+ m_Maxima.sort();
+ ProcessHorizontals();
+ m_Maxima.clear();
+
+ //4. Promote intermediate vertices ...
+ e = m_ActiveEdges;
+ while(e)
+ {
+ if(IsIntermediate(e, topY))
+ {
+ OutPt* op = 0;
+ if( e->OutIdx >= 0 )
+ op = AddOutPt(e, e->Top);
+ UpdateEdgeIntoAEL(e);
+
+ //if output polygons share an edge, they'll need joining later ...
+ TEdge* ePrev = e->PrevInAEL;
+ TEdge* eNext = e->NextInAEL;
+ if (ePrev && ePrev->Curr.X == e->Bot.X &&
+ ePrev->Curr.Y == e->Bot.Y && op &&
+ ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&
+ SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) &&
+ (e->WindDelta != 0) && (ePrev->WindDelta != 0))
+ {
+ OutPt* op2 = AddOutPt(ePrev, e->Bot);
+ AddJoin(op, op2, e->Top);
+ }
+ else if (eNext && eNext->Curr.X == e->Bot.X &&
+ eNext->Curr.Y == e->Bot.Y && op &&
+ eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&
+ SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) &&
+ (e->WindDelta != 0) && (eNext->WindDelta != 0))
+ {
+ OutPt* op2 = AddOutPt(eNext, e->Bot);
+ AddJoin(op, op2, e->Top);
+ }
+ }
+ e = e->NextInAEL;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupOutPolyline(OutRec &outrec)
+{
+ OutPt *pp = outrec.Pts;
+ OutPt *lastPP = pp->Prev;
+ while (pp != lastPP)
+ {
+ pp = pp->Next;
+ if (pp->Pt == pp->Prev->Pt)
+ {
+ if (pp == lastPP) lastPP = pp->Prev;
+ OutPt *tmpPP = pp->Prev;
+ tmpPP->Next = pp->Next;
+ pp->Next->Prev = tmpPP;
+ delete pp;
+ pp = tmpPP;
+ }
+ }
+
+ if (pp == pp->Prev)
+ {
+ DisposeOutPts(pp);
+ outrec.Pts = 0;
+ return;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupOutPolygon(OutRec &outrec)
+{
+ //FixupOutPolygon() - removes duplicate points and simplifies consecutive
+ //parallel edges by removing the middle vertex.
+ OutPt *lastOK = 0;
+ outrec.BottomPt = 0;
+ OutPt *pp = outrec.Pts;
+ bool preserveCol = m_PreserveCollinear || m_StrictSimple;
+
+ for (;;)
+ {
+ if (pp->Prev == pp || pp->Prev == pp->Next)
+ {
+ DisposeOutPts(pp);
+ outrec.Pts = 0;
+ return;
+ }
+
+ //test for duplicate points and collinear edges ...
+ if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||
+ (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&
+ (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))
+ {
+ lastOK = 0;
+ OutPt *tmp = pp;
+ pp->Prev->Next = pp->Next;
+ pp->Next->Prev = pp->Prev;
+ pp = pp->Prev;
+ delete tmp;
+ }
+ else if (pp == lastOK) break;
+ else
+ {
+ if (!lastOK) lastOK = pp;
+ pp = pp->Next;
+ }
+ }
+ outrec.Pts = pp;
+}
+//------------------------------------------------------------------------------
+
+int PointCount(OutPt *Pts)
+{
+ if (!Pts) return 0;
+ int result = 0;
+ OutPt* p = Pts;
+ do
+ {
+ result++;
+ p = p->Next;
+ }
+ while (p != Pts);
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult(Paths &polys)
+{
+ polys.reserve(m_PolyOuts.size());
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ if (!m_PolyOuts[i]->Pts) continue;
+ Path pg;
+ OutPt* p = m_PolyOuts[i]->Pts->Prev;
+ int cnt = PointCount(p);
+ if (cnt < 2) continue;
+ pg.reserve(cnt);
+ for (int i = 0; i < cnt; ++i)
+ {
+ pg.push_back(p->Pt);
+ p = p->Prev;
+ }
+ polys.push_back(pg);
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult2(PolyTree& polytree)
+{
+ polytree.Clear();
+ polytree.AllNodes.reserve(m_PolyOuts.size());
+ //add each output polygon/contour to polytree ...
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ int cnt = PointCount(outRec->Pts);
+ if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue;
+ FixHoleLinkage(*outRec);
+ PolyNode* pn = new PolyNode();
+ //nb: polytree takes ownership of all the PolyNodes
+ polytree.AllNodes.push_back(pn);
+ outRec->PolyNd = pn;
+ pn->Parent = 0;
+ pn->Index = 0;
+ pn->Contour.reserve(cnt);
+ OutPt *op = outRec->Pts->Prev;
+ for (int j = 0; j < cnt; j++)
+ {
+ pn->Contour.push_back(op->Pt);
+ op = op->Prev;
+ }
+ }
+
+ //fixup PolyNode links etc ...
+ polytree.Childs.reserve(m_PolyOuts.size());
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ if (!outRec->PolyNd) continue;
+ if (outRec->IsOpen)
+ {
+ outRec->PolyNd->m_IsOpen = true;
+ polytree.AddChild(*outRec->PolyNd);
+ }
+ else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd)
+ outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd);
+ else
+ polytree.AddChild(*outRec->PolyNd);
+ }
+}
+//------------------------------------------------------------------------------
+
+void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2)
+{
+ //just swap the contents (because fIntersectNodes is a single-linked-list)
+ IntersectNode inode = int1; //gets a copy of Int1
+ int1.Edge1 = int2.Edge1;
+ int1.Edge2 = int2.Edge2;
+ int1.Pt = int2.Pt;
+ int2.Edge1 = inode.Edge1;
+ int2.Edge2 = inode.Edge2;
+ int2.Pt = inode.Pt;
+}
+//------------------------------------------------------------------------------
+
+inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2)
+{
+ if (e2.Curr.X == e1.Curr.X)
+ {
+ if (e2.Top.Y > e1.Top.Y)
+ return e2.Top.X < TopX(e1, e2.Top.Y);
+ else return e1.Top.X > TopX(e2, e1.Top.Y);
+ }
+ else return e2.Curr.X < e1.Curr.X;
+}
+//------------------------------------------------------------------------------
+
+bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2,
+ cInt& Left, cInt& Right)
+{
+ if (a1 < a2)
+ {
+ if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);}
+ else {Left = std::max(a1,b2); Right = std::min(a2,b1);}
+ }
+ else
+ {
+ if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);}
+ else {Left = std::max(a2,b2); Right = std::min(a1,b1);}
+ }
+ return Left < Right;
+}
+//------------------------------------------------------------------------------
+
+inline void UpdateOutPtIdxs(OutRec& outrec)
+{
+ OutPt* op = outrec.Pts;
+ do
+ {
+ op->Idx = outrec.Idx;
+ op = op->Prev;
+ }
+ while(op != outrec.Pts);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge)
+{
+ if(!m_ActiveEdges)
+ {
+ edge->PrevInAEL = 0;
+ edge->NextInAEL = 0;
+ m_ActiveEdges = edge;
+ }
+ else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge))
+ {
+ edge->PrevInAEL = 0;
+ edge->NextInAEL = m_ActiveEdges;
+ m_ActiveEdges->PrevInAEL = edge;
+ m_ActiveEdges = edge;
+ }
+ else
+ {
+ if(!startEdge) startEdge = m_ActiveEdges;
+ while(startEdge->NextInAEL &&
+ !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge))
+ startEdge = startEdge->NextInAEL;
+ edge->NextInAEL = startEdge->NextInAEL;
+ if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge;
+ edge->PrevInAEL = startEdge;
+ startEdge->NextInAEL = edge;
+ }
+}
+//----------------------------------------------------------------------
+
+OutPt* DupOutPt(OutPt* outPt, bool InsertAfter)
+{
+ OutPt* result = new OutPt;
+ result->Pt = outPt->Pt;
+ result->Idx = outPt->Idx;
+ if (InsertAfter)
+ {
+ result->Next = outPt->Next;
+ result->Prev = outPt;
+ outPt->Next->Prev = result;
+ outPt->Next = result;
+ }
+ else
+ {
+ result->Prev = outPt->Prev;
+ result->Next = outPt;
+ outPt->Prev->Next = result;
+ outPt->Prev = result;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
+ const IntPoint Pt, bool DiscardLeft)
+{
+ Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight);
+ Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight);
+ if (Dir1 == Dir2) return false;
+
+ //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we
+ //want Op1b to be on the Right. (And likewise with Op2 and Op2b.)
+ //So, to facilitate this while inserting Op1b and Op2b ...
+ //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,
+ //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)
+ if (Dir1 == dLeftToRight)
+ {
+ while (op1->Next->Pt.X <= Pt.X &&
+ op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)
+ op1 = op1->Next;
+ if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
+ op1b = DupOutPt(op1, !DiscardLeft);
+ if (op1b->Pt != Pt)
+ {
+ op1 = op1b;
+ op1->Pt = Pt;
+ op1b = DupOutPt(op1, !DiscardLeft);
+ }
+ }
+ else
+ {
+ while (op1->Next->Pt.X >= Pt.X &&
+ op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)
+ op1 = op1->Next;
+ if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
+ op1b = DupOutPt(op1, DiscardLeft);
+ if (op1b->Pt != Pt)
+ {
+ op1 = op1b;
+ op1->Pt = Pt;
+ op1b = DupOutPt(op1, DiscardLeft);
+ }
+ }
+
+ if (Dir2 == dLeftToRight)
+ {
+ while (op2->Next->Pt.X <= Pt.X &&
+ op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)
+ op2 = op2->Next;
+ if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
+ op2b = DupOutPt(op2, !DiscardLeft);
+ if (op2b->Pt != Pt)
+ {
+ op2 = op2b;
+ op2->Pt = Pt;
+ op2b = DupOutPt(op2, !DiscardLeft);
+ };
+ } else
+ {
+ while (op2->Next->Pt.X >= Pt.X &&
+ op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)
+ op2 = op2->Next;
+ if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
+ op2b = DupOutPt(op2, DiscardLeft);
+ if (op2b->Pt != Pt)
+ {
+ op2 = op2b;
+ op2->Pt = Pt;
+ op2b = DupOutPt(op2, DiscardLeft);
+ };
+ };
+
+ if ((Dir1 == dLeftToRight) == DiscardLeft)
+ {
+ op1->Prev = op2;
+ op2->Next = op1;
+ op1b->Next = op2b;
+ op2b->Prev = op1b;
+ }
+ else
+ {
+ op1->Next = op2;
+ op2->Prev = op1;
+ op1b->Prev = op2b;
+ op2b->Next = op1b;
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
+{
+ OutPt *op1 = j->OutPt1, *op1b;
+ OutPt *op2 = j->OutPt2, *op2b;
+
+ //There are 3 kinds of joins for output polygons ...
+ //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
+ //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
+ //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
+ //location at the Bottom of the overlapping segment (& Join.OffPt is above).
+ //3. StrictSimple joins where edges touch but are not collinear and where
+ //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
+ bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y);
+
+ if (isHorizontal && (j->OffPt == j->OutPt1->Pt) &&
+ (j->OffPt == j->OutPt2->Pt))
+ {
+ //Strictly Simple join ...
+ if (outRec1 != outRec2) return false;
+ op1b = j->OutPt1->Next;
+ while (op1b != op1 && (op1b->Pt == j->OffPt))
+ op1b = op1b->Next;
+ bool reverse1 = (op1b->Pt.Y > j->OffPt.Y);
+ op2b = j->OutPt2->Next;
+ while (op2b != op2 && (op2b->Pt == j->OffPt))
+ op2b = op2b->Next;
+ bool reverse2 = (op2b->Pt.Y > j->OffPt.Y);
+ if (reverse1 == reverse2) return false;
+ if (reverse1)
+ {
+ op1b = DupOutPt(op1, false);
+ op2b = DupOutPt(op2, true);
+ op1->Prev = op2;
+ op2->Next = op1;
+ op1b->Next = op2b;
+ op2b->Prev = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ } else
+ {
+ op1b = DupOutPt(op1, true);
+ op2b = DupOutPt(op2, false);
+ op1->Next = op2;
+ op2->Prev = op1;
+ op1b->Prev = op2b;
+ op2b->Next = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ }
+ }
+ else if (isHorizontal)
+ {
+ //treat horizontal joins differently to non-horizontal joins since with
+ //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt
+ //may be anywhere along the horizontal edge.
+ op1b = op1;
+ while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2)
+ op1 = op1->Prev;
+ while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2)
+ op1b = op1b->Next;
+ if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon'
+
+ op2b = op2;
+ while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b)
+ op2 = op2->Prev;
+ while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1)
+ op2b = op2b->Next;
+ if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon'
+
+ cInt Left, Right;
+ //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges
+ if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right))
+ return false;
+
+ //DiscardLeftSide: when overlapping edges are joined, a spike will created
+ //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
+ //on the discard Side as either may still be needed for other joins ...
+ IntPoint Pt;
+ bool DiscardLeftSide;
+ if (op1->Pt.X >= Left && op1->Pt.X <= Right)
+ {
+ Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X);
+ }
+ else if (op2->Pt.X >= Left&& op2->Pt.X <= Right)
+ {
+ Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X);
+ }
+ else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right)
+ {
+ Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X;
+ }
+ else
+ {
+ Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X);
+ }
+ j->OutPt1 = op1; j->OutPt2 = op2;
+ return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide);
+ } else
+ {
+ //nb: For non-horizontal joins ...
+ // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y
+ // 2. Jr.OutPt1.Pt > Jr.OffPt.Y
+
+ //make sure the polygons are correctly oriented ...
+ op1b = op1->Next;
+ while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next;
+ bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) ||
+ !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange));
+ if (Reverse1)
+ {
+ op1b = op1->Prev;
+ while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev;
+ if ((op1b->Pt.Y > op1->Pt.Y) ||
+ !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false;
+ };
+ op2b = op2->Next;
+ while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next;
+ bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) ||
+ !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange));
+ if (Reverse2)
+ {
+ op2b = op2->Prev;
+ while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev;
+ if ((op2b->Pt.Y > op2->Pt.Y) ||
+ !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false;
+ }
+
+ if ((op1b == op1) || (op2b == op2) || (op1b == op2b) ||
+ ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false;
+
+ if (Reverse1)
+ {
+ op1b = DupOutPt(op1, false);
+ op2b = DupOutPt(op2, true);
+ op1->Prev = op2;
+ op2->Next = op1;
+ op1b->Next = op2b;
+ op2b->Prev = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ } else
+ {
+ op1b = DupOutPt(op1, true);
+ op2b = DupOutPt(op2, false);
+ op1->Next = op2;
+ op2->Prev = op1;
+ op1b->Prev = op2b;
+ op2b->Next = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ }
+ }
+}
+//----------------------------------------------------------------------
+
+static OutRec* ParseFirstLeft(OutRec* FirstLeft)
+{
+ while (FirstLeft && !FirstLeft->Pts)
+ FirstLeft = FirstLeft->FirstLeft;
+ return FirstLeft;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)
+{
+ //tests if NewOutRec contains the polygon before reassigning FirstLeft
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);
+ if (outRec->Pts && firstLeft == OldOutRec)
+ {
+ if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts))
+ outRec->FirstLeft = NewOutRec;
+ }
+ }
+}
+//----------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec)
+{
+ //A polygon has split into two such that one is now the inner of the other.
+ //It's possible that these polygons now wrap around other polygons, so check
+ //every polygon that's also contained by OuterOutRec's FirstLeft container
+ //(including 0) to see if they've become inner to the new inner polygon ...
+ OutRec* orfl = OuterOutRec->FirstLeft;
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+
+ if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec)
+ continue;
+ OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);
+ if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec)
+ continue;
+ if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts))
+ outRec->FirstLeft = InnerOutRec;
+ else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts))
+ outRec->FirstLeft = OuterOutRec;
+ else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec)
+ outRec->FirstLeft = orfl;
+ }
+}
+//----------------------------------------------------------------------
+void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec)
+{
+ //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);
+ if (outRec->Pts && outRec->FirstLeft == OldOutRec)
+ outRec->FirstLeft = NewOutRec;
+ }
+}
+//----------------------------------------------------------------------
+
+void Clipper::JoinCommonEdges()
+{
+ for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+ {
+ Join* join = m_Joins[i];
+
+ OutRec *outRec1 = GetOutRec(join->OutPt1->Idx);
+ OutRec *outRec2 = GetOutRec(join->OutPt2->Idx);
+
+ if (!outRec1->Pts || !outRec2->Pts) continue;
+ if (outRec1->IsOpen || outRec2->IsOpen) continue;
+
+ //get the polygon fragment with the correct hole state (FirstLeft)
+ //before calling JoinPoints() ...
+ OutRec *holeStateRec;
+ if (outRec1 == outRec2) holeStateRec = outRec1;
+ else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2;
+ else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1;
+ else holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+ if (!JoinPoints(join, outRec1, outRec2)) continue;
+
+ if (outRec1 == outRec2)
+ {
+ //instead of joining two polygons, we've just created a new one by
+ //splitting one polygon into two.
+ outRec1->Pts = join->OutPt1;
+ outRec1->BottomPt = 0;
+ outRec2 = CreateOutRec();
+ outRec2->Pts = join->OutPt2;
+
+ //update all OutRec2.Pts Idx's ...
+ UpdateOutPtIdxs(*outRec2);
+
+ if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts))
+ {
+ //outRec1 contains outRec2 ...
+ outRec2->IsHole = !outRec1->IsHole;
+ outRec2->FirstLeft = outRec1;
+
+ if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+
+ if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0))
+ ReversePolyPtLinks(outRec2->Pts);
+
+ } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts))
+ {
+ //outRec2 contains outRec1 ...
+ outRec2->IsHole = outRec1->IsHole;
+ outRec1->IsHole = !outRec2->IsHole;
+ outRec2->FirstLeft = outRec1->FirstLeft;
+ outRec1->FirstLeft = outRec2;
+
+ if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);
+
+ if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0))
+ ReversePolyPtLinks(outRec1->Pts);
+ }
+ else
+ {
+ //the 2 polygons are completely separate ...
+ outRec2->IsHole = outRec1->IsHole;
+ outRec2->FirstLeft = outRec1->FirstLeft;
+
+ //fixup FirstLeft pointers that may need reassigning to OutRec2
+ if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);
+ }
+
+ } else
+ {
+ //joined 2 polygons together ...
+
+ outRec2->Pts = 0;
+ outRec2->BottomPt = 0;
+ outRec2->Idx = outRec1->Idx;
+
+ outRec1->IsHole = holeStateRec->IsHole;
+ if (holeStateRec == outRec2)
+ outRec1->FirstLeft = outRec2->FirstLeft;
+ outRec2->FirstLeft = outRec1;
+
+ if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// ClipperOffset support functions ...
+//------------------------------------------------------------------------------
+
+DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)
+{
+ if(pt2.X == pt1.X && pt2.Y == pt1.Y)
+ return DoublePoint(0, 0);
+
+ double Dx = (double)(pt2.X - pt1.X);
+ double dy = (double)(pt2.Y - pt1.Y);
+ double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy );
+ Dx *= f;
+ dy *= f;
+ return DoublePoint(dy, -Dx);
+}
+
+//------------------------------------------------------------------------------
+// ClipperOffset class
+//------------------------------------------------------------------------------
+
+ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance)
+{
+ this->MiterLimit = miterLimit;
+ this->ArcTolerance = arcTolerance;
+ m_lowest.X = -1;
+
+ //Avoid uninitialized vars:
+ m_delta = m_sinA = m_sin = m_cos = 0;
+ m_miterLim = m_StepsPerRad = 0;
+
+}
+//------------------------------------------------------------------------------
+
+ClipperOffset::~ClipperOffset()
+{
+ Clear();
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Clear()
+{
+ for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+ delete m_polyNodes.Childs[i];
+ m_polyNodes.Childs.clear();
+ m_lowest.X = -1;
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType)
+{
+ int highI = (int)path.size() - 1;
+ if (highI < 0) return;
+ PolyNode* newNode = new PolyNode();
+ newNode->m_jointype = joinType;
+ newNode->m_endtype = endType;
+
+ //strip duplicate points from path and also get index to the lowest point ...
+ if (endType == etClosedLine || endType == etClosedPolygon)
+ while (highI > 0 && path[0] == path[highI]) highI--;
+ newNode->Contour.reserve(highI + 1);
+ newNode->Contour.push_back(path[0]);
+ int j = 0, k = 0;
+ for (int i = 1; i <= highI; i++)
+ if (newNode->Contour[j] != path[i])
+ {
+ j++;
+ newNode->Contour.push_back(path[i]);
+ if (path[i].Y > newNode->Contour[k].Y ||
+ (path[i].Y == newNode->Contour[k].Y &&
+ path[i].X < newNode->Contour[k].X)) k = j;
+ }
+ if (endType == etClosedPolygon && j < 2)
+ {
+ delete newNode;
+ return;
+ }
+ m_polyNodes.AddChild(*newNode);
+
+ //if this path's lowest pt is lower than all the others then update m_lowest
+ if (endType != etClosedPolygon) return;
+ if (m_lowest.X < 0)
+ m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);
+ else
+ {
+ IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y];
+ if (newNode->Contour[k].Y > ip.Y ||
+ (newNode->Contour[k].Y == ip.Y &&
+ newNode->Contour[k].X < ip.X))
+ m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType)
+{
+ for (Paths::size_type i = 0; i < paths.size(); ++i)
+ AddPath(paths[i], joinType, endType);
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::FixOrientations()
+{
+ //fixup orientations of all closed paths if the orientation of the
+ //closed path with the lowermost vertex is wrong ...
+ if (m_lowest.X >= 0 &&
+ !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour))
+ {
+ for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ if (node.m_endtype == etClosedPolygon ||
+ (node.m_endtype == etClosedLine && Orientation(node.Contour)))
+ ReversePath(node.Contour);
+ }
+ } else
+ {
+ for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ if (node.m_endtype == etClosedLine && !Orientation(node.Contour))
+ ReversePath(node.Contour);
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Execute(Paths& solution, double delta)
+{
+ solution.clear();
+ FixOrientations();
+ DoOffset(delta);
+
+ //now clean up 'corners' ...
+ Clipper clpr;
+ clpr.AddPaths(m_destPolys, ptSubject, true);
+ if (delta > 0)
+ {
+ clpr.Execute(ctUnion, solution, pftPositive, pftPositive);
+ }
+ else
+ {
+ IntRect r = clpr.GetBounds();
+ Path outer(4);
+ outer[0] = IntPoint(r.left - 10, r.bottom + 10);
+ outer[1] = IntPoint(r.right + 10, r.bottom + 10);
+ outer[2] = IntPoint(r.right + 10, r.top - 10);
+ outer[3] = IntPoint(r.left - 10, r.top - 10);
+
+ clpr.AddPath(outer, ptSubject, true);
+ clpr.ReverseSolution(true);
+ clpr.Execute(ctUnion, solution, pftNegative, pftNegative);
+ if (solution.size() > 0) solution.erase(solution.begin());
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Execute(PolyTree& solution, double delta)
+{
+ solution.Clear();
+ FixOrientations();
+ DoOffset(delta);
+
+ //now clean up 'corners' ...
+ Clipper clpr;
+ clpr.AddPaths(m_destPolys, ptSubject, true);
+ if (delta > 0)
+ {
+ clpr.Execute(ctUnion, solution, pftPositive, pftPositive);
+ }
+ else
+ {
+ IntRect r = clpr.GetBounds();
+ Path outer(4);
+ outer[0] = IntPoint(r.left - 10, r.bottom + 10);
+ outer[1] = IntPoint(r.right + 10, r.bottom + 10);
+ outer[2] = IntPoint(r.right + 10, r.top - 10);
+ outer[3] = IntPoint(r.left - 10, r.top - 10);
+
+ clpr.AddPath(outer, ptSubject, true);
+ clpr.ReverseSolution(true);
+ clpr.Execute(ctUnion, solution, pftNegative, pftNegative);
+ //remove the outer PolyNode rectangle ...
+ if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0)
+ {
+ PolyNode* outerNode = solution.Childs[0];
+ solution.Childs.reserve(outerNode->ChildCount());
+ solution.Childs[0] = outerNode->Childs[0];
+ solution.Childs[0]->Parent = outerNode->Parent;
+ for (int i = 1; i < outerNode->ChildCount(); ++i)
+ solution.AddChild(*outerNode->Childs[i]);
+ }
+ else
+ solution.Clear();
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoOffset(double delta)
+{
+ m_destPolys.clear();
+ m_delta = delta;
+
+ //if Zero offset, just copy any CLOSED polygons to m_p and return ...
+ if (NEAR_ZERO(delta))
+ {
+ m_destPolys.reserve(m_polyNodes.ChildCount());
+ for (int i = 0; i < m_polyNodes.ChildCount(); i++)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ if (node.m_endtype == etClosedPolygon)
+ m_destPolys.push_back(node.Contour);
+ }
+ return;
+ }
+
+ //see offset_triginometry3.svg in the documentation folder ...
+ if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit);
+ else m_miterLim = 0.5;
+
+ double y;
+ if (ArcTolerance <= 0.0) y = def_arc_tolerance;
+ else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance)
+ y = std::fabs(delta) * def_arc_tolerance;
+ else y = ArcTolerance;
+ //see offset_triginometry2.svg in the documentation folder ...
+ double steps = pi / std::acos(1 - y / std::fabs(delta));
+ if (steps > std::fabs(delta) * pi)
+ steps = std::fabs(delta) * pi; //ie excessive precision check
+ m_sin = std::sin(two_pi / steps);
+ m_cos = std::cos(two_pi / steps);
+ m_StepsPerRad = steps / two_pi;
+ if (delta < 0.0) m_sin = -m_sin;
+
+ m_destPolys.reserve(m_polyNodes.ChildCount() * 2);
+ for (int i = 0; i < m_polyNodes.ChildCount(); i++)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ m_srcPoly = node.Contour;
+
+ int len = (int)m_srcPoly.size();
+ if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon)))
+ continue;
+
+ m_destPoly.clear();
+ if (len == 1)
+ {
+ if (node.m_jointype == jtRound)
+ {
+ double X = 1.0, Y = 0.0;
+ for (cInt j = 1; j <= steps; j++)
+ {
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[0].X + X * delta),
+ Round(m_srcPoly[0].Y + Y * delta)));
+ double X2 = X;
+ X = X * m_cos - m_sin * Y;
+ Y = X2 * m_sin + Y * m_cos;
+ }
+ }
+ else
+ {
+ double X = -1.0, Y = -1.0;
+ for (int j = 0; j < 4; ++j)
+ {
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[0].X + X * delta),
+ Round(m_srcPoly[0].Y + Y * delta)));
+ if (X < 0) X = 1;
+ else if (Y < 0) Y = 1;
+ else X = -1;
+ }
+ }
+ m_destPolys.push_back(m_destPoly);
+ continue;
+ }
+ //build m_normals ...
+ m_normals.clear();
+ m_normals.reserve(len);
+ for (int j = 0; j < len - 1; ++j)
+ m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
+ if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon)
+ m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
+ else
+ m_normals.push_back(DoublePoint(m_normals[len - 2]));
+
+ if (node.m_endtype == etClosedPolygon)
+ {
+ int k = len - 1;
+ for (int j = 0; j < len; ++j)
+ OffsetPoint(j, k, node.m_jointype);
+ m_destPolys.push_back(m_destPoly);
+ }
+ else if (node.m_endtype == etClosedLine)
+ {
+ int k = len - 1;
+ for (int j = 0; j < len; ++j)
+ OffsetPoint(j, k, node.m_jointype);
+ m_destPolys.push_back(m_destPoly);
+ m_destPoly.clear();
+ //re-build m_normals ...
+ DoublePoint n = m_normals[len -1];
+ for (int j = len - 1; j > 0; j--)
+ m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+ m_normals[0] = DoublePoint(-n.X, -n.Y);
+ k = 0;
+ for (int j = len - 1; j >= 0; j--)
+ OffsetPoint(j, k, node.m_jointype);
+ m_destPolys.push_back(m_destPoly);
+ }
+ else
+ {
+ int k = 0;
+ for (int j = 1; j < len - 1; ++j)
+ OffsetPoint(j, k, node.m_jointype);
+
+ IntPoint pt1;
+ if (node.m_endtype == etOpenButt)
+ {
+ int j = len - 1;
+ pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X *
+ delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta));
+ m_destPoly.push_back(pt1);
+ pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X *
+ delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta));
+ m_destPoly.push_back(pt1);
+ }
+ else
+ {
+ int j = len - 1;
+ k = len - 2;
+ m_sinA = 0;
+ m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y);
+ if (node.m_endtype == etOpenSquare)
+ DoSquare(j, k);
+ else
+ DoRound(j, k);
+ }
+
+ //re-build m_normals ...
+ for (int j = len - 1; j > 0; j--)
+ m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+ m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y);
+
+ k = len - 1;
+ for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype);
+
+ if (node.m_endtype == etOpenButt)
+ {
+ pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta),
+ (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta));
+ m_destPoly.push_back(pt1);
+ pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta),
+ (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta));
+ m_destPoly.push_back(pt1);
+ }
+ else
+ {
+ k = 1;
+ m_sinA = 0;
+ if (node.m_endtype == etOpenSquare)
+ DoSquare(0, 1);
+ else
+ DoRound(0, 1);
+ }
+ m_destPolys.push_back(m_destPoly);
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
+{
+ //cross product ...
+ m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);
+ if (std::fabs(m_sinA * m_delta) < 1.0)
+ {
+ //dot product ...
+ double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y );
+ if (cosA > 0) // angle => 0 degrees
+ {
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+ return;
+ }
+ //else angle => 180 degrees
+ }
+ else if (m_sinA > 1.0) m_sinA = 1.0;
+ else if (m_sinA < -1.0) m_sinA = -1.0;
+
+ if (m_sinA * m_delta < 0)
+ {
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+ m_destPoly.push_back(m_srcPoly[j]);
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+ }
+ else
+ switch (jointype)
+ {
+ case jtMiter:
+ {
+ double r = 1 + (m_normals[j].X * m_normals[k].X +
+ m_normals[j].Y * m_normals[k].Y);
+ if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k);
+ break;
+ }
+ case jtSquare: DoSquare(j, k); break;
+ case jtRound: DoRound(j, k); break;
+ }
+ k = j;
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoSquare(int j, int k)
+{
+ double dx = std::tan(std::atan2(m_sinA,
+ m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4);
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)),
+ Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx))));
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)),
+ Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx))));
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoMiter(int j, int k, double r)
+{
+ double q = m_delta / r;
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q),
+ Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q)));
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoRound(int j, int k)
+{
+ double a = std::atan2(m_sinA,
+ m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);
+ int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1);
+
+ double X = m_normals[k].X, Y = m_normals[k].Y, X2;
+ for (int i = 0; i < steps; ++i)
+ {
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + X * m_delta),
+ Round(m_srcPoly[j].Y + Y * m_delta)));
+ X2 = X;
+ X = X * m_cos - m_sin * Y;
+ Y = X2 * m_sin + Y * m_cos;
+ }
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+}
+
+//------------------------------------------------------------------------------
+// Miscellaneous public functions
+//------------------------------------------------------------------------------
+
+void Clipper::DoSimplePolygons()
+{
+ PolyOutList::size_type i = 0;
+ while (i < m_PolyOuts.size())
+ {
+ OutRec* outrec = m_PolyOuts[i++];
+ OutPt* op = outrec->Pts;
+ if (!op || outrec->IsOpen) continue;
+ do //for each Pt in Polygon until duplicate found do ...
+ {
+ OutPt* op2 = op->Next;
+ while (op2 != outrec->Pts)
+ {
+ if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op)
+ {
+ //split the polygon into two ...
+ OutPt* op3 = op->Prev;
+ OutPt* op4 = op2->Prev;
+ op->Prev = op4;
+ op4->Next = op;
+ op2->Prev = op3;
+ op3->Next = op2;
+
+ outrec->Pts = op;
+ OutRec* outrec2 = CreateOutRec();
+ outrec2->Pts = op2;
+ UpdateOutPtIdxs(*outrec2);
+ if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))
+ {
+ //OutRec2 is contained by OutRec1 ...
+ outrec2->IsHole = !outrec->IsHole;
+ outrec2->FirstLeft = outrec;
+ if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
+ }
+ else
+ if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))
+ {
+ //OutRec1 is contained by OutRec2 ...
+ outrec2->IsHole = outrec->IsHole;
+ outrec->IsHole = !outrec2->IsHole;
+ outrec2->FirstLeft = outrec->FirstLeft;
+ outrec->FirstLeft = outrec2;
+ if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
+ }
+ else
+ {
+ //the 2 polygons are separate ...
+ outrec2->IsHole = outrec->IsHole;
+ outrec2->FirstLeft = outrec->FirstLeft;
+ if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
+ }
+ op2 = op; //ie get ready for the Next iteration
+ }
+ op2 = op2->Next;
+ }
+ op = op->Next;
+ }
+ while (op != outrec->Pts);
+ }
+}
+//------------------------------------------------------------------------------
+
+void ReversePath(Path& p)
+{
+ std::reverse(p.begin(), p.end());
+}
+//------------------------------------------------------------------------------
+
+void ReversePaths(Paths& p)
+{
+ for (Paths::size_type i = 0; i < p.size(); ++i)
+ ReversePath(p[i]);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType)
+{
+ Clipper c;
+ c.StrictlySimple(true);
+ c.AddPath(in_poly, ptSubject, true);
+ c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType)
+{
+ Clipper c;
+ c.StrictlySimple(true);
+ c.AddPaths(in_polys, ptSubject, true);
+ c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(Paths &polys, PolyFillType fillType)
+{
+ SimplifyPolygons(polys, polys, fillType);
+}
+//------------------------------------------------------------------------------
+
+inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2)
+{
+ double Dx = ((double)pt1.X - pt2.X);
+ double dy = ((double)pt1.Y - pt2.Y);
+ return (Dx*Dx + dy*dy);
+}
+//------------------------------------------------------------------------------
+
+double DistanceFromLineSqrd(
+ const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2)
+{
+ //The equation of a line in general form (Ax + By + C = 0)
+ //given 2 points (x¹,y¹) & (x²,y²) is ...
+ //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0
+ //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹
+ //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
+ //see http://en.wikipedia.org/wiki/Perpendicular_distance
+ double A = double(ln1.Y - ln2.Y);
+ double B = double(ln2.X - ln1.X);
+ double C = A * ln1.X + B * ln1.Y;
+ C = A * pt.X + B * pt.Y - C;
+ return (C * C) / (A * A + B * B);
+}
+//---------------------------------------------------------------------------
+
+bool SlopesNearCollinear(const IntPoint& pt1,
+ const IntPoint& pt2, const IntPoint& pt3, double distSqrd)
+{
+ //this function is more accurate when the point that's geometrically
+ //between the other 2 points is the one that's tested for distance.
+ //ie makes it more likely to pick up 'spikes' ...
+ if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y))
+ {
+ if ((pt1.X > pt2.X) == (pt1.X < pt3.X))
+ return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+ else if ((pt2.X > pt1.X) == (pt2.X < pt3.X))
+ return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+ else
+ return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+ }
+ else
+ {
+ if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y))
+ return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+ else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y))
+ return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+ else
+ return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
+{
+ double Dx = (double)pt1.X - pt2.X;
+ double dy = (double)pt1.Y - pt2.Y;
+ return ((Dx * Dx) + (dy * dy) <= distSqrd);
+}
+//------------------------------------------------------------------------------
+
+OutPt* ExcludeOp(OutPt* op)
+{
+ OutPt* result = op->Prev;
+ result->Next = op->Next;
+ op->Next->Prev = result;
+ result->Idx = 0;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)
+{
+ //distance = proximity in units/pixels below which vertices
+ //will be stripped. Default ~= sqrt(2).
+
+ size_t size = in_poly.size();
+
+ if (size == 0)
+ {
+ out_poly.clear();
+ return;
+ }
+
+ OutPt* outPts = new OutPt[size];
+ for (size_t i = 0; i < size; ++i)
+ {
+ outPts[i].Pt = in_poly[i];
+ outPts[i].Next = &outPts[(i + 1) % size];
+ outPts[i].Next->Prev = &outPts[i];
+ outPts[i].Idx = 0;
+ }
+
+ double distSqrd = distance * distance;
+ OutPt* op = &outPts[0];
+ while (op->Idx == 0 && op->Next != op->Prev)
+ {
+ if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd))
+ {
+ op = ExcludeOp(op);
+ size--;
+ }
+ else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd))
+ {
+ ExcludeOp(op->Next);
+ op = ExcludeOp(op);
+ size -= 2;
+ }
+ else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd))
+ {
+ op = ExcludeOp(op);
+ size--;
+ }
+ else
+ {
+ op->Idx = 1;
+ op = op->Next;
+ }
+ }
+
+ if (size < 3) size = 0;
+ out_poly.resize(size);
+ for (size_t i = 0; i < size; ++i)
+ {
+ out_poly[i] = op->Pt;
+ op = op->Next;
+ }
+ delete [] outPts;
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygon(Path& poly, double distance)
+{
+ CleanPolygon(poly, poly, distance);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance)
+{
+ out_polys.resize(in_polys.size());
+ for (Paths::size_type i = 0; i < in_polys.size(); ++i)
+ CleanPolygon(in_polys[i], out_polys[i], distance);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygons(Paths& polys, double distance)
+{
+ CleanPolygons(polys, polys, distance);
+}
+//------------------------------------------------------------------------------
+
+void Minkowski(const Path& poly, const Path& path,
+ Paths& solution, bool isSum, bool isClosed)
+{
+ int delta = (isClosed ? 1 : 0);
+ size_t polyCnt = poly.size();
+ size_t pathCnt = path.size();
+ Paths pp;
+ pp.reserve(pathCnt);
+ if (isSum)
+ for (size_t i = 0; i < pathCnt; ++i)
+ {
+ Path p;
+ p.reserve(polyCnt);
+ for (size_t j = 0; j < poly.size(); ++j)
+ p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y));
+ pp.push_back(p);
+ }
+ else
+ for (size_t i = 0; i < pathCnt; ++i)
+ {
+ Path p;
+ p.reserve(polyCnt);
+ for (size_t j = 0; j < poly.size(); ++j)
+ p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y));
+ pp.push_back(p);
+ }
+
+ solution.clear();
+ solution.reserve((pathCnt + delta) * (polyCnt + 1));
+ for (size_t i = 0; i < pathCnt - 1 + delta; ++i)
+ for (size_t j = 0; j < polyCnt; ++j)
+ {
+ Path quad;
+ quad.reserve(4);
+ quad.push_back(pp[i % pathCnt][j % polyCnt]);
+ quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]);
+ quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
+ quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]);
+ if (!Orientation(quad)) ReversePath(quad);
+ solution.push_back(quad);
+ }
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed)
+{
+ Minkowski(pattern, path, solution, true, pathIsClosed);
+ Clipper c;
+ c.AddPaths(solution, ptSubject, true);
+ c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+void TranslatePath(const Path& input, Path& output, const IntPoint delta)
+{
+ //precondition: input != output
+ output.resize(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y);
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed)
+{
+ Clipper c;
+ for (size_t i = 0; i < paths.size(); ++i)
+ {
+ Paths tmp;
+ Minkowski(pattern, paths[i], tmp, true, pathIsClosed);
+ c.AddPaths(tmp, ptSubject, true);
+ if (pathIsClosed)
+ {
+ Path tmp2;
+ TranslatePath(paths[i], tmp2, pattern[0]);
+ c.AddPath(tmp2, ptClip, true);
+ }
+ }
+ c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution)
+{
+ Minkowski(poly1, poly2, solution, false, true);
+ Clipper c;
+ c.AddPaths(solution, ptSubject, true);
+ c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+enum NodeType {ntAny, ntOpen, ntClosed};
+
+void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths)
+{
+ bool match = true;
+ if (nodetype == ntClosed) match = !polynode.IsOpen();
+ else if (nodetype == ntOpen) return;
+
+ if (!polynode.Contour.empty() && match)
+ paths.push_back(polynode.Contour);
+ for (int i = 0; i < polynode.ChildCount(); ++i)
+ AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths);
+}
+//------------------------------------------------------------------------------
+
+void PolyTreeToPaths(const PolyTree& polytree, Paths& paths)
+{
+ paths.resize(0);
+ paths.reserve(polytree.Total());
+ AddPolyNodeToPaths(polytree, ntAny, paths);
+}
+//------------------------------------------------------------------------------
+
+void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths)
+{
+ paths.resize(0);
+ paths.reserve(polytree.Total());
+ AddPolyNodeToPaths(polytree, ntClosed, paths);
+}
+//------------------------------------------------------------------------------
+
+void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths)
+{
+ paths.resize(0);
+ paths.reserve(polytree.Total());
+ //Open paths are top level only, so ...
+ for (int i = 0; i < polytree.ChildCount(); ++i)
+ if (polytree.Childs[i]->IsOpen())
+ paths.push_back(polytree.Childs[i]->Contour);
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const IntPoint &p)
+{
+ s << "(" << p.X << "," << p.Y << ")";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const Path &p)
+{
+ if (p.empty()) return s;
+ Path::size_type last = p.size() -1;
+ for (Path::size_type i = 0; i < last; i++)
+ s << "(" << p[i].X << "," << p[i].Y << "), ";
+ s << "(" << p[last].X << "," << p[last].Y << ")\n";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const Paths &p)
+{
+ for (Paths::size_type i = 0; i < p.size(); i++)
+ s << p[i];
+ s << "\n";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
diff --git a/polygon/clipper.hpp b/polygon/clipper.hpp
new file mode 100644
index 0000000..1c38371
--- /dev/null
+++ b/polygon/clipper.hpp
@@ -0,0 +1,404 @@
+/*******************************************************************************
+* *
+* Author : Angus Johnson *
+* Version : 6.4.0 *
+* Date : 2 July 2015 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2015 *
+* *
+* License: *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt *
+* *
+* Attributions: *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping" *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
+* http://portal.acm.org/citation.cfm?id=129906 *
+* *
+* Computer graphics and geometric modeling: implementation and algorithms *
+* By Max K. Agoston *
+* Springer; 1 edition (January 4, 2005) *
+* http://books.google.com/books?q=vatti+clipping+agoston *
+* *
+* See also: *
+* "Polygon Offsetting by Computing Winding Numbers" *
+* Paper no. DETC2005-85513 pp. 565-575 *
+* ASME 2005 International Design Engineering Technical Conferences *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
+* September 24-28, 2005 , Long Beach, California, USA *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
+* *
+*******************************************************************************/
+
+#ifndef clipper_hpp
+#define clipper_hpp
+
+#define CLIPPER_VERSION "6.2.6"
+
+//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
+//improve performance but coordinate values are limited to the range +/- 46340
+//#define use_int32
+
+//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
+//#define use_xyz
+
+//use_lines: Enables line clipping. Adds a very minor cost to performance.
+#define use_lines
+
+//use_deprecated: Enables temporary support for the obsolete functions
+//#define use_deprecated
+
+#include <vector>
+#include <list>
+#include <set>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+#include <functional>
+#include <queue>
+
+namespace ClipperLib {
+
+enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
+enum PolyType { ptSubject, ptClip };
+//By far the most widely used winding rules for polygon filling are
+//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
+//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
+//see http://glprogramming.com/red/chapter11.html
+enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
+
+#ifdef use_int32
+ typedef int cInt;
+ static cInt const loRange = 0x7FFF;
+ static cInt const hiRange = 0x7FFF;
+#else
+ typedef signed long long cInt;
+ static cInt const loRange = 0x3FFFFFFF;
+ static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
+ typedef signed long long long64; //used by Int128 class
+ typedef unsigned long long ulong64;
+
+#endif
+
+struct IntPoint {
+ cInt X;
+ cInt Y;
+#ifdef use_xyz
+ cInt Z;
+ IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
+#else
+ IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
+#endif
+
+ friend inline bool operator== (const IntPoint& a, const IntPoint& b)
+ {
+ return a.X == b.X && a.Y == b.Y;
+ }
+ friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
+ {
+ return a.X != b.X || a.Y != b.Y;
+ }
+};
+//------------------------------------------------------------------------------
+
+typedef std::vector< IntPoint > Path;
+typedef std::vector< Path > Paths;
+
+inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
+inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
+
+std::ostream& operator <<(std::ostream &s, const IntPoint &p);
+std::ostream& operator <<(std::ostream &s, const Path &p);
+std::ostream& operator <<(std::ostream &s, const Paths &p);
+
+struct DoublePoint
+{
+ double X;
+ double Y;
+ DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
+ DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
+};
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
+#endif
+
+enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
+enum JoinType {jtSquare, jtRound, jtMiter};
+enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
+
+class PolyNode;
+typedef std::vector< PolyNode* > PolyNodes;
+
+class PolyNode
+{
+public:
+ PolyNode();
+ virtual ~PolyNode(){};
+ Path Contour;
+ PolyNodes Childs;
+ PolyNode* Parent;
+ PolyNode* GetNext() const;
+ bool IsHole() const;
+ bool IsOpen() const;
+ int ChildCount() const;
+private:
+ unsigned Index; //node index in Parent.Childs
+ bool m_IsOpen;
+ JoinType m_jointype;
+ EndType m_endtype;
+ PolyNode* GetNextSiblingUp() const;
+ void AddChild(PolyNode& child);
+ friend class Clipper; //to access Index
+ friend class ClipperOffset;
+};
+
+class PolyTree: public PolyNode
+{
+public:
+ ~PolyTree(){Clear();};
+ PolyNode* GetFirst() const;
+ void Clear();
+ int Total() const;
+private:
+ PolyNodes AllNodes;
+ friend class Clipper; //to access AllNodes
+};
+
+bool Orientation(const Path &poly);
+double Area(const Path &poly);
+int PointInPolygon(const IntPoint &pt, const Path &path);
+
+void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
+
+void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
+void CleanPolygon(Path& poly, double distance = 1.415);
+void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
+void CleanPolygons(Paths& polys, double distance = 1.415);
+
+void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
+void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
+void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
+
+void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
+void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
+void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
+
+void ReversePath(Path& p);
+void ReversePaths(Paths& p);
+
+struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
+
+//enums that are used internally ...
+enum EdgeSide { esLeft = 1, esRight = 2};
+
+//forward declarations (for stuff used internally) ...
+struct TEdge;
+struct IntersectNode;
+struct LocalMinimum;
+struct OutPt;
+struct OutRec;
+struct Join;
+
+typedef std::vector < OutRec* > PolyOutList;
+typedef std::vector < TEdge* > EdgeList;
+typedef std::vector < Join* > JoinList;
+typedef std::vector < IntersectNode* > IntersectList;
+
+//------------------------------------------------------------------------------
+
+//ClipperBase is the ancestor to the Clipper class. It should not be
+//instantiated directly. This class simply abstracts the conversion of sets of
+//polygon coordinates into edge objects that are stored in a LocalMinima list.
+class ClipperBase
+{
+public:
+ ClipperBase();
+ virtual ~ClipperBase();
+ virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
+ bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
+ virtual void Clear();
+ IntRect GetBounds();
+ bool PreserveCollinear() {return m_PreserveCollinear;};
+ void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
+protected:
+ void DisposeLocalMinimaList();
+ TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
+ virtual void Reset();
+ TEdge* ProcessBound(TEdge* E, bool IsClockwise);
+ void InsertScanbeam(const cInt Y);
+ bool PopScanbeam(cInt &Y);
+ bool LocalMinimaPending();
+ bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);
+ OutRec* CreateOutRec();
+ void DisposeAllOutRecs();
+ void DisposeOutRec(PolyOutList::size_type index);
+ void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
+ void DeleteFromAEL(TEdge *e);
+ void UpdateEdgeIntoAEL(TEdge *&e);
+
+ typedef std::vector<LocalMinimum> MinimaList;
+ MinimaList::iterator m_CurrentLM;
+ MinimaList m_MinimaList;
+
+ bool m_UseFullRange;
+ EdgeList m_edges;
+ bool m_PreserveCollinear;
+ bool m_HasOpenPaths;
+ PolyOutList m_PolyOuts;
+ TEdge *m_ActiveEdges;
+
+ typedef std::priority_queue<cInt> ScanbeamList;
+ ScanbeamList m_Scanbeam;
+};
+//------------------------------------------------------------------------------
+
+class Clipper : public virtual ClipperBase
+{
+public:
+ Clipper(int initOptions = 0);
+ bool Execute(ClipType clipType,
+ Paths &solution,
+ PolyFillType fillType = pftEvenOdd);
+ bool Execute(ClipType clipType,
+ Paths &solution,
+ PolyFillType subjFillType,
+ PolyFillType clipFillType);
+ bool Execute(ClipType clipType,
+ PolyTree &polytree,
+ PolyFillType fillType = pftEvenOdd);
+ bool Execute(ClipType clipType,
+ PolyTree &polytree,
+ PolyFillType subjFillType,
+ PolyFillType clipFillType);
+ bool ReverseSolution() { return m_ReverseOutput; };
+ void ReverseSolution(bool value) {m_ReverseOutput = value;};
+ bool StrictlySimple() {return m_StrictSimple;};
+ void StrictlySimple(bool value) {m_StrictSimple = value;};
+ //set the callback function for z value filling on intersections (otherwise Z is 0)
+#ifdef use_xyz
+ void ZFillFunction(ZFillCallback zFillFunc);
+#endif
+protected:
+ virtual bool ExecuteInternal();
+private:
+ JoinList m_Joins;
+ JoinList m_GhostJoins;
+ IntersectList m_IntersectList;
+ ClipType m_ClipType;
+ typedef std::list<cInt> MaximaList;
+ MaximaList m_Maxima;
+ TEdge *m_SortedEdges;
+ bool m_ExecuteLocked;
+ PolyFillType m_ClipFillType;
+ PolyFillType m_SubjFillType;
+ bool m_ReverseOutput;
+ bool m_UsingPolyTree;
+ bool m_StrictSimple;
+#ifdef use_xyz
+ ZFillCallback m_ZFill; //custom callback
+#endif
+ void SetWindingCount(TEdge& edge);
+ bool IsEvenOddFillType(const TEdge& edge) const;
+ bool IsEvenOddAltFillType(const TEdge& edge) const;
+ void InsertLocalMinimaIntoAEL(const cInt botY);
+ void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
+ void AddEdgeToSEL(TEdge *edge);
+ bool PopEdgeFromSEL(TEdge *&edge);
+ void CopyAELToSEL();
+ void DeleteFromSEL(TEdge *e);
+ void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
+ bool IsContributing(const TEdge& edge) const;
+ bool IsTopHorz(const cInt XPos);
+ void DoMaxima(TEdge *e);
+ void ProcessHorizontals();
+ void ProcessHorizontal(TEdge *horzEdge);
+ void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ OutRec* GetOutRec(int idx);
+ void AppendPolygon(TEdge *e1, TEdge *e2);
+ void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
+ OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
+ OutPt* GetLastOutPt(TEdge *e);
+ bool ProcessIntersections(const cInt topY);
+ void BuildIntersectList(const cInt topY);
+ void ProcessIntersectList();
+ void ProcessEdgesAtTopOfScanbeam(const cInt topY);
+ void BuildResult(Paths& polys);
+ void BuildResult2(PolyTree& polytree);
+ void SetHoleState(TEdge *e, OutRec *outrec);
+ void DisposeIntersectNodes();
+ bool FixupIntersectionOrder();
+ void FixupOutPolygon(OutRec &outrec);
+ void FixupOutPolyline(OutRec &outrec);
+ bool IsHole(TEdge *e);
+ bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
+ void FixHoleLinkage(OutRec &outrec);
+ void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
+ void ClearJoins();
+ void ClearGhostJoins();
+ void AddGhostJoin(OutPt *op, const IntPoint offPt);
+ bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
+ void JoinCommonEdges();
+ void DoSimplePolygons();
+ void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
+ void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);
+ void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);
+#ifdef use_xyz
+ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
+#endif
+};
+//------------------------------------------------------------------------------
+
+class ClipperOffset
+{
+public:
+ ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
+ ~ClipperOffset();
+ void AddPath(const Path& path, JoinType joinType, EndType endType);
+ void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
+ void Execute(Paths& solution, double delta);
+ void Execute(PolyTree& solution, double delta);
+ void Clear();
+ double MiterLimit;
+ double ArcTolerance;
+private:
+ Paths m_destPolys;
+ Path m_srcPoly;
+ Path m_destPoly;
+ std::vector<DoublePoint> m_normals;
+ double m_delta, m_sinA, m_sin, m_cos;
+ double m_miterLim, m_StepsPerRad;
+ IntPoint m_lowest;
+ PolyNode m_polyNodes;
+
+ void FixOrientations();
+ void DoOffset(double delta);
+ void OffsetPoint(int j, int& k, JoinType jointype);
+ void DoSquare(int j, int k);
+ void DoMiter(int j, int k, double r);
+ void DoRound(int j, int k);
+};
+//------------------------------------------------------------------------------
+
+class clipperException : public std::exception
+{
+ public:
+ clipperException(const char* description): m_descr(description) {}
+ virtual ~clipperException() throw() {}
+ virtual const char* what() const throw() {return m_descr.c_str();}
+ private:
+ std::string m_descr;
+};
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
+
+#endif //clipper_hpp
+
+
diff --git a/polygon/determine_if_point_inside_polygon.odt b/polygon/determine_if_point_inside_polygon.odt
new file mode 100644
index 0000000..af612f7
--- /dev/null
+++ b/polygon/determine_if_point_inside_polygon.odt
Binary files differ
diff --git a/polygon/math_for_graphics.cpp b/polygon/math_for_graphics.cpp
new file mode 100644
index 0000000..c43f323
--- /dev/null
+++ b/polygon/math_for_graphics.cpp
@@ -0,0 +1,520 @@
+// math for graphics utility routines and RC, from FreePCB
+
+#include <vector>
+
+#include <cmath>
+#include <float.h>
+#include <limits.h>
+#include <common.h>
+#include <fctsys.h>
+
+#include <PolyLine.h>
+#include <math_for_graphics.h>
+
+static bool InRange( double x, double xi, double xf );
+
+/* Function FindSegmentIntersections
+ * find intersections between line segment (xi,yi) to (xf,yf)
+ * and line segment (xi2,yi2) to (xf2,yf2)
+ * returns true if intersection found
+ */
+bool FindSegmentIntersections( int xi, int yi, int xf, int yf,
+ int xi2, int yi2, int xf2, int yf2 )
+{
+ if( std::max( xi, xf ) < std::min( xi2, xf2 )
+ || std::min( xi, xf ) > std::max( xi2, xf2 )
+ || std::max( yi, yf ) < std::min( yi2, yf2 )
+ || std::min( yi, yf ) > std::max( yi2, yf2 ) )
+ return false;
+
+ return TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf,
+ xi2, yi2, xf2, yf2 );
+}
+
+
+/* Function FindLineSegmentIntersection
+ * find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * return false if no intersection or true if intersect
+ * return coords of intersections in *x1, *y1, *x2, *y2
+ * if no intersection, returns min distance in dist
+ */
+bool FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf,
+ double* x1, double* y1, double* x2, double* y2,
+ double* dist )
+{
+ double xx = 0, yy = 0; // Init made to avoid C compil "uninitialized" warning
+ bool bVert = false;
+
+ if( b > DBL_MAX / 10.0 )
+ bVert = true;
+
+ if( xf != xi ) // non-vertical segment, get intersection
+ {
+ // horizontal or oblique straight segment
+ // put into form y = c + dx;
+ double d = (double) (yf - yi) / (double) (xf - xi);
+ double c = yf - d * xf;
+
+ if( bVert )
+ {
+ // if vertical line, easy
+ if( InRange( a, xi, xf ) )
+ {
+ *x1 = a;
+ *y1 = c + d * a;
+ return 1;
+ }
+ else
+ {
+ if( dist )
+ *dist = std::min( std::abs( a - xi ), std::abs( a - xf ) );
+
+ return false;
+ }
+ }
+
+ if( std::abs( b - d ) < 1E-12 )
+ {
+ // parallel lines
+ if( dist )
+ {
+ *dist = GetPointToLineDistance( a, b, xi, xf );
+ }
+
+ return false; // lines parallel
+ }
+
+ // calculate intersection
+ xx = (c - a) / (b - d);
+ yy = a + b * (xx);
+
+ // see if intersection is within the line segment
+ if( yf == yi )
+ {
+ // horizontal line
+ if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf) )
+ return false;
+ }
+ else
+ {
+ // oblique line
+ if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf)
+ || (yy>yi && yy>yf) || (yy<yi && yy<yf) )
+ return false;
+ }
+ }
+ else
+ {
+ // vertical line segment
+ if( bVert )
+ return false;
+
+ xx = xi;
+ yy = a + b * xx;
+
+ if( (yy>=yi && yy>yf) || (yy<=yi && yy<yf) )
+ return 0;
+ }
+
+ *x1 = xx;
+ *y1 = yy;
+ return true;
+}
+
+
+/*
+ * Function TestForIntersectionOfStraightLineSegments
+ * Test for intersection of line segments
+ * If lines are parallel, returns false
+ * If true, returns also intersection coords in x, y
+ * if false, returns min. distance in dist (may be 0.0 if parallel)
+ */
+bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
+ int x2i, int y2i, int x2f, int y2f,
+ int* x, int* y, double* d )
+{
+ double a, b, dist;
+
+ // first, test for intersection
+ if( x1i == x1f && x2i == x2f )
+ {
+ // both segments are vertical, can't intersect
+ }
+ else if( y1i == y1f && y2i == y2f )
+ {
+ // both segments are horizontal, can't intersect
+ }
+ else if( x1i == x1f && y2i == y2f )
+ {
+ // first seg. vertical, second horizontal, see if they cross
+ if( InRange( x1i, x2i, x2f )
+ && InRange( y2i, y1i, y1f ) )
+ {
+ if( x )
+ *x = x1i;
+
+ if( y )
+ *y = y2i;
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ else if( y1i == y1f && x2i == x2f )
+ {
+ // first seg. horizontal, second vertical, see if they cross
+ if( InRange( y1i, y2i, y2f )
+ && InRange( x2i, x1i, x1f ) )
+ {
+ if( x )
+ *x = x2i;
+
+ if( y )
+ *y = y1i;
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ else if( x1i == x1f )
+ {
+ // first segment vertical, second oblique
+ // get a and b for second line segment, so that y = a + bx;
+ b = double( y2f - y2i ) / (x2f - x2i);
+ a = (double) y2i - b * x2i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else if( y1i == y1f )
+ {
+ // first segment horizontal, second oblique
+ // get a and b for second line segment, so that y = a + bx;
+ b = double( y2f - y2i ) / (x2f - x2i);
+ a = (double) y2i - b * x2i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else if( x2i == x2f )
+ {
+ // second segment vertical, first oblique
+ // get a and b for first line segment, so that y = a + bx;
+ b = double( y1f - y1i ) / (x1f - x1i);
+ a = (double) y1i - b * x1i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) && InRange( y1, y2i, y2f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else if( y2i == y2f )
+ {
+ // second segment horizontal, first oblique
+ // get a and b for second line segment, so that y = a + bx;
+ b = double( y1f - y1i ) / (x1f - x1i);
+ a = (double) y1i - b * x1i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // both segments oblique
+ if( long( y1f - y1i ) * (x2f - x2i) != long( y2f - y2i ) * (x1f - x1i) )
+ {
+ // not parallel, get a and b for first line segment, so that y = a + bx;
+ b = double( y1f - y1i ) / (x1f - x1i);
+ a = (double) y1i - b * x1i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
+ &x1, &y1, &x2, &y2 );
+
+ // both segments oblique
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ // don't intersect, get shortest distance between each endpoint and the other line segment
+ dist = GetPointToLineSegmentDistance( x1i, y1i, x2i, y2i, x2f, y2f );
+
+ double xx = x1i;
+ double yy = y1i;
+ double dd = GetPointToLineSegmentDistance( x1f, y1f, x2i, y2i, x2f, y2f );
+
+ if( dd < dist )
+ {
+ dist = dd;
+ xx = x1f;
+ yy = y1f;
+ }
+
+ dd = GetPointToLineSegmentDistance( x2i, y2i, x1i, y1i, x1f, y1f );
+
+ if( dd < dist )
+ {
+ dist = dd;
+ xx = x2i;
+ yy = y2i;
+ }
+
+ dd = GetPointToLineSegmentDistance( x2f, y2f, x1i, y1i, x1f, y1f );
+
+ if( dd < dist )
+ {
+ dist = dd;
+ xx = x2f;
+ yy = y2f;
+ }
+
+ if( x )
+ *x = KiROUND( xx );
+
+ if( y )
+ *y = KiROUND( yy );
+
+ if( d )
+ *d = dist;
+
+ return false;
+}
+
+
+/* Function GetClearanceBetweenSegments
+ * Get clearance between 2 segments
+ * Returns coordinates of the closest point between these 2 segments in x, y
+ * If clearance > max_cl, just returns max_cl+1 and doesn't return x,y
+ */
+int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int w1,
+ int x2i, int y2i, int x2f, int y2f, int w2,
+ int max_cl, int* x, int* y )
+{
+ // check clearance between bounding rectangles
+ int min_dist = max_cl + ( (w1 + w2) / 2 );
+
+ if( std::min( x1i, x1f ) - std::max( x2i, x2f ) > min_dist )
+ return max_cl+1;
+
+ if( std::min( x2i, x2f ) - std::max( x1i, x1f ) > min_dist )
+ return max_cl+1;
+
+ if( std::min( y1i, y1f ) - std::max( y2i, y2f ) > min_dist )
+ return max_cl+1;
+
+ if( std::min( y2i, y2f ) - std::max( y1i, y1f ) > min_dist )
+ return max_cl+1;
+
+ int xx, yy;
+ double dist;
+ TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f,
+ x2i, y2i, x2f, y2f, &xx, &yy, &dist );
+ int d = KiROUND( dist ) - ((w1 + w2) / 2);
+ if( d < 0 )
+ d = 0;
+
+ if( x )
+ *x = xx;
+
+ if( y )
+ *y = yy;
+
+ return d;
+}
+
+
+/* Function GetPointToLineDistance
+ * Get min. distance from (x,y) to line y = a + bx
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * returns closest point on line in xpp, ypp
+ */
+double GetPointToLineDistance( double a, double b, int x, int y, double* xpp, double* ypp )
+{
+ if( b > DBL_MAX / 10 )
+ {
+ // vertical line
+ if( xpp && ypp )
+ {
+ *xpp = a;
+ *ypp = y;
+ }
+
+ return std::abs( a - x );
+ }
+
+ // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
+ double d = -1.0 / b;
+ double c = (double) y - d * x;
+
+ // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
+ double xp = (a - c) / (d - b);
+ double yp = a + b * xp;
+
+ if( xpp && ypp )
+ {
+ *xpp = xp;
+ *ypp = yp;
+ }
+
+ // find distance
+ return Distance( x, y, xp, yp );
+}
+
+
+/**
+ * Function GetPointToLineSegmentDistance
+ * Get distance between line segment and point
+ * @param x,y = point
+ * @param xi,yi Start point of the line segament
+ * @param xf,yf End point of the line segment
+ * @return the distance
+ */
+double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf )
+{
+ // test for vertical or horizontal segment
+ if( xf==xi )
+ {
+ // vertical line segment
+ if( InRange( y, yi, yf ) )
+ return std::abs( x - xi );
+ else
+ return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
+ }
+ else if( yf==yi )
+ {
+ // horizontal line segment
+ if( InRange( x, xi, xf ) )
+ return std::abs( y - yi );
+ else
+ return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
+ }
+ else
+ {
+ // oblique segment
+ // find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
+ double b = (double) (yf - yi) / (xf - xi);
+ double a = (double) yi - b * xi;
+
+ // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
+ double d = -1.0 / b;
+ double c = (double) y - d * x;
+
+ // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
+ double xp = (a - c) / (d - b);
+ double yp = a + b * xp;
+
+ // find distance
+ if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) )
+ return Distance( x, y, xp, yp );
+ else
+ return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
+ }
+}
+
+
+// test for value within range
+bool InRange( double x, double xi, double xf )
+{
+ if( xf > xi )
+ {
+ if( x >= xi && x <= xf )
+ return true;
+ }
+ else
+ {
+ if( x >= xf && x <= xi )
+ return true;
+ }
+
+ return false;
+}
diff --git a/polygon/math_for_graphics.h b/polygon/math_for_graphics.h
new file mode 100644
index 0000000..c8be901
--- /dev/null
+++ b/polygon/math_for_graphics.h
@@ -0,0 +1,71 @@
+#ifndef MATH_FOR_GRAPHICS_H
+#define MATH_FOR_GRAPHICS_H
+// math stuff for graphics, from FreePCB
+
+/* Function FindLineSegmentIntersection
+ * find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * return false if no intersection or true if intersect
+ * return coords of intersections in *x1, *y1, *x2, *y2
+ * if no intersection, returns min distance in dist
+ */
+bool FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf,
+ double * x1, double * y1, double * x2, double * y2, double * dist=NULL );
+
+/* Function FindSegmentIntersections
+ * find intersections between line segment (xi,yi) to (xf,yf)
+ * and line segment (xi2,yi2) to (xf2,yf2)
+ * returns true if intersection found
+ */
+bool FindSegmentIntersections( int xi, int yi, int xf, int yf,
+ int xi2, int yi2, int xf2, int yf2 );
+
+/**
+ * Function TestForIntersectionOfStraightLineSegments
+ * Test for intersection of line segments
+ * If lines are parallel, returns false
+ * If true, returns also intersection coords in x, y
+ * if false, returns min. distance in dist (may be 0.0 if parallel)
+ * and coords on nearest point in one of the segments in (x,y)
+ * @param x1i, y1i, x1f, y1f = integer coordinates of the first segment
+ * @param x2i, y2i, x2f, y2f = integer coordinates of the other segment
+ * @param x, y = pointers on 2 integer to store the intersection coordinates (can be NULL)
+ * @param dist = pointeur on a double to store the dist.
+ * @return true if intersect.
+ */
+bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
+ int x2i, int y2i, int x2f, int y2f,
+ int * x=NULL, int * y=NULL, double * dist=NULL );
+
+/* Function GetClearanceBetweenSegments
+ * Get clearance between 2 segments
+ * Returns coordinates of the closest point between these 2 segments in x, y
+ * If clearance > max_cl, just returns max_cl+1 and doesn't return x,y
+ */
+int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int w1,
+ int x2i, int y2i, int x2f, int y2f, int w2,
+ int max_cl, int * x, int * y );
+
+/**
+ * Function GetPointToLineSegmentDistance
+ * Get distance between line segment and point
+ * @param x,y = point
+ * @param xi,yi, xf,yf = the end-points of the line segment
+ * @return the distance
+ */
+double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf );
+
+/* Function GetPointToLineDistance
+ * Get min. distance from (x,y) to line y = a + bx
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * returns closest point on line in xpp, ypp
+ */
+double GetPointToLineDistance( double a, double b, int x, int y,
+ double * xp=NULL, double * yp=NULL );
+
+inline double Distance( double x1, double y1, double x2, double y2 )
+{
+ return hypot( x1 - x2, y1 - y2 );
+}
+
+#endif
diff --git a/polygon/poly2tri/common/shapes.cc b/polygon/poly2tri/common/shapes.cc
new file mode 100644
index 0000000..06eb1f8
--- /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 <iostream>
+
+namespace p2t {
+Triangle::Triangle( Point& a, Point& b, Point& c )
+{
+ points_[0] = &a; points_[1] = &b; points_[2] = &c;
+ neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL;
+ constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false;
+ delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
+ interior_ = false;
+}
+
+
+// Update neighbor pointers
+void Triangle::MarkNeighbor( Point* p1, Point* p2, Triangle* t )
+{
+ if( (p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2]) )
+ neighbors_[0] = t;
+ else if( (p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0]) )
+ neighbors_[1] = t;
+ else if( (p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0]) )
+ neighbors_[2] = t;
+ else
+ assert( 0 );
+}
+
+
+// Exhaustive search to update neighbor pointers
+void Triangle::MarkNeighbor( Triangle& t )
+{
+ if( t.Contains( points_[1], points_[2] ) )
+ {
+ neighbors_[0] = &t;
+ t.MarkNeighbor( points_[1], points_[2], this );
+ }
+ else if( t.Contains( points_[0], points_[2] ) )
+ {
+ neighbors_[1] = &t;
+ t.MarkNeighbor( points_[0], points_[2], this );
+ }
+ else if( t.Contains( points_[0], points_[1] ) )
+ {
+ neighbors_[2] = &t;
+ t.MarkNeighbor( points_[0], points_[1], this );
+ }
+}
+
+
+/**
+ * Clears all references to all other triangles and points
+ */
+void Triangle::Clear()
+{
+ Triangle* t;
+
+ for( int i = 0; i<3; i++ )
+ {
+ t = neighbors_[i];
+
+ if( t != NULL )
+ {
+ t->ClearNeighbor( this );
+ }
+ }
+
+ ClearNeighbors();
+ points_[0] = points_[1] = points_[2] = NULL;
+}
+
+
+void Triangle::ClearNeighbor( Triangle* triangle )
+{
+ if( neighbors_[0] == triangle )
+ {
+ neighbors_[0] = NULL;
+ }
+ else if( neighbors_[1] == triangle )
+ {
+ neighbors_[1] = NULL;
+ }
+ else
+ {
+ neighbors_[2] = NULL;
+ }
+}
+
+
+void Triangle::ClearNeighbors()
+{
+ neighbors_[0] = NULL;
+ neighbors_[1] = NULL;
+ neighbors_[2] = NULL;
+}
+
+
+void Triangle::ClearDelunayEdges()
+{
+ delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
+}
+
+
+Point* Triangle::OppositePoint( Triangle& t, Point& p )
+{
+ Point* cw = t.PointCW( p );
+
+ /*
+ double x = cw->x;
+ double y = cw->y;
+
+ x = p.x;
+ y = p.y;
+ */
+
+ return PointCW( *cw );
+}
+
+
+// Legalized triangle by rotating clockwise around point(0)
+void Triangle::Legalize( Point& point )
+{
+ points_[1] = points_[0];
+ points_[0] = points_[2];
+ points_[2] = &point;
+}
+
+
+// Legalize triagnle by rotating clockwise around oPoint
+void Triangle::Legalize( Point& opoint, Point& npoint )
+{
+ if( &opoint == points_[0] )
+ {
+ points_[1] = points_[0];
+ points_[0] = points_[2];
+ points_[2] = &npoint;
+ }
+ else if( &opoint == points_[1] )
+ {
+ points_[2] = points_[1];
+ points_[1] = points_[0];
+ points_[0] = &npoint;
+ }
+ else if( &opoint == points_[2] )
+ {
+ points_[0] = points_[2];
+ points_[2] = points_[1];
+ points_[1] = &npoint;
+ }
+ else
+ {
+ assert( 0 );
+ }
+}
+
+
+int Triangle::Index( const Point* p )
+{
+ if( p == points_[0] )
+ {
+ return 0;
+ }
+ else if( p == points_[1] )
+ {
+ return 1;
+ }
+ else if( p == points_[2] )
+ {
+ return 2;
+ }
+
+ assert( 0 );
+ return 0; // you better hope its a Debug build.
+}
+
+
+int Triangle::EdgeIndex( const Point* p1, const Point* p2 )
+{
+ if( points_[0] == p1 )
+ {
+ if( points_[1] == p2 )
+ {
+ return 2;
+ }
+ else if( points_[2] == p2 )
+ {
+ return 1;
+ }
+ }
+ else if( points_[1] == p1 )
+ {
+ if( points_[2] == p2 )
+ {
+ return 0;
+ }
+ else if( points_[0] == p2 )
+ {
+ return 2;
+ }
+ }
+ else if( points_[2] == p1 )
+ {
+ if( points_[0] == p2 )
+ {
+ return 1;
+ }
+ else if( points_[1] == p2 )
+ {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+void Triangle::MarkConstrainedEdge( const int index )
+{
+ constrained_edge[index] = true;
+}
+
+
+void Triangle::MarkConstrainedEdge( Edge& edge )
+{
+ MarkConstrainedEdge( edge.p, edge.q );
+}
+
+
+// Mark edge as constrained
+void Triangle::MarkConstrainedEdge( Point* p, Point* q )
+{
+ if( (q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0]) )
+ {
+ constrained_edge[2] = true;
+ }
+ else if( (q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0]) )
+ {
+ constrained_edge[1] = true;
+ }
+ else if( (q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1]) )
+ {
+ constrained_edge[0] = true;
+ }
+}
+
+
+// The point counter-clockwise to given point
+Point* Triangle::PointCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return points_[2];
+ }
+ else if( &point == points_[1] )
+ {
+ return points_[0];
+ }
+ else if( &point == points_[2] )
+ {
+ return points_[1];
+ }
+
+ assert( 0 );
+ return NULL; // you better hope its a Debug build.
+}
+
+
+// The point counter-clockwise to given point
+Point* Triangle::PointCCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return points_[1];
+ }
+ else if( &point == points_[1] )
+ {
+ return points_[2];
+ }
+ else if( &point == points_[2] )
+ {
+ return points_[0];
+ }
+
+ assert( 0 );
+ return NULL; // you better hope its a Debug build.
+}
+
+
+// The neighbor clockwise to given point
+Triangle* Triangle::NeighborCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return neighbors_[1];
+ }
+ else if( &point == points_[1] )
+ {
+ return neighbors_[2];
+ }
+
+ return neighbors_[0];
+}
+
+
+// The neighbor counter-clockwise to given point
+Triangle* Triangle::NeighborCCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return neighbors_[2];
+ }
+ else if( &point == points_[1] )
+ {
+ return neighbors_[0];
+ }
+
+ return neighbors_[1];
+}
+
+
+bool Triangle::GetConstrainedEdgeCCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return constrained_edge[2];
+ }
+ else if( &p == points_[1] )
+ {
+ return constrained_edge[0];
+ }
+
+ return constrained_edge[1];
+}
+
+
+bool Triangle::GetConstrainedEdgeCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return constrained_edge[1];
+ }
+ else if( &p == points_[1] )
+ {
+ return constrained_edge[2];
+ }
+
+ return constrained_edge[0];
+}
+
+
+void Triangle::SetConstrainedEdgeCCW( Point& p, bool ce )
+{
+ if( &p == points_[0] )
+ {
+ constrained_edge[2] = ce;
+ }
+ else if( &p == points_[1] )
+ {
+ constrained_edge[0] = ce;
+ }
+ else
+ {
+ constrained_edge[1] = ce;
+ }
+}
+
+
+void Triangle::SetConstrainedEdgeCW( Point& p, bool ce )
+{
+ if( &p == points_[0] )
+ {
+ constrained_edge[1] = ce;
+ }
+ else if( &p == points_[1] )
+ {
+ constrained_edge[2] = ce;
+ }
+ else
+ {
+ constrained_edge[0] = ce;
+ }
+}
+
+
+bool Triangle::GetDelunayEdgeCCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return delaunay_edge[2];
+ }
+ else if( &p == points_[1] )
+ {
+ return delaunay_edge[0];
+ }
+
+ return delaunay_edge[1];
+}
+
+
+bool Triangle::GetDelunayEdgeCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return delaunay_edge[1];
+ }
+ else if( &p == points_[1] )
+ {
+ return delaunay_edge[2];
+ }
+
+ return delaunay_edge[0];
+}
+
+
+void Triangle::SetDelunayEdgeCCW( Point& p, bool e )
+{
+ if( &p == points_[0] )
+ {
+ delaunay_edge[2] = e;
+ }
+ else if( &p == points_[1] )
+ {
+ delaunay_edge[0] = e;
+ }
+ else
+ {
+ delaunay_edge[1] = e;
+ }
+}
+
+
+void Triangle::SetDelunayEdgeCW( Point& p, bool e )
+{
+ if( &p == points_[0] )
+ {
+ delaunay_edge[1] = e;
+ }
+ else if( &p == points_[1] )
+ {
+ delaunay_edge[2] = e;
+ }
+ else
+ {
+ delaunay_edge[0] = e;
+ }
+}
+
+
+// The neighbor across to given point
+Triangle& Triangle::NeighborAcross( Point& opoint )
+{
+ if( &opoint == points_[0] )
+ {
+ return *neighbors_[0];
+ }
+ else if( &opoint == points_[1] )
+ {
+ return *neighbors_[1];
+ }
+
+ return *neighbors_[2];
+}
+
+
+void Triangle::DebugPrint()
+{
+ std::cout << points_[0]->x << "," << points_[0]->y << " ";
+ std::cout << points_[1]->x << "," << points_[1]->y << " ";
+ std::cout << points_[2]->x << "," << points_[2]->y << "\n";
+}
+}
diff --git a/polygon/poly2tri/common/shapes.h b/polygon/poly2tri/common/shapes.h
new file mode 100644
index 0000000..c65f485
--- /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 <vector>
+#include <cstddef>
+#include <assert.h>
+#include <cmath>
+
+namespace p2t {
+struct Edge;
+
+struct Point
+{
+ double x, y;
+
+ /// Default constructor does nothing (for performance).
+ Point()
+ {
+ x = 0.0;
+ y = 0.0;
+ }
+
+ /// The edges this point constitutes an upper ending point
+ std::vector<Edge*> edge_list;
+
+ /// Construct using coordinates.
+ Point( double x, double y ) : x( x ), y( y ) {}
+
+ /// Set this point to all zeros.
+ void set_zero()
+ {
+ x = 0.0;
+ y = 0.0;
+ }
+
+ /// Set this point to some specified coordinates.
+ void set( double x_, double y_ )
+ {
+ x = x_;
+ y = y_;
+ }
+
+ /// Negate this point.
+ Point operator -() const
+ {
+ Point v;
+
+ v.set( -x, -y );
+ return v;
+ }
+
+ /// Add a point to this point.
+ void operator +=( const Point& v )
+ {
+ x += v.x;
+ y += v.y;
+ }
+
+ /// Subtract a point from this point.
+ void operator -=( const Point& v )
+ {
+ x -= v.x;
+ y -= v.y;
+ }
+
+ /// Multiply this point by a scalar.
+ void operator *=( double a )
+ {
+ x *= a;
+ y *= a;
+ }
+
+ /// Get the length of this point (the norm).
+ double Length() const
+ {
+ return sqrt( x * x + y * y );
+ }
+
+ /// Convert this point into a unit point. Returns the Length.
+ double Normalize()
+ {
+ double len = Length();
+
+ x /= len;
+ y /= len;
+ return len;
+ }
+};
+
+// Represents a simple polygon's edge
+struct Edge
+{
+ Point* p, * q;
+
+ /// Constructor
+ Edge( Point& p1, Point& p2 ) : p( &p1 ), q( &p2 )
+ {
+ if( p1.y > p2.y )
+ {
+ q = &p1;
+ p = &p2;
+ }
+ else if( p1.y == p2.y )
+ {
+ if( p1.x > p2.x )
+ {
+ q = &p1;
+ p = &p2;
+ }
+ else if( p1.x == p2.x )
+ {
+ // Repeat points
+ assert( false );
+ }
+ }
+
+ q->edge_list.push_back( this );
+ }
+};
+
+// Triangle-based data structures are know to have better performance than quad-edge structures
+// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
+// "Triangulations in CGAL"
+class Triangle
+{
+public:
+
+/// Constructor
+ Triangle( Point& a, Point& b, Point& c );
+
+/// Flags to determine if an edge is a Constrained edge
+ bool constrained_edge[3];
+/// Flags to determine if an edge is a Delauney edge
+ bool delaunay_edge[3];
+
+ Point* GetPoint( const int& index );
+ Point* PointCW( Point& point );
+ Point* PointCCW( Point& point );
+ Point* OppositePoint( Triangle& t, Point& p );
+
+ Triangle* GetNeighbor( const int& index );
+ void MarkNeighbor( Point* p1, Point* p2, Triangle* t );
+ void MarkNeighbor( Triangle& t );
+
+ void MarkConstrainedEdge( const int index );
+ void MarkConstrainedEdge( Edge& edge );
+ void MarkConstrainedEdge( Point* p, Point* q );
+
+ int Index( const Point* p );
+ int EdgeIndex( const Point* p1, const Point* p2 );
+
+ Triangle* NeighborCW( Point& point );
+ Triangle* NeighborCCW( Point& point );
+ bool GetConstrainedEdgeCCW( Point& p );
+ bool GetConstrainedEdgeCW( Point& p );
+ void SetConstrainedEdgeCCW( Point& p, bool ce );
+ void SetConstrainedEdgeCW( Point& p, bool ce );
+ bool GetDelunayEdgeCCW( Point& p );
+ bool GetDelunayEdgeCW( Point& p );
+ void SetDelunayEdgeCCW( Point& p, bool e );
+ void SetDelunayEdgeCW( Point& p, bool e );
+
+ bool Contains( Point* p );
+ bool Contains( const Edge& e );
+ bool Contains( Point* p, Point* q );
+ void Legalize( Point& point );
+ void Legalize( Point& opoint, Point& npoint );
+
+/**
+ * Clears all references to all other triangles and points
+ */
+ void Clear();
+ void ClearNeighbor( Triangle* triangle );
+ void ClearNeighbors();
+ void ClearDelunayEdges();
+
+ inline bool IsInterior();
+ inline void IsInterior( bool b );
+
+ Triangle& NeighborAcross( Point& opoint );
+
+ void DebugPrint();
+
+private:
+
+/// Triangle points
+ Point* points_[3];
+/// Neighbor list
+ Triangle* neighbors_[3];
+
+/// Has this triangle been marked as an interior triangle?
+ bool interior_;
+};
+
+inline bool cmp( const Point* a, const Point* b )
+{
+ if( a->y < b->y )
+ {
+ return true;
+ }
+ else if( a->y == b->y )
+ {
+ // Make sure q is point with greater x value
+ if( a->x < b->x )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/// Add two points_ component-wise.
+inline Point operator +( const Point& a, const Point& b )
+{
+ return Point( a.x + b.x, a.y + b.y );
+}
+
+
+/// Subtract two points_ component-wise.
+inline Point operator -( const Point& a, const Point& b )
+{
+ return Point( a.x - b.x, a.y - b.y );
+}
+
+
+/// Multiply point by scalar
+inline Point operator *( double s, const Point& a )
+{
+ return Point( s * a.x, s * a.y );
+}
+
+
+inline bool operator ==( const Point& a, const Point& b )
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+
+inline bool operator !=( const Point& a, const Point& b )
+{
+ return !(a.x == b.x) && !(a.y == b.y);
+}
+
+
+/// Peform the dot product on two vectors.
+inline double Dot( const Point& a, const Point& b )
+{
+ return a.x * b.x + a.y * b.y;
+}
+
+
+/// Perform the cross product on two vectors. In 2D this produces a scalar.
+inline double Cross( const Point& a, const Point& b )
+{
+ return a.x * b.y - a.y * b.x;
+}
+
+
+/// Perform the cross product on a point and a scalar. In 2D this produces
+/// a point.
+inline Point Cross( const Point& a, double s )
+{
+ return Point( s * a.y, -s * a.x );
+}
+
+
+/// Perform the cross product on a scalar and a point. In 2D this produces
+/// a point.
+inline Point Cross( const double s, const Point& a )
+{
+ return Point( -s * a.y, s * a.x );
+}
+
+
+inline Point* Triangle::GetPoint( const int& index )
+{
+ return points_[index];
+}
+
+
+inline Triangle* Triangle::GetNeighbor( const int& index )
+{
+ return neighbors_[index];
+}
+
+
+inline bool Triangle::Contains( Point* p )
+{
+ return p == points_[0] || p == points_[1] || p == points_[2];
+}
+
+
+inline bool Triangle::Contains( const Edge& e )
+{
+ return Contains( e.p ) && Contains( e.q );
+}
+
+
+inline bool Triangle::Contains( Point* p, Point* q )
+{
+ return Contains( p ) && Contains( q );
+}
+
+
+inline bool Triangle::IsInterior()
+{
+ return interior_;
+}
+
+
+inline void Triangle::IsInterior( bool b )
+{
+ interior_ = b;
+}
+}
+
+#endif
diff --git a/polygon/poly2tri/common/utils.h b/polygon/poly2tri/common/utils.h
new file mode 100644
index 0000000..3de9fb1
--- /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 <exception>
+#include <math.h>
+
+namespace p2t {
+const double PI_3div4 = 3 * M_PI / 4;
+const double PI_div2 = 1.57079632679489661923;
+const double EPSILON = 1e-12;
+
+enum Orientation {
+ CW, CCW, COLLINEAR
+};
+
+/**
+ * Forumla to calculate signed area<br>
+ * Positive if CCW<br>
+ * Negative if CW<br>
+ * 0 if collinear<br>
+ * <pre>
+ * A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ * = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * </pre>
+ */
+Orientation Orient2d( Point& pa, Point& pb, Point& pc )
+{
+ double detleft = (pa.x - pc.x) * (pb.y - pc.y);
+ double detright = (pa.y - pc.y) * (pb.x - pc.x);
+ double val = detleft - detright;
+
+ if( val > -EPSILON && val < EPSILON )
+ {
+ return COLLINEAR;
+ }
+ else if( val > 0 )
+ {
+ return CCW;
+ }
+
+ return CW;
+}
+
+
+/*
+ * bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
+ * {
+ * double pdx = pd.x;
+ * double pdy = pd.y;
+ * double adx = pa.x - pdx;
+ * double ady = pa.y - pdy;
+ * double bdx = pb.x - pdx;
+ * double bdy = pb.y - pdy;
+ *
+ * double adxbdy = adx * bdy;
+ * double bdxady = bdx * ady;
+ * double oabd = adxbdy - bdxady;
+ *
+ * if (oabd <= EPSILON) {
+ * return false;
+ * }
+ *
+ * double cdx = pc.x - pdx;
+ * double cdy = pc.y - pdy;
+ *
+ * double cdxady = cdx * ady;
+ * double adxcdy = adx * cdy;
+ * double ocad = cdxady - adxcdy;
+ *
+ * if (ocad <= EPSILON) {
+ * return false;
+ * }
+ *
+ * return true;
+ * }
+ *
+ */
+
+bool InScanArea( Point& pa, Point& pb, Point& pc, Point& pd )
+{
+ double oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y);
+
+ if( oadb >= -EPSILON )
+ {
+ return false;
+ }
+
+ double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y);
+
+ if( oadc <= EPSILON )
+ {
+ return false;
+ }
+
+ return true;
+}
+}
+
+#endif
diff --git a/polygon/poly2tri/poly2tri.h b/polygon/poly2tri/poly2tri.h
new file mode 100644
index 0000000..487755e
--- /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 0000000..019df4a
--- /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 0000000..bab73d4
--- /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 0000000..d783825
--- /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<Point*> polyline)
+{
+ sweep_context_ = new SweepContext(polyline);
+ sweep_ = new Sweep;
+}
+
+void CDT::AddHole(std::vector<Point*> polyline)
+{
+ sweep_context_->AddHole(polyline);
+}
+
+void CDT::AddPoint(Point* point) {
+ sweep_context_->AddPoint(point);
+}
+
+void CDT::Triangulate()
+{
+ sweep_->Triangulate(*sweep_context_);
+}
+
+std::vector<p2t::Triangle*> CDT::GetTriangles()
+{
+ return sweep_context_->GetTriangles();
+}
+
+std::list<p2t::Triangle*> CDT::GetMap()
+{
+ return sweep_context_->GetMap();
+}
+
+CDT::~CDT()
+{
+ delete sweep_context_;
+ delete sweep_;
+}
+
+}
+
diff --git a/polygon/poly2tri/sweep/cdt.h b/polygon/poly2tri/sweep/cdt.h
new file mode 100644
index 0000000..3e6f024
--- /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 <mason.green@gmail.com>
+ *
+ */
+
+namespace p2t {
+
+class CDT
+{
+public:
+
+ /**
+ * Constructor - add polyline with non repeating points
+ *
+ * @param polyline
+ */
+ CDT(std::vector<Point*> polyline);
+
+ /**
+ * Destructor - clean up memory
+ */
+ ~CDT();
+
+ /**
+ * Add a hole
+ *
+ * @param polyline
+ */
+ void AddHole(std::vector<Point*> polyline);
+
+ /**
+ * Add a steiner point
+ *
+ * @param point
+ */
+ void AddPoint(Point* point);
+
+ /**
+ * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points
+ */
+ void Triangulate();
+
+ /**
+ * Get CDT triangles
+ */
+ std::vector<Triangle*> GetTriangles();
+
+ /**
+ * Get triangle map
+ */
+ std::list<Triangle*> GetMap();
+
+ private:
+
+ /**
+ * Internals
+ */
+
+ SweepContext* sweep_context_;
+ Sweep* sweep_;
+
+};
+
+}
+
+#endif
diff --git a/polygon/poly2tri/sweep/sweep.cc b/polygon/poly2tri/sweep/sweep.cc
new file mode 100644
index 0000000..75e7adf
--- /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 <stdexcept>
+#include "sweep.h"
+#include "sweep_context.h"
+#include "advancing_front.h"
+#include "../common/utils.h"
+
+namespace p2t {
+
+// Triangulate simple polygon with holes
+void Sweep::Triangulate(SweepContext& tcx)
+{
+ tcx.InitTriangulation();
+ tcx.CreateAdvancingFront(nodes_);
+ // Sweep points; build mesh
+ SweepPoints(tcx);
+ // Clean up
+ FinalizationPolygon(tcx);
+}
+
+void Sweep::SweepPoints(SweepContext& tcx)
+{
+ for (int i = 1; i < tcx.point_count(); i++) {
+ Point& point = *tcx.GetPoint(i);
+ Node* node = &PointEvent(tcx, point);
+ for (unsigned int i = 0; i < point.edge_list.size(); i++) {
+ EdgeEvent(tcx, point.edge_list[i], node);
+ }
+ }
+}
+
+void Sweep::FinalizationPolygon(SweepContext& tcx)
+{
+ // Get an Internal triangle to start with
+ Triangle* t = tcx.front()->head()->next->triangle;
+ Point* p = tcx.front()->head()->next->point;
+ while (!t->GetConstrainedEdgeCW(*p)) {
+ t = t->NeighborCCW(*p);
+ }
+
+ // Collect interior triangles constrained by edges
+ tcx.MeshClean(*t);
+}
+
+Node& Sweep::PointEvent(SweepContext& tcx, Point& point)
+{
+ Node& node = tcx.LocateNode(point);
+ Node& new_node = NewFrontTriangle(tcx, point, node);
+
+ // Only need to check +epsilon since point never have smaller
+ // x value than node due to how we fetch nodes from the front
+ if (point.x <= node.point->x + EPSILON) {
+ Fill(tcx, node);
+ }
+
+ //tcx.AddNode(new_node);
+
+ FillAdvancingFront(tcx, new_node);
+ return new_node;
+}
+
+void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ tcx.edge_event.constrained_edge = edge;
+ tcx.edge_event.right = (edge->p->x > edge->q->x);
+
+ if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) {
+ return;
+ }
+
+ // For now we will do all needed filling
+ // TODO: integrate with flip process might give some better performance
+ // but for now this avoid the issue with cases that needs both flips and fills
+ FillEdgeEvent(tcx, edge, node);
+ EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q);
+}
+
+void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
+{
+ if (IsEdgeSideOfTriangle(*triangle, ep, eq)) {
+ return;
+ }
+
+ Point* p1 = triangle->PointCCW(point);
+ Orientation o1 = Orient2d(eq, *p1, ep);
+ if (o1 == COLLINEAR) {
+ if( triangle->Contains(&eq, p1)) {
+ triangle->MarkConstrainedEdge(&eq, p1 );
+ // We are modifying the constraint maybe it would be better to
+ // not change the given constraint and just keep a variable for the new constraint
+ tcx.edge_event.constrained_edge->q = p1;
+ triangle = &triangle->NeighborAcross(point);
+ EdgeEvent( tcx, ep, *p1, triangle, *p1 );
+ } else {
+ std::runtime_error("EdgeEvent - collinear points not supported");
+ assert(0);
+ }
+ return;
+ }
+
+ Point* p2 = triangle->PointCW(point);
+ Orientation o2 = Orient2d(eq, *p2, ep);
+ if (o2 == COLLINEAR) {
+ if( triangle->Contains(&eq, p2)) {
+ triangle->MarkConstrainedEdge(&eq, p2 );
+ // We are modifying the constraint maybe it would be better to
+ // not change the given constraint and just keep a variable for the new constraint
+ tcx.edge_event.constrained_edge->q = p2;
+ triangle = &triangle->NeighborAcross(point);
+ EdgeEvent( tcx, ep, *p2, triangle, *p2 );
+ } else {
+ std::runtime_error("EdgeEvent - collinear points not supported");
+ assert(0);
+ }
+ return;
+ }
+
+ if (o1 == o2) {
+ // Need to decide if we are rotating CW or CCW to get to a triangle
+ // that will cross edge
+ if (o1 == CW) {
+ triangle = triangle->NeighborCCW(point);
+ } else{
+ triangle = triangle->NeighborCW(point);
+ }
+ EdgeEvent(tcx, ep, eq, triangle, point);
+ } else {
+ // This triangle crosses constraint so lets flippin start!
+ FlipEdgeEvent(tcx, ep, eq, triangle, point);
+ }
+}
+
+bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq)
+{
+ int index = triangle.EdgeIndex(&ep, &eq);
+
+ if (index != -1) {
+ triangle.MarkConstrainedEdge(index);
+ Triangle* t = triangle.GetNeighbor(index);
+ if (t) {
+ t->MarkConstrainedEdge(&ep, &eq);
+ }
+ return true;
+ }
+ return false;
+}
+
+Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node)
+{
+ Triangle* triangle = new Triangle(point, *node.point, *node.next->point);
+
+ triangle->MarkNeighbor(*node.triangle);
+ tcx.AddToMap(triangle);
+
+ Node* new_node = new Node(point);
+ nodes_.push_back(new_node);
+
+ new_node->next = node.next;
+ new_node->prev = &node;
+ node.next->prev = new_node;
+ node.next = new_node;
+
+ if (!Legalize(tcx, *triangle)) {
+ tcx.MapTriangleToNodes(*triangle);
+ }
+
+ return *new_node;
+}
+
+void Sweep::Fill(SweepContext& tcx, Node& node)
+{
+ Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point);
+
+ // TODO: should copy the constrained_edge value from neighbor triangles
+ // for now constrained_edge values are copied during the legalize
+ triangle->MarkNeighbor(*node.prev->triangle);
+ triangle->MarkNeighbor(*node.triangle);
+
+ tcx.AddToMap(triangle);
+
+ // Update the advancing front
+ node.prev->next = node.next;
+ node.next->prev = node.prev;
+
+ // If it was legalized the triangle has already been mapped
+ if (!Legalize(tcx, *triangle)) {
+ tcx.MapTriangleToNodes(*triangle);
+ }
+
+}
+
+void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
+{
+
+ // Fill right holes
+ Node* node = n.next;
+
+ while (node->next) {
+ // if HoleAngle exceeds 90 degrees then break.
+ if (LargeHole_DontFill(node)) break;
+ Fill(tcx, *node);
+ node = node->next;
+ }
+
+ // Fill left holes
+ node = n.prev;
+
+ while (node->prev) {
+ // if HoleAngle exceeds 90 degrees then break.
+ if (LargeHole_DontFill(node)) break;
+ Fill(tcx, *node);
+ node = node->prev;
+ }
+
+ // Fill right basins
+ if (n.next && n.next->next) {
+ double angle = BasinAngle(n);
+ if (angle < PI_3div4) {
+ FillBasin(tcx, n);
+ }
+ }
+}
+
+// True if HoleAngle exceeds 90 degrees.
+bool Sweep::LargeHole_DontFill(Node* node) {
+
+ Node* nextNode = node->next;
+ Node* prevNode = node->prev;
+ if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point))
+ return false;
+
+ // Check additional points on front.
+ Node* next2Node = nextNode->next;
+ // "..Plus.." because only want angles on same side as point being added.
+ if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point))
+ return false;
+
+ Node* prev2Node = prevNode->prev;
+ // "..Plus.." because only want angles on same side as point being added.
+ if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point))
+ return false;
+
+ return true;
+}
+
+bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) {
+ double angle = Angle(*origin, *pa, *pb);
+ bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2));
+ return exceeds90Degrees;
+}
+
+bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) {
+ double angle = Angle(*origin, *pa, *pb);
+ bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0);
+ return exceedsPlus90DegreesOrIsNegative;
+}
+
+double Sweep::Angle(Point& origin, Point& pa, Point& pb) {
+ /* Complex plane
+ * ab = cosA +i*sinA
+ * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
+ * atan2(y,x) computes the principal value of the argument function
+ * applied to the complex number x+iy
+ * Where x = ax*bx + ay*by
+ * y = ax*by - ay*bx
+ */
+ double px = origin.x;
+ double py = origin.y;
+ double ax = pa.x- px;
+ double ay = pa.y - py;
+ double bx = pb.x - px;
+ double by = pb.y - py;
+ double x = ax * by - ay * bx;
+ double y = ax * bx + ay * by;
+ double angle = atan2(x, y);
+ return angle;
+}
+
+double Sweep::BasinAngle(Node& node)
+{
+ double ax = node.point->x - node.next->next->point->x;
+ double ay = node.point->y - node.next->next->point->y;
+ return atan2(ay, ax);
+}
+
+double Sweep::HoleAngle(Node& node)
+{
+ /* Complex plane
+ * ab = cosA +i*sinA
+ * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
+ * atan2(y,x) computes the principal value of the argument function
+ * applied to the complex number x+iy
+ * Where x = ax*bx + ay*by
+ * y = ax*by - ay*bx
+ */
+ double ax = node.next->point->x - node.point->x;
+ double ay = node.next->point->y - node.point->y;
+ double bx = node.prev->point->x - node.point->x;
+ double by = node.prev->point->y - node.point->y;
+ return atan2(ax * by - ay * bx, ax * bx + ay * by);
+}
+
+bool Sweep::Legalize(SweepContext& tcx, Triangle& t)
+{
+ // To legalize a triangle we start by finding if any of the three edges
+ // violate the Delaunay condition
+ for (int i = 0; i < 3; i++) {
+ if (t.delaunay_edge[i])
+ continue;
+
+ Triangle* ot = t.GetNeighbor(i);
+
+ if (ot) {
+ Point* p = t.GetPoint(i);
+ Point* op = ot->OppositePoint(t, *p);
+ int oi = ot->Index(op);
+
+ // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
+ // then we should not try to legalize
+ if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) {
+ t.constrained_edge[i] = ot->constrained_edge[oi];
+ continue;
+ }
+
+ bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op);
+
+ if (inside) {
+ // Lets mark this shared edge as Delaunay
+ t.delaunay_edge[i] = true;
+ ot->delaunay_edge[oi] = true;
+
+ // Lets rotate shared edge one vertex CW to legalize it
+ RotateTrianglePair(t, *p, *ot, *op);
+
+ // We now got one valid Delaunay Edge shared by two triangles
+ // This gives us 4 new edges to check for Delaunay
+
+ // Make sure that triangle to node mapping is done only one time for a specific triangle
+ bool not_legalized = !Legalize(tcx, t);
+ if (not_legalized) {
+ tcx.MapTriangleToNodes(t);
+ }
+
+ not_legalized = !Legalize(tcx, *ot);
+ if (not_legalized)
+ tcx.MapTriangleToNodes(*ot);
+
+ // Reset the Delaunay edges, since they only are valid Delaunay edges
+ // until we add a new triangle or point.
+ // XXX: need to think about this. Can these edges be tried after we
+ // return to previous recursive level?
+ t.delaunay_edge[i] = false;
+ ot->delaunay_edge[oi] = false;
+
+ // If triangle have been legalized no need to check the other edges since
+ // the recursive legalization will handles those so we can end here.
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd)
+{
+ double adx = pa.x - pd.x;
+ double ady = pa.y - pd.y;
+ double bdx = pb.x - pd.x;
+ double bdy = pb.y - pd.y;
+
+ double adxbdy = adx * bdy;
+ double bdxady = bdx * ady;
+ double oabd = adxbdy - bdxady;
+
+ if (oabd <= 0)
+ return false;
+
+ double cdx = pc.x - pd.x;
+ double cdy = pc.y - pd.y;
+
+ double cdxady = cdx * ady;
+ double adxcdy = adx * cdy;
+ double ocad = cdxady - adxcdy;
+
+ if (ocad <= 0)
+ return false;
+
+ double bdxcdy = bdx * cdy;
+ double cdxbdy = cdx * bdy;
+
+ double alift = adx * adx + ady * ady;
+ double blift = bdx * bdx + bdy * bdy;
+ double clift = cdx * cdx + cdy * cdy;
+
+ double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
+
+ return det > 0;
+}
+
+void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op)
+{
+ Triangle* n1, *n2, *n3, *n4;
+ n1 = t.NeighborCCW(p);
+ n2 = t.NeighborCW(p);
+ n3 = ot.NeighborCCW(op);
+ n4 = ot.NeighborCW(op);
+
+ bool ce1, ce2, ce3, ce4;
+ ce1 = t.GetConstrainedEdgeCCW(p);
+ ce2 = t.GetConstrainedEdgeCW(p);
+ ce3 = ot.GetConstrainedEdgeCCW(op);
+ ce4 = ot.GetConstrainedEdgeCW(op);
+
+ bool de1, de2, de3, de4;
+ de1 = t.GetDelunayEdgeCCW(p);
+ de2 = t.GetDelunayEdgeCW(p);
+ de3 = ot.GetDelunayEdgeCCW(op);
+ de4 = ot.GetDelunayEdgeCW(op);
+
+ t.Legalize(p, op);
+ ot.Legalize(op, p);
+
+ // Remap delaunay_edge
+ ot.SetDelunayEdgeCCW(p, de1);
+ t.SetDelunayEdgeCW(p, de2);
+ t.SetDelunayEdgeCCW(op, de3);
+ ot.SetDelunayEdgeCW(op, de4);
+
+ // Remap constrained_edge
+ ot.SetConstrainedEdgeCCW(p, ce1);
+ t.SetConstrainedEdgeCW(p, ce2);
+ t.SetConstrainedEdgeCCW(op, ce3);
+ ot.SetConstrainedEdgeCW(op, ce4);
+
+ // Remap neighbors
+ // XXX: might optimize the markNeighbor by keeping track of
+ // what side should be assigned to what neighbor after the
+ // rotation. Now mark neighbor does lots of testing to find
+ // the right side.
+ t.ClearNeighbors();
+ ot.ClearNeighbors();
+ if (n1) ot.MarkNeighbor(*n1);
+ if (n2) t.MarkNeighbor(*n2);
+ if (n3) t.MarkNeighbor(*n3);
+ if (n4) ot.MarkNeighbor(*n4);
+ t.MarkNeighbor(ot);
+}
+
+void Sweep::FillBasin(SweepContext& tcx, Node& node)
+{
+ if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
+ tcx.basin.left_node = node.next->next;
+ } else {
+ tcx.basin.left_node = node.next;
+ }
+
+ // Find the bottom and right node
+ tcx.basin.bottom_node = tcx.basin.left_node;
+ while (tcx.basin.bottom_node->next
+ && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) {
+ tcx.basin.bottom_node = tcx.basin.bottom_node->next;
+ }
+ if (tcx.basin.bottom_node == tcx.basin.left_node) {
+ // No valid basin
+ return;
+ }
+
+ tcx.basin.right_node = tcx.basin.bottom_node;
+ while (tcx.basin.right_node->next
+ && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) {
+ tcx.basin.right_node = tcx.basin.right_node->next;
+ }
+ if (tcx.basin.right_node == tcx.basin.bottom_node) {
+ // No valid basins
+ return;
+ }
+
+ tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x;
+ tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y;
+
+ FillBasinReq(tcx, tcx.basin.bottom_node);
+}
+
+void Sweep::FillBasinReq(SweepContext& tcx, Node* node)
+{
+ // if shallow stop filling
+ if (IsShallow(tcx, *node)) {
+ return;
+ }
+
+ Fill(tcx, *node);
+
+ if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) {
+ return;
+ } else if (node->prev == tcx.basin.left_node) {
+ Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point);
+ if (o == CW) {
+ return;
+ }
+ node = node->next;
+ } else if (node->next == tcx.basin.right_node) {
+ Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point);
+ if (o == CCW) {
+ return;
+ }
+ node = node->prev;
+ } else {
+ // Continue with the neighbor node with lowest Y value
+ if (node->prev->point->y < node->next->point->y) {
+ node = node->prev;
+ } else {
+ node = node->next;
+ }
+ }
+
+ FillBasinReq(tcx, node);
+}
+
+bool Sweep::IsShallow(SweepContext& tcx, Node& node)
+{
+ double height;
+
+ if (tcx.basin.left_highest) {
+ height = tcx.basin.left_node->point->y - node.point->y;
+ } else {
+ height = tcx.basin.right_node->point->y - node.point->y;
+ }
+
+ // if shallow stop filling
+ if (tcx.basin.width > height) {
+ return true;
+ }
+ return false;
+}
+
+void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ if (tcx.edge_event.right) {
+ FillRightAboveEdgeEvent(tcx, edge, node);
+ } else {
+ FillLeftAboveEdgeEvent(tcx, edge, node);
+ }
+}
+
+void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ while (node->next->point->x < edge->p->x) {
+ // Check if next node is below the edge
+ if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) {
+ FillRightBelowEdgeEvent(tcx, edge, *node);
+ } else {
+ node = node->next;
+ }
+ }
+}
+
+void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ if (node.point->x < edge->p->x) {
+ if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
+ // Concave
+ FillRightConcaveEdgeEvent(tcx, edge, node);
+ } else{
+ // Convex
+ FillRightConvexEdgeEvent(tcx, edge, node);
+ // Retry this one
+ FillRightBelowEdgeEvent(tcx, edge, node);
+ }
+ }
+}
+
+void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ Fill(tcx, *node.next);
+ if (node.next->point != edge->p) {
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) {
+ // Below
+ if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
+ // Next is concave
+ FillRightConcaveEdgeEvent(tcx, edge, node);
+ } else {
+ // Next is convex
+ }
+ }
+ }
+
+}
+
+void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ // Next concave or convex?
+ if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) {
+ // Concave
+ FillRightConcaveEdgeEvent(tcx, edge, *node.next);
+ } else{
+ // Convex
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) {
+ // Below
+ FillRightConvexEdgeEvent(tcx, edge, *node.next);
+ } else{
+ // Above
+ }
+ }
+}
+
+void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ while (node->prev->point->x > edge->p->x) {
+ // Check if next node is below the edge
+ if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) {
+ FillLeftBelowEdgeEvent(tcx, edge, *node);
+ } else {
+ node = node->prev;
+ }
+ }
+}
+
+void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ if (node.point->x > edge->p->x) {
+ if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
+ // Concave
+ FillLeftConcaveEdgeEvent(tcx, edge, node);
+ } else {
+ // Convex
+ FillLeftConvexEdgeEvent(tcx, edge, node);
+ // Retry this one
+ FillLeftBelowEdgeEvent(tcx, edge, node);
+ }
+ }
+}
+
+void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ // Next concave or convex?
+ if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) {
+ // Concave
+ FillLeftConcaveEdgeEvent(tcx, edge, *node.prev);
+ } else{
+ // Convex
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) {
+ // Below
+ FillLeftConvexEdgeEvent(tcx, edge, *node.prev);
+ } else{
+ // Above
+ }
+ }
+}
+
+void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ Fill(tcx, *node.prev);
+ if (node.prev->point != edge->p) {
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) {
+ // Below
+ if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
+ // Next is concave
+ FillLeftConcaveEdgeEvent(tcx, edge, node);
+ } else{
+ // Next is convex
+ }
+ }
+ }
+
+}
+
+void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p)
+{
+ Triangle& ot = t->NeighborAcross(p);
+ Point& op = *ot.OppositePoint(*t, p);
+
+ if (&ot == NULL) {
+ // If we want to integrate the fillEdgeEvent do it here
+ // With current implementation we should never get here
+ //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
+ assert(0);
+ }
+
+ if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
+ // Lets rotate shared edge one vertex CW
+ RotateTrianglePair(*t, p, ot, op);
+ tcx.MapTriangleToNodes(*t);
+ tcx.MapTriangleToNodes(ot);
+
+ if (p == eq && op == ep) {
+ if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) {
+ t->MarkConstrainedEdge(&ep, &eq);
+ ot.MarkConstrainedEdge(&ep, &eq);
+ Legalize(tcx, *t);
+ Legalize(tcx, ot);
+ } else {
+ // XXX: I think one of the triangles should be legalized here?
+ }
+ } else {
+ Orientation o = Orient2d(eq, op, ep);
+ t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op);
+ FlipEdgeEvent(tcx, ep, eq, t, p);
+ }
+ } else {
+ Point& newP = NextFlipPoint(ep, eq, ot, op);
+ FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP);
+ EdgeEvent(tcx, ep, eq, t, p);
+ }
+}
+
+Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op)
+{
+ if (o == CCW) {
+ // ot is not crossing edge after flip
+ int edge_index = ot.EdgeIndex(&p, &op);
+ ot.delaunay_edge[edge_index] = true;
+ Legalize(tcx, ot);
+ ot.ClearDelunayEdges();
+ return t;
+ }
+
+ // t is not crossing edge after flip
+ int edge_index = t.EdgeIndex(&p, &op);
+
+ t.delaunay_edge[edge_index] = true;
+ Legalize(tcx, t);
+ t.ClearDelunayEdges();
+ return ot;
+}
+
+Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op)
+{
+ Orientation o2d = Orient2d(eq, op, ep);
+ if (o2d == CW) {
+ // Right
+ return *ot.PointCCW(op);
+ } else if (o2d == CCW) {
+ // Left
+ return *ot.PointCW(op);
+ }
+
+ //throw new RuntimeException("[Unsupported] Opposing point on constrained edge");
+ assert(0);
+
+ // Never executed, due tu assert( 0 ). Just to avoid compil warning
+ return ep;
+}
+
+void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
+ Triangle& t, Point& p)
+{
+ Triangle& ot = t.NeighborAcross(p);
+ Point& op = *ot.OppositePoint(t, p);
+
+ if (&t.NeighborAcross(p) == NULL) {
+ // If we want to integrate the fillEdgeEvent do it here
+ // With current implementation we should never get here
+ //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
+ assert(0);
+ }
+
+ if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
+ // flip with new edge op->eq
+ FlipEdgeEvent(tcx, eq, op, &ot, op);
+ // TODO: Actually I just figured out that it should be possible to
+ // improve this by getting the next ot and op before the the above
+ // flip and continue the flipScanEdgeEvent here
+ // set new ot and op here and loop back to inScanArea test
+ // also need to set a new flip_triangle first
+ // Turns out at first glance that this is somewhat complicated
+ // so it will have to wait.
+ } else{
+ Point& newP = NextFlipPoint(ep, eq, ot, op);
+ FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
+ }
+}
+
+Sweep::~Sweep() {
+
+ // Clean up memory
+ for( unsigned i = 0; i < nodes_.size(); i++ )
+ {
+ delete nodes_[i];
+ }
+
+}
+
+}
+
diff --git a/polygon/poly2tri/sweep/sweep.h b/polygon/poly2tri/sweep/sweep.h
new file mode 100644
index 0000000..07822d1
--- /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 <vector>
+
+namespace p2t {
+
+class SweepContext;
+struct Node;
+struct Point;
+struct Edge;
+class Triangle;
+
+class Sweep
+{
+public:
+
+ /**
+ * Triangulate
+ *
+ * @param tcx
+ */
+ void Triangulate(SweepContext& tcx);
+
+ /**
+ * Destructor - clean up memory
+ */
+ ~Sweep();
+
+private:
+
+ /**
+ * Start sweeping the Y-sorted point set from bottom to top
+ *
+ * @param tcx
+ */
+ void SweepPoints(SweepContext& tcx);
+
+ /**
+ * Find closes node to the left of the new point and
+ * create a new triangle. If needed new holes and basins
+ * will be filled to.
+ *
+ * @param tcx
+ * @param point
+ * @return
+ */
+ Node& PointEvent(SweepContext& tcx, Point& point);
+
+ /**
+ *
+ *
+ * @param tcx
+ * @param edge
+ * @param node
+ */
+ void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point);
+
+ /**
+ * Creates a new front triangle and legalize it
+ *
+ * @param tcx
+ * @param point
+ * @param node
+ * @return
+ */
+ Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node);
+
+ /**
+ * Adds a triangle to the advancing front to fill a hole.
+ * @param tcx
+ * @param node - middle node, that is the bottom of the hole
+ */
+ void Fill(SweepContext& tcx, Node& node);
+
+ /**
+ * Returns true if triangle was legalized
+ */
+ bool Legalize(SweepContext& tcx, Triangle& t);
+
+ /**
+ * <b>Requirement</b>:<br>
+ * 1. a,b and c form a triangle.<br>
+ * 2. a and d is know to be on opposite side of bc<br>
+ * <pre>
+ * a
+ * +
+ * / \
+ * / \
+ * b/ \c
+ * +-------+
+ * / d \
+ * / \
+ * </pre>
+ * <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
+ * a,b and c<br>
+ * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
+ * This preknowledge gives us a way to optimize the incircle test
+ * @param pa - triangle point, opposite d
+ * @param pb - triangle point
+ * @param pc - triangle point
+ * @param pd - point opposite a
+ * @return true if d is inside circle, false if on circle edge
+ */
+ bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd);
+
+ /**
+ * Rotates a triangle pair one vertex CW
+ *<pre>
+ * n2 n2
+ * P +-----+ P +-----+
+ * | t /| |\ t |
+ * | / | | \ |
+ * n1| / |n3 n1| \ |n3
+ * | / | after CW | \ |
+ * |/ oT | | oT \|
+ * +-----+ oP +-----+
+ * n4 n4
+ * </pre>
+ */
+ void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op);
+
+ /**
+ * Fills holes in the Advancing Front
+ *
+ *
+ * @param tcx
+ * @param n
+ */
+ void FillAdvancingFront(SweepContext& tcx, Node& n);
+
+ // Decision-making about when to Fill hole.
+ // Contributed by ToolmakerSteve2
+ bool LargeHole_DontFill(Node* node);
+ bool AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb);
+ bool AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb);
+ double Angle(Point& origin, Point& pa, Point& pb);
+
+ /**
+ *
+ * @param node - middle node
+ * @return the angle between 3 front nodes
+ */
+ double HoleAngle(Node& node);
+
+ /**
+ * The basin angle is decided against the horizontal line [1,0]
+ */
+ double BasinAngle(Node& node);
+
+ /**
+ * Fills a basin that has formed on the Advancing Front to the right
+ * of given node.<br>
+ * First we decide a left,bottom and right node that forms the
+ * boundaries of the basin. Then we do a reqursive fill.
+ *
+ * @param tcx
+ * @param node - starting node, this or next node will be left node
+ */
+ void FillBasin(SweepContext& tcx, Node& node);
+
+ /**
+ * Recursive algorithm to fill a Basin with triangles
+ *
+ * @param tcx
+ * @param node - bottom_node
+ */
+ void FillBasinReq(SweepContext& tcx, Node* node);
+
+ bool IsShallow(SweepContext& tcx, Node& node);
+
+ bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq);
+
+ void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p);
+
+ /**
+ * After a flip we have two triangles and know that only one will still be
+ * intersecting the edge. So decide which to contiune with and legalize the other
+ *
+ * @param tcx
+ * @param o - should be the result of an orient2d( eq, op, ep )
+ * @param t - triangle 1
+ * @param ot - triangle 2
+ * @param p - a point shared by both triangles
+ * @param op - another point shared by both triangles
+ * @return returns the triangle still intersecting the edge
+ */
+ Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op);
+
+ /**
+ * When we need to traverse from one triangle to the next we need
+ * the point in current triangle that is the opposite point to the next
+ * triangle.
+ *
+ * @param ep
+ * @param eq
+ * @param ot
+ * @param op
+ * @return
+ */
+ Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op);
+
+ /**
+ * Scan part of the FlipScan algorithm<br>
+ * When a triangle pair isn't flippable we will scan for the next
+ * point that is inside the flip triangle scan area. When found
+ * we generate a new flipEdgeEvent
+ *
+ * @param tcx
+ * @param ep - last point on the edge we are traversing
+ * @param eq - first point on the edge we are traversing
+ * @param flipTriangle - the current triangle sharing the point eq with edge
+ * @param t
+ * @param p
+ */
+ void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p);
+
+ void FinalizationPolygon(SweepContext& tcx);
+
+ std::vector<Node*> nodes_;
+
+};
+
+}
+
+#endif
diff --git a/polygon/poly2tri/sweep/sweep_context.cc b/polygon/poly2tri/sweep/sweep_context.cc
new file mode 100644
index 0000000..6c0b044
--- /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 <algorithm>
+#include "advancing_front.h"
+
+namespace p2t {
+
+SweepContext::SweepContext(std::vector<Point*> polyline) :
+ front_(0),
+ head_(0),
+ tail_(0),
+ af_head_(0),
+ af_middle_(0),
+ af_tail_(0)
+{
+ basin = Basin();
+ edge_event = EdgeEvent();
+
+ points_ = polyline;
+
+ InitEdges(points_);
+}
+
+void SweepContext::AddHole(std::vector<Point*> polyline)
+{
+ InitEdges(polyline);
+ for(unsigned int i = 0; i < polyline.size(); i++) {
+ points_.push_back(polyline[i]);
+ }
+}
+
+void SweepContext::AddPoint(Point* point) {
+ points_.push_back(point);
+}
+
+std::vector<Triangle*> SweepContext::GetTriangles()
+{
+ return triangles_;
+}
+
+std::list<Triangle*> SweepContext::GetMap()
+{
+ return map_;
+}
+
+void SweepContext::InitTriangulation()
+{
+ double xmax(points_[0]->x), xmin(points_[0]->x);
+ double ymax(points_[0]->y), ymin(points_[0]->y);
+
+ // Calculate bounds.
+ for (unsigned int i = 0; i < points_.size(); i++) {
+ Point& p = *points_[i];
+ if (p.x > xmax)
+ xmax = p.x;
+ if (p.x < xmin)
+ xmin = p.x;
+ if (p.y > ymax)
+ ymax = p.y;
+ if (p.y < ymin)
+ ymin = p.y;
+ }
+
+ double dx = kAlpha * (xmax - xmin);
+ double dy = kAlpha * (ymax - ymin);
+ head_ = new Point(xmax + dx, ymin - dy);
+ tail_ = new Point(xmin - dx, ymin - dy);
+
+ // Sort points along y-axis
+ std::sort(points_.begin(), points_.end(), cmp);
+
+}
+
+void SweepContext::InitEdges(std::vector<Point*> polyline)
+{
+ int num_points = polyline.size();
+ for (int i = 0; i < num_points; i++) {
+ int j = i < num_points - 1 ? i + 1 : 0;
+ edge_list.push_back(new Edge(*polyline[i], *polyline[j]));
+ }
+}
+
+Point* SweepContext::GetPoint(const int& index)
+{
+ return points_[index];
+}
+
+void SweepContext::AddToMap(Triangle* triangle)
+{
+ map_.push_back(triangle);
+}
+
+Node& SweepContext::LocateNode(Point& point)
+{
+ // TODO implement search tree
+ return *front_->LocateNode(point.x);
+}
+
+void SweepContext::CreateAdvancingFront(std::vector<Node*> nodes)
+{
+
+ (void) nodes;
+ // Initial triangle
+ Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
+
+ map_.push_back(triangle);
+
+ af_head_ = new Node(*triangle->GetPoint(1), *triangle);
+ af_middle_ = new Node(*triangle->GetPoint(0), *triangle);
+ af_tail_ = new Node(*triangle->GetPoint(2));
+ front_ = new AdvancingFront(*af_head_, *af_tail_);
+
+ // TODO: More intuitive if head is middles next and not previous?
+ // so swap head and tail
+ af_head_->next = af_middle_;
+ af_middle_->next = af_tail_;
+ af_middle_->prev = af_head_;
+ af_tail_->prev = af_middle_;
+}
+
+void SweepContext::RemoveNode(Node* node)
+{
+ delete node;
+}
+
+void SweepContext::MapTriangleToNodes(Triangle& t)
+{
+ for (int i = 0; i < 3; i++) {
+ if (!t.GetNeighbor(i)) {
+ Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i)));
+ if (n)
+ n->triangle = &t;
+ }
+ }
+}
+
+void SweepContext::RemoveFromMap(Triangle* triangle)
+{
+ map_.remove(triangle);
+}
+
+void SweepContext::MeshClean(Triangle& triangle)
+{
+ std::vector<Triangle *> triangles;
+ triangles.push_back(&triangle);
+
+ while(!triangles.empty()){
+ Triangle *t = triangles.back();
+ triangles.pop_back();
+
+ if (t != NULL && !t->IsInterior()) {
+ t->IsInterior(true);
+ triangles_.push_back(t);
+ for (int i = 0; i < 3; i++) {
+ if (!t->constrained_edge[i])
+ triangles.push_back(t->GetNeighbor(i));
+ }
+ }
+ }
+}
+
+SweepContext::~SweepContext()
+{
+
+ // Clean up memory
+
+ delete head_;
+ delete tail_;
+ delete front_;
+ delete af_head_;
+ delete af_middle_;
+ delete af_tail_;
+
+ typedef std::list<Triangle*> type_list;
+
+ for(type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) {
+ Triangle* ptr = *iter;
+ delete ptr;
+ }
+
+ for(unsigned int i = 0; i < edge_list.size(); i++) {
+ delete edge_list[i];
+ }
+
+}
+
+}
diff --git a/polygon/poly2tri/sweep/sweep_context.h b/polygon/poly2tri/sweep/sweep_context.h
new file mode 100644
index 0000000..1010c0e
--- /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 <list>
+#include <vector>
+#include <cstddef>
+
+namespace p2t {
+
+// Inital triangle factor, seed triangle will extend 30% of
+// PointSet width to both left and right.
+const double kAlpha = 0.3;
+
+struct Point;
+class Triangle;
+struct Node;
+struct Edge;
+class AdvancingFront;
+
+class SweepContext {
+public:
+
+/// Constructor
+SweepContext(std::vector<Point*> polyline);
+/// Destructor
+~SweepContext();
+
+void set_head(Point* p1);
+
+Point* head();
+
+void set_tail(Point* p1);
+
+Point* tail();
+
+int point_count();
+
+Node& LocateNode(Point& point);
+
+void RemoveNode(Node* node);
+
+void CreateAdvancingFront(std::vector<Node*> nodes);
+
+/// Try to map a node to all sides of this triangle that don't have a neighbor
+void MapTriangleToNodes(Triangle& t);
+
+void AddToMap(Triangle* triangle);
+
+Point* GetPoint(const int& index);
+
+Point* GetPoints();
+
+void RemoveFromMap(Triangle* triangle);
+
+void AddHole(std::vector<Point*> polyline);
+
+void AddPoint(Point* point);
+
+AdvancingFront* front();
+
+void MeshClean(Triangle& triangle);
+
+std::vector<Triangle*> GetTriangles();
+std::list<Triangle*> GetMap();
+
+std::vector<Edge*> edge_list;
+
+struct Basin {
+ Node* left_node;
+ Node* bottom_node;
+ Node* right_node;
+ double width;
+ bool left_highest;
+
+ Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false)
+ {
+ }
+
+ void Clear()
+ {
+ left_node = NULL;
+ bottom_node = NULL;
+ right_node = NULL;
+ width = 0.0;
+ left_highest = false;
+ }
+};
+
+struct EdgeEvent {
+ Edge* constrained_edge;
+ bool right;
+
+ EdgeEvent() : constrained_edge(NULL), right(false)
+ {
+ }
+};
+
+Basin basin;
+EdgeEvent edge_event;
+
+private:
+
+friend class Sweep;
+
+std::vector<Triangle*> triangles_;
+std::list<Triangle*> map_;
+std::vector<Point*> points_;
+
+// Advancing front
+AdvancingFront* front_;
+// head point used with advancing front
+Point* head_;
+// tail point used with advancing front
+Point* tail_;
+
+Node *af_head_, *af_middle_, *af_tail_;
+
+void InitTriangulation();
+void InitEdges(std::vector<Point*> polyline);
+
+};
+
+inline AdvancingFront* SweepContext::front()
+{
+ return front_;
+}
+
+inline int SweepContext::point_count()
+{
+ return points_.size();
+}
+
+inline void SweepContext::set_head(Point* p1)
+{
+ head_ = p1;
+}
+
+inline Point* SweepContext::head()
+{
+ return head_;
+}
+
+inline void SweepContext::set_tail(Point* p1)
+{
+ tail_ = p1;
+}
+
+inline Point* SweepContext::tail()
+{
+ return tail_;
+}
+
+}
+
+#endif
diff --git a/polygon/polygon_test_point_inside.cpp b/polygon/polygon_test_point_inside.cpp
new file mode 100644
index 0000000..df2a34b
--- /dev/null
+++ b/polygon/polygon_test_point_inside.cpp
@@ -0,0 +1,171 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2007-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 polygon_test_point_inside.cpp
+ */
+
+#include <cmath>
+#include <vector>
+#include <PolyLine.h>
+
+/* this algo uses the the Jordan curve theorem to find if a point is inside or outside a polygon:
+ * It run a semi-infinite line horizontally (increasing x, fixed y)
+ * out from the test point, and count how many edges it crosses.
+ * At each crossing, the ray switches between inside and outside.
+ * If odd count, the test point is inside the polygon
+ * This is called the Jordan curve theorem, or sometimes referred to as the "even-odd" test.
+ * Take care to starting and ending points of segments outlines, when the horizontal line crosses a segment outline
+ * exactly on an ending point:
+ * Because the starting point of a segment is also the ending point of the previous, only one must be used.
+ * And we do no use twice the same segment, so we do NOT use both starting and ending points of these 2 segments.
+ * So we must use only one ending point of each segment when calculating intersections
+ * but it cannot be always the starting or the ending point. This depend on relative position of 2 consectutive segments
+ * Here, the ending point above the Y reference position is used
+ * and the ending point below or equal the Y reference position is NOT used
+ * Obviously, others cases are irrelevant because there is not intersection.
+ */
+
+#define OUTSIDE false
+#define INSIDE true
+
+bool TestPointInsidePolygon( const CPOLYGONS_LIST& aPolysList,
+ int aIdxstart,
+ int aIdxend,
+ int aRefx,
+ int aRefy)
+
+/**
+ * Function TestPointInsidePolygon
+ * test if a point is inside or outside a polygon.
+ * the polygon must have only lines (not arcs) for outlines.
+ * @param aPolysList: the list of polygons
+ * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
+ * @param aIdxend: the ending point of this polygon in m_FilledPolysList.
+ * @param aRefx, aRefy: the point coordinate to test
+ * @return true if the point is inside, false for outside
+ */
+{
+ // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
+ int ics, ice;
+ int count = 0;
+
+ // find all intersection points of line with polyline sides
+ for( ics = aIdxstart, ice = aIdxend; ics <= aIdxend; ice = ics++ )
+ {
+ int seg_startX = aPolysList.GetX( ics );
+ int seg_startY = aPolysList.GetY( ics );
+ int seg_endX = aPolysList.GetX( ice );
+ int seg_endY = aPolysList.GetY( ice );
+
+ /* Trivial cases: skip if ref above or below the segment to test */
+ if( ( seg_startY > aRefy ) && (seg_endY > aRefy ) )
+ continue;
+
+ // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
+ // So we eliminate one end point of 2 consecutive segments.
+ // Note: also we skip horizontal segments if ref point is on this horizontal line
+ // So reference points on horizontal segments outlines always are seen as outside the polygon
+ if( ( seg_startY <= aRefy ) && (seg_endY <= aRefy ) )
+ continue;
+
+ /* refy is between seg_startY and seg_endY.
+ * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
+ * either by the first test or by the second test
+ * see if an horizontal semi infinite line from refx is intersecting the segment
+ */
+
+ // calculate the x position of the intersection of this segment and the semi 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 newrefx = (double) (aRefx - seg_startX);
+ double newrefy = (double) (aRefy - seg_startY);
+
+ // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
+ // with the horizontal line at the new refy position
+ // the line slope = seg_endY/seg_endX;
+ // and the x pos relative to the new origin is intersec_x = refy/slope
+ // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
+ double intersec_x = (newrefy * seg_endX) / seg_endY;
+ if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
+ count++;
+ }
+
+ return count & 1 ? INSIDE : OUTSIDE;
+}
+
+
+/* Function TestPointInsidePolygon (overlaid)
+ * same as previous, but use wxPoint and aCount corners
+ */
+bool TestPointInsidePolygon( const wxPoint *aPolysList, int aCount, const wxPoint &aRefPoint )
+{
+ // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
+ int ics, ice;
+ int count = 0;
+ // find all intersection points of line with polyline sides
+ for( ics = 0, ice = aCount-1; ics < aCount; ice = ics++ )
+ {
+ int seg_startX = aPolysList[ics].x;
+ int seg_startY = aPolysList[ics].y;
+ int seg_endX = aPolysList[ice].x;
+ int seg_endY = aPolysList[ice].y;
+
+ /* Trivial cases: skip if ref above or below the segment to test */
+ if( ( seg_startY > aRefPoint.y ) && (seg_endY > aRefPoint.y ) )
+ continue;
+
+ // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
+ // So we eliminate one end point of 2 consecutive segments.
+ // Note: also we skip horizontal segments if ref point is on this horizontal line
+ // So reference points on horizontal segments outlines always are seen as outside the polygon
+ if( ( seg_startY <= aRefPoint.y ) && (seg_endY <= aRefPoint.y ) )
+ continue;
+
+ /* refy is between seg_startY and seg_endY.
+ * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
+ * either by the first test or by the second test
+ * see if an horizontal semi infinite line from refx is intersecting the segment
+ */
+
+ // calculate the x position of the intersection of this segment and the semi 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 newrefx = (double) (aRefPoint.x - seg_startX);
+ double newrefy = (double) (aRefPoint.y - seg_startY);
+
+ // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
+ // with the horizontal line at the new refy position
+ // the line slope = seg_endY/seg_endX;
+ // and the x pos relative to the new origin is intersec_x = refy/slope
+ // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
+ double intersec_x = (newrefy * seg_endX) / seg_endY;
+ if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
+ count++;
+ }
+
+ return count & 1 ? INSIDE : OUTSIDE;
+}
diff --git a/polygon/polygon_test_point_inside.h b/polygon/polygon_test_point_inside.h
new file mode 100644
index 0000000..833eba8
--- /dev/null
+++ b/polygon/polygon_test_point_inside.h
@@ -0,0 +1,59 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2007-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
+ */
+
+#ifndef __WXWINDOWS__
+// define here wxPoint if we want to compile outside wxWidgets
+class wxPoint
+{
+public:
+ int x, y;
+};
+#endif
+class CPOLYGONS_LIST;
+
+/**
+ * Function TestPointInsidePolygon
+ * test if a point is inside or outside a polygon.
+ * @param aPolysList: the list of polygons
+ * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
+ * @param aIdxend: the ending point of the polygon in m_FilledPolysList.
+ * @param aRefx, aRefy: the point coordinate to test
+ * @return true if the point is inside, false for outside
+ */
+bool TestPointInsidePolygon( const CPOLYGONS_LIST& aPolysList,
+ int aIdxstart,
+ int aIdxend,
+ int aRefx,
+ int aRefy);
+/**
+ * Function TestPointInsidePolygon (overlaid)
+ * same as previous, but mainly use wxPoint
+ * @param aPolysList: the list of polygons
+ * @param aCount: corners count in aPolysList.
+ * @param aRefPoint: the point coordinate to test
+ * @return true if the point is inside, false for outside
+ */
+bool TestPointInsidePolygon( const wxPoint* aPolysList,
+ int aCount,
+ const wxPoint &aRefPoint );
diff --git a/polygon/polygons_defs.h b/polygon/polygons_defs.h
new file mode 100644
index 0000000..82bccfa
--- /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 <boost/polygon/polygon.hpp>
+
+// Define some types used here from boost::polygon
+namespace bpl = boost::polygon; // bpl = boost polygon library
+using namespace bpl::operators; // +, -, =, ...
+
+// Definitions needed by boost::polygon
+typedef int coordinate_type;
+
+/**
+ * KI_POLYGON defines a single polygon ( boost::polygon_data type.
+ * When holes are created in a KPolygon, they are
+ * linked to main outline by overlapping segments,
+ * so there is always one polygon and one list of corners
+ * coordinates are int
+ */
+typedef bpl::polygon_data<int> KI_POLYGON;
+
+/**
+ * KI_POLYGON_SET defines a set of single KI_POLYGON.
+ * A KI_POLYGON_SET is used to store a set of polygons
+ * when performing operations between 2 polygons
+ * or 2 sets of polygons
+ * The result of operations like and, xor... between 2 polygons
+ * is always stored in a KI_POLYGON_SET, because these operations
+ * can create many polygons
+ */
+typedef std::vector<KI_POLYGON> KI_POLYGON_SET;
+
+/**
+ * KI_POLY_POINT defines a point for boost::polygon.
+ * KI_POLY_POINT store x and y coordinates (int)
+ */
+typedef bpl::point_data<int> KI_POLY_POINT;
+
+/**
+ * KI_POLYGON_WITH_HOLES defines a single polygon with holes
+ * When holes are created in a KI_POLYGON_WITH_HOLES, they are
+ * stored as separate single polygons,
+ * KI_POLYGON_WITH_HOLES store always one polygon for the external outline
+ * and one list of polygons (holes) which can be empty
+ */
+typedef bpl::polygon_with_holes_data<int> KI_POLYGON_WITH_HOLES;
+
+/**
+ * KI_POLYGON_WITH_HOLES_SET defines a set of KI_POLYGON_WITH_HOLES.
+ * A KI_POLYGON_WITH_HOLES_SET is used to store a set of polygons with holes
+ * when performing operations between 2 polygons
+ * or 2 sets of polygons with holes
+ * The result of operations like and, xor... between 2 polygons with holes
+ * is always stored in a KI_POLYGON_WITH_HOLES_SET, because these operations
+ * can create many separate polygons with holespolygons
+ */
+typedef std::vector<KI_POLYGON_WITH_HOLES> KI_POLYGON_WITH_HOLES_SET;
+
+
+#endif // #ifndef _POLYGONS_DEFS_H_