clipper.hpp   clipper.hpp 
/************************************************************************** ***** /************************************************************************** *****
* * * *
* Author : Angus Johnson * * Author : Angus Johnson *
* Version : 6.2.1 * Version : 4.8.9
* *
* Date : 31 October 2014 * Date : 25 September 2012
* *
* Website : http://www.angusj.com * * Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 * * Copyright : Angus Johnson 2010-2012 *
* * * *
* License: * * License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. * * Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt * * http://www.boost.org/LICENSE_1_0.txt *
* * * *
* Attributions: * * Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorit hm: * * The code in this library is an extension of Bala Vatti's clipping algorit hm: *
* "A generic solution to polygon clipping" * * "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 * * http://portal.acm.org/citation.cfm?id=129906 *
skipping to change at line 29 skipping to change at line 29
* Computer graphics and geometric modeling: implementation and algorithms * * Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston * * By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) * * Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston * * http://books.google.com/books?q=vatti+clipping+agoston *
* * * *
* See also: * * See also: *
* "Polygon Offsetting by Computing Winding Numbers" * * "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 * * Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences * * ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) * * and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA * * September 2428, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* * * *
*************************************************************************** ****/ *************************************************************************** ****/
#ifndef clipper_hpp #ifndef clipper_hpp
#define clipper_hpp #define clipper_hpp
#define CLIPPER_VERSION "6.2.0"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46
340
//#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 <vector>
#include <set>
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <ostream> #include <ostream>
#include <functional>
#include <queue>
namespace ClipperLib { namespace ClipperLib {
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip }; enum PolyType { ptSubject, ptClip };
//By far the most widely used winding rules for polygon filling are //By far the most widely used winding rules for polygon filling are
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32 ) //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32 )
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenG L) //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenG L)
//see http://glprogramming.com/red/chapter11.html //see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32 typedef signed long long long64;
typedef int cInt; typedef unsigned long long ulong64;
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 { struct IntPoint {
cInt X; public:
cInt Y; long64 X;
#ifdef use_xyz long64 Y;
cInt Z; IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {};
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; friend std::ostream& operator <<(std::ostream &s, IntPoint &p);
#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< IntPoint > Polygon;
typedef std::vector< Path > Paths; typedef std::vector< Polygon > Polygons;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); std::ostream& operator <<(std::ostream &s, Polygon &p);
return poly;} std::ostream& operator <<(std::ostream &s, Polygons &p);
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p);
return polys;}
std::ostream& operator <<(std::ostream &s, const IntPoint &p); struct ExPolygon {
std::ostream& operator <<(std::ostream &s, const Path &p); Polygon outer;
std::ostream& operator <<(std::ostream &s, const Paths &p); Polygons holes;
};
typedef std::vector< ExPolygon > ExPolygons;
struct DoublePoint enum JoinType { jtSquare, jtRound, jtMiter };
{
double X; bool Orientation(const Polygon &poly);
double Y; double Area(const Polygon &poly);
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} double delta, JoinType jointype = jtSquare, double MiterLimit = 2);
void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillT
ype fillType = pftEvenOdd);
void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFi
llType fillType = pftEvenOdd);
void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd);
void ReversePolygon(Polygon& p);
void ReversePolygons(Polygons& p);
//used internally ...
enum EdgeSide { esNeither = 0, esLeft = 1, esRight = 2, esBoth = 3 };
enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 };
struct TEdge {
long64 xbot;
long64 ybot;
long64 xcurr;
long64 ycurr;
long64 xtop;
long64 ytop;
double dx;
long64 tmpX;
PolyType polyType;
EdgeSide side;
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;
}; };
//------------------------------------------------------------------------- -----
#ifdef use_xyz struct IntersectNode {
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e TEdge *edge1;
2bot, IntPoint& e2top, IntPoint& pt); TEdge *edge2;
#endif IntPoint pt;
IntersectNode *next;
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCo };
llinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOp
enRound};
class PolyNode; struct LocalMinima {
typedef std::vector< PolyNode* > PolyNodes; long64 Y;
TEdge *leftBound;
TEdge *rightBound;
LocalMinima *next;
};
class PolyNode struct Scanbeam {
{ long64 Y;
public: Scanbeam *next;
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 struct OutPt; //forward declaration
{
public: struct OutRec {
~PolyTree(){Clear();}; int idx;
PolyNode* GetFirst() const; bool isHole;
void Clear(); OutRec *FirstLeft;
int Total() const; OutRec *AppendLink;
private: OutPt *pts;
PolyNodes AllNodes; OutPt *bottomPt;
friend class Clipper; //to access AllNodes OutPt *bottomFlag;
EdgeSide sides;
};
struct OutPt {
int idx;
IntPoint pt;
OutPt *next;
OutPt *prev;
};
struct JoinRec {
IntPoint pt1a;
IntPoint pt1b;
int poly1Idx;
IntPoint pt2a;
IntPoint pt2b;
int poly2Idx;
}; };
bool Orientation(const Path &poly); struct HorzJoinRec {
double Area(const Path &poly); TEdge *edge;
int PointInPolygon(const IntPoint &pt, const Path &path); int savedIdx;
};
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fi
llType = pftEvenOdd); struct IntRect { long64 left; long64 top; long64 right; long64 bottom; };
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, b
ool 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 Scanbeam;
struct OutPt;
struct OutRec;
struct Join;
typedef std::vector < OutRec* > PolyOutList; typedef std::vector < OutRec* > PolyOutList;
typedef std::vector < TEdge* > EdgeList; typedef std::vector < TEdge* > EdgeList;
typedef std::vector < Join* > JoinList; typedef std::vector < JoinRec* > JoinList;
typedef std::vector < IntersectNode* > IntersectList; typedef std::vector < HorzJoinRec* > HorzJoinList;
//-------------------------------------------------------------------------
-----
//ClipperBase is the ancestor to the Clipper class. It should not be //ClipperBase is the ancestor to the Clipper class. It should not be
//instantiated directly. This class simply abstracts the conversion of sets of //instantiated directly. This class simply abstracts the conversion of sets of
//polygon coordinates into edge objects that are stored in a LocalMinima li st. //polygon coordinates into edge objects that are stored in a LocalMinima li st.
class ClipperBase class ClipperBase
{ {
public: public:
ClipperBase(); ClipperBase();
virtual ~ClipperBase(); virtual ~ClipperBase();
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPolygon(const Polygon &pg, PolyType polyType);
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); bool AddPolygons( const Polygons &ppg, PolyType polyType);
virtual void Clear(); virtual void Clear();
IntRect GetBounds(); IntRect GetBounds();
bool PreserveCollinear() {return m_PreserveCollinear;};
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
protected: protected:
void DisposeLocalMinimaList(); void DisposeLocalMinimaList();
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); TEdge* AddBoundsToLML(TEdge *e);
void PopLocalMinima(); void PopLocalMinima();
virtual void Reset(); virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise); void InsertLocalMinima(LocalMinima *newLm);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); LocalMinima *m_CurrentLM;
TEdge* DescendToMin(TEdge *&E); LocalMinima *m_MinimaList;
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
typedef std::vector<LocalMinimum> MinimaList;
MinimaList::iterator m_CurrentLM;
MinimaList m_MinimaList;
bool m_UseFullRange; bool m_UseFullRange;
EdgeList m_edges; EdgeList m_edges;
bool m_PreserveCollinear;
bool m_HasOpenPaths;
}; };
//------------------------------------------------------------------------- -----
class Clipper : public virtual ClipperBase class Clipper : public virtual ClipperBase
{ {
public: public:
Clipper(int initOptions = 0); Clipper();
~Clipper(); ~Clipper();
bool Execute(ClipType clipType, bool Execute(ClipType clipType,
Paths &solution, Polygons &solution,
PolyFillType subjFillType = pftEvenOdd, PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd); PolyFillType clipFillType = pftEvenOdd);
bool Execute(ClipType clipType, bool Execute(ClipType clipType,
PolyTree &polytree, ExPolygons &solution,
PolyFillType subjFillType = pftEvenOdd, PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd); PolyFillType clipFillType = pftEvenOdd);
void Clear();
bool ReverseSolution() {return m_ReverseOutput;}; bool ReverseSolution() {return m_ReverseOutput;};
void ReverseSolution(bool value) {m_ReverseOutput = value;}; 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 (otherwi
se Z is 0)
#ifdef use_xyz
void ZFillFunction(ZFillCallback zFillFunc);
#endif
protected: protected:
void Reset(); void Reset();
virtual bool ExecuteInternal(); virtual bool ExecuteInternal(bool fixHoleLinkages);
private: private:
PolyOutList m_PolyOuts; PolyOutList m_PolyOuts;
JoinList m_Joins; JoinList m_Joins;
JoinList m_GhostJoins; HorzJoinList m_HorizJoins;
IntersectList m_IntersectList;
ClipType m_ClipType; ClipType m_ClipType;
typedef std::priority_queue<cInt> ScanbeamList; Scanbeam *m_Scanbeam;
ScanbeamList m_Scanbeam;
TEdge *m_ActiveEdges; TEdge *m_ActiveEdges;
TEdge *m_SortedEdges; TEdge *m_SortedEdges;
bool m_ExecuteLocked; IntersectNode *m_IntersectNodes;
PolyFillType m_ClipFillType; bool m_ExecuteLocked;
PolyFillType m_SubjFillType; PolyFillType m_ClipFillType;
bool m_ReverseOutput; PolyFillType m_SubjFillType;
bool m_UsingPolyTree; bool m_ReverseOutput;
bool m_StrictSimple; void DisposeScanbeamList();
#ifdef use_xyz
ZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge); void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const; bool IsEvenOddFillType(const TEdge& edge) const;
bool IsEvenOddAltFillType(const TEdge& edge) const; bool IsEvenOddAltFillType(const TEdge& edge) const;
void InsertScanbeam(const cInt Y); void InsertScanbeam(const long64 Y);
cInt PopScanbeam(); long64 PopScanbeam();
void InsertLocalMinimaIntoAEL(const cInt botY); void InsertLocalMinimaIntoAEL(const long64 botY);
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); void InsertEdgeIntoAEL(TEdge *edge);
void AddEdgeToSEL(TEdge *edge); void AddEdgeToSEL(TEdge *edge);
void CopyAELToSEL(); void CopyAELToSEL();
void DeleteFromSEL(TEdge *e); void DeleteFromSEL(TEdge *e);
void DeleteFromAEL(TEdge *e); void DeleteFromAEL(TEdge *e);
void UpdateEdgeIntoAEL(TEdge *&e); void UpdateEdgeIntoAEL(TEdge *&e);
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
bool IsContributing(const TEdge& edge) const; bool IsContributing(const TEdge& edge) const;
bool IsTopHorz(const cInt XPos); bool IsTopHorz(const long64 XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e); void DoMaxima(TEdge *e, long64 topY);
void ProcessHorizontals(bool IsTopOfScanbeam); void ProcessHorizontals();
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); void ProcessHorizontal(TEdge *horzEdge);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2); void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); void DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
void DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
void DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, IntersectProtects protects);
OutRec* CreateOutRec(); OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt); void AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs(); void DisposeBottomPt(OutRec &outRec);
void DisposeAllPolyPts();
void DisposeOutRec(PolyOutList::size_type index); void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt topY); bool ProcessIntersections(const long64 botY, const long64 topY);
void BuildIntersectList(const cInt topY); void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
void BuildIntersectList(const long64 botY, const long64 topY);
void ProcessIntersectList(); void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY); void ProcessEdgesAtTopOfScanbeam(const long64 topY);
void BuildResult(Paths& polys); void BuildResult(Polygons& polys);
void BuildResult2(PolyTree& polytree); void BuildResultEx(ExPolygons& polys);
void SetHoleState(TEdge *e, OutRec *outrec); void SetHoleState(TEdge *e, OutRec *OutRec);
void DisposeIntersectNodes(); void DisposeIntersectNodes();
bool FixupIntersectionOrder(); bool FixupIntersections();
void FixupOutPolygon(OutRec &outrec); void FixupOutPolygon(OutRec &outRec);
bool IsHole(TEdge *e); bool IsHole(TEdge *e);
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); void FixHoleLinkage(OutRec *outRec);
void FixHoleLinkage(OutRec &outrec); void CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2);
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); void CheckHoleLinkages2(OutRec *outRec1, OutRec *outRec2);
void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1);
void ClearJoins(); void ClearJoins();
void ClearGhostJoins(); void AddHorzJoin(TEdge *e, int idx);
void AddGhostJoin(OutPt *op, const IntPoint offPt); void ClearHorzJoins();
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); void JoinCommonEdges(bool fixHoleLinkages);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(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 class clipperException : public std::exception
{ {
public: public:
clipperException(const char* description): m_descr(description) {} clipperException(const char* description): m_descr(description) {}
virtual ~clipperException() throw() {} virtual ~clipperException() throw() {}
virtual const char* what() const throw() {return m_descr.c_str();} virtual const char* what() const throw() {return m_descr.c_str();}
private: private:
std::string m_descr; std::string m_descr;
 End of changes. 47 change blocks. 
271 lines changed or deleted 171 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/