Pcbnew: very minor fixes and update: update clipper version. uncrustify polytri/* and fix some warning compil.
This commit is contained in:
parent
6e56aa2ff0
commit
eb22bf426c
|
@ -13,7 +13,7 @@
|
|||
#ifndef DRW_BASE_H
|
||||
#define DRW_BASE_H
|
||||
|
||||
#define DRW_VERSION "0.5.10"
|
||||
#define DRW_VERSION "0.5.11"
|
||||
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
|
|
@ -27,7 +27,7 @@ class dxfRW
|
|||
public:
|
||||
dxfRW( const char* name );
|
||||
~dxfRW();
|
||||
// / reads the file specified in constructor
|
||||
/// reads the file specified in constructor
|
||||
/*!
|
||||
* An interface must be provided. It is used by the class to signal various
|
||||
* components being added.
|
||||
|
@ -71,7 +71,7 @@ public:
|
|||
|
||||
void setEllipseParts( int parts ) { elParts = parts; } /*!< set parts munber when convert ellipse to polyline */
|
||||
private:
|
||||
// / used by read() to parse the content of the file
|
||||
/// used by read() to parse the content of the file
|
||||
bool processDxf();
|
||||
bool processHeader();
|
||||
bool processTables();
|
||||
|
|
|
@ -88,8 +88,8 @@ CPolyLine::~CPolyLine()
|
|||
#include "clipper.hpp"
|
||||
int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
|
||||
{
|
||||
ClipperLib::Polygon raw_polygon;
|
||||
ClipperLib::Polygons normalized_polygons;
|
||||
ClipperLib::Path raw_polygon;
|
||||
ClipperLib::Paths normalized_polygons;
|
||||
|
||||
unsigned corners_count = m_CornersList.GetCornersCount();
|
||||
|
||||
|
@ -115,7 +115,7 @@ int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
|
|||
// enter main outline
|
||||
for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ )
|
||||
{
|
||||
ClipperLib::Polygon& polygon = normalized_polygons[ii];
|
||||
ClipperLib::Path& polygon = normalized_polygons[ii];
|
||||
cornerslist.clear();
|
||||
for( unsigned jj = 0; jj < polygon.size(); jj++ )
|
||||
cornerslist.push_back( KI_POLY_POINT( KiROUND( polygon[jj].X ),
|
||||
|
@ -142,7 +142,7 @@ int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
|
|||
ClipperLib::SimplifyPolygon( raw_polygon, normalized_polygons );
|
||||
for( unsigned ii = 0; ii < normalized_polygons.size(); ii++ )
|
||||
{
|
||||
ClipperLib::Polygon& polygon = normalized_polygons[ii];
|
||||
ClipperLib::Path& polygon = normalized_polygons[ii];
|
||||
cornerslist.clear();
|
||||
for( unsigned jj = 0; jj < polygon.size(); jj++ )
|
||||
cornerslist.push_back( KI_POLY_POINT( KiROUND( polygon[jj].X ),
|
||||
|
|
4703
polygon/clipper.cpp
4703
polygon/clipper.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +1,8 @@
|
|||
/*******************************************************************************
|
||||
* *
|
||||
* Author : Angus Johnson *
|
||||
* Version : 5.1.4 *
|
||||
* Date : 24 March 2013 *
|
||||
* Version : 6.1.2 *
|
||||
* Date : 15 December 2013 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2013 *
|
||||
* *
|
||||
|
@ -34,11 +34,29 @@
|
|||
#ifndef clipper_hpp
|
||||
#define clipper_hpp
|
||||
|
||||
#define CLIPPER_VERSION "6.1.2"
|
||||
|
||||
//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 support for the obsolete OffsetPaths() function
|
||||
//which has been replace with the ClipperOffset class.
|
||||
#define use_deprecated
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
|
||||
namespace ClipperLib {
|
||||
|
||||
|
@ -50,23 +68,64 @@ enum PolyType { ptSubject, ptClip };
|
|||
//see http://glprogramming.com/red/chapter11.html
|
||||
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
|
||||
|
||||
typedef signed long long long64;
|
||||
typedef unsigned long long ulong64;
|
||||
#ifdef use_int32
|
||||
typedef int cInt;
|
||||
typedef unsigned int cUInt;
|
||||
#else
|
||||
typedef signed long long cInt;
|
||||
typedef unsigned long long cUInt;
|
||||
#endif
|
||||
|
||||
struct IntPoint {
|
||||
public:
|
||||
long64 X;
|
||||
long64 Y;
|
||||
IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {};
|
||||
friend std::ostream& operator <<(std::ostream &s, IntPoint &p);
|
||||
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 > Polygon;
|
||||
typedef std::vector< Polygon > Polygons;
|
||||
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, Polygon &p);
|
||||
std::ostream& operator <<(std::ostream &s, Polygons &p);
|
||||
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 (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
|
||||
#endif
|
||||
|
||||
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
|
||||
enum JoinType {jtSquare, jtRound, jtMiter};
|
||||
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
|
||||
#ifdef use_deprecated
|
||||
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
|
||||
#endif
|
||||
|
||||
class PolyNode;
|
||||
typedef std::vector< PolyNode* > PolyNodes;
|
||||
|
@ -75,17 +134,22 @@ class PolyNode
|
|||
{
|
||||
public:
|
||||
PolyNode();
|
||||
Polygon Contour;
|
||||
Path Contour;
|
||||
PolyNodes Childs;
|
||||
PolyNode* Parent;
|
||||
PolyNode* GetNext() const;
|
||||
bool IsHole() const;
|
||||
bool IsOpen() const;
|
||||
int ChildCount() const;
|
||||
private:
|
||||
PolyNode* GetNextSiblingUp() const;
|
||||
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
|
||||
|
@ -100,112 +164,54 @@ private:
|
|||
friend class Clipper; //to access AllNodes
|
||||
};
|
||||
|
||||
enum JoinType { jtSquare, jtRound, jtMiter };
|
||||
bool Orientation(const Path &poly);
|
||||
double Area(const Path &poly);
|
||||
|
||||
bool Orientation(const Polygon &poly);
|
||||
double Area(const Polygon &poly);
|
||||
#ifdef use_deprecated
|
||||
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
|
||||
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
|
||||
#endif
|
||||
|
||||
void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
|
||||
double delta, JoinType jointype = jtSquare, double limit = 0, bool autoFix = true);
|
||||
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 SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(Polygons &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 CleanPolygon(Polygon& in_poly, Polygon& out_poly, double distance = 1.415);
|
||||
void CleanPolygons(Polygons& in_polys, Polygons& out_polys, double distance = 1.415);
|
||||
void MinkowskiSum(const Path& poly, const Path& path, Paths& solution, bool isClosed);
|
||||
void MinkowskiDiff(const Path& poly, const Path& path, Paths& solution, bool isClosed);
|
||||
|
||||
void PolyTreeToPolygons(PolyTree& polytree, Polygons& polygons);
|
||||
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
|
||||
void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
|
||||
void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
|
||||
|
||||
void ReversePolygon(Polygon& p);
|
||||
void ReversePolygons(Polygons& p);
|
||||
void ReversePath(Path& p);
|
||||
void ReversePaths(Paths& p);
|
||||
|
||||
//used internally ...
|
||||
struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
|
||||
|
||||
//enums that are used internally ...
|
||||
enum EdgeSide { esLeft = 1, esRight = 2};
|
||||
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 deltaX;
|
||||
long64 deltaY;
|
||||
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;
|
||||
};
|
||||
|
||||
struct IntersectNode {
|
||||
TEdge *edge1;
|
||||
TEdge *edge2;
|
||||
IntPoint pt;
|
||||
IntersectNode *next;
|
||||
};
|
||||
|
||||
struct LocalMinima {
|
||||
long64 Y;
|
||||
TEdge *leftBound;
|
||||
TEdge *rightBound;
|
||||
LocalMinima *next;
|
||||
};
|
||||
|
||||
struct Scanbeam {
|
||||
long64 Y;
|
||||
Scanbeam *next;
|
||||
};
|
||||
|
||||
struct OutPt; //forward declaration
|
||||
|
||||
struct OutRec {
|
||||
int idx;
|
||||
bool isHole;
|
||||
OutRec *FirstLeft; //see comments in clipper.pas
|
||||
PolyNode *polyNode;
|
||||
OutPt *pts;
|
||||
OutPt *bottomPt;
|
||||
};
|
||||
|
||||
struct OutPt {
|
||||
int idx;
|
||||
IntPoint pt;
|
||||
OutPt *next;
|
||||
OutPt *prev;
|
||||
};
|
||||
|
||||
struct JoinRec {
|
||||
IntPoint pt1a;
|
||||
IntPoint pt1b;
|
||||
int poly1Idx;
|
||||
IntPoint pt2a;
|
||||
IntPoint pt2b;
|
||||
int poly2Idx;
|
||||
};
|
||||
|
||||
struct HorzJoinRec {
|
||||
TEdge *edge;
|
||||
int savedIdx;
|
||||
};
|
||||
|
||||
struct IntRect { long64 left; long64 top; long64 right; long64 bottom; };
|
||||
//forward declarations (for stuff used internally) ...
|
||||
struct TEdge;
|
||||
struct IntersectNode;
|
||||
struct LocalMinima;
|
||||
struct Scanbeam;
|
||||
struct OutPt;
|
||||
struct OutRec;
|
||||
struct Join;
|
||||
|
||||
typedef std::vector < OutRec* > PolyOutList;
|
||||
typedef std::vector < TEdge* > EdgeList;
|
||||
typedef std::vector < JoinRec* > JoinList;
|
||||
typedef std::vector < HorzJoinRec* > HorzJoinList;
|
||||
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
|
||||
|
@ -215,29 +221,38 @@ class ClipperBase
|
|||
public:
|
||||
ClipperBase();
|
||||
virtual ~ClipperBase();
|
||||
bool AddPolygon(const Polygon &pg, PolyType polyType);
|
||||
bool AddPolygons( const Polygons &ppg, PolyType polyType);
|
||||
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);
|
||||
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
|
||||
void PopLocalMinima();
|
||||
virtual void Reset();
|
||||
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
|
||||
void InsertLocalMinima(LocalMinima *newLm);
|
||||
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
|
||||
TEdge* DescendToMin(TEdge *&E);
|
||||
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
|
||||
LocalMinima *m_CurrentLM;
|
||||
LocalMinima *m_MinimaList;
|
||||
bool m_UseFullRange;
|
||||
EdgeList m_edges;
|
||||
bool m_PreserveCollinear;
|
||||
bool m_HasOpenPaths;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class Clipper : public virtual ClipperBase
|
||||
{
|
||||
public:
|
||||
Clipper();
|
||||
Clipper(int initOptions = 0);
|
||||
~Clipper();
|
||||
bool Execute(ClipType clipType,
|
||||
Polygons &solution,
|
||||
Paths &solution,
|
||||
PolyFillType subjFillType = pftEvenOdd,
|
||||
PolyFillType clipFillType = pftEvenOdd);
|
||||
bool Execute(ClipType clipType,
|
||||
|
@ -247,31 +262,40 @@ public:
|
|||
void Clear();
|
||||
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(TZFillCallback zFillFunc);
|
||||
#endif
|
||||
protected:
|
||||
void Reset();
|
||||
virtual bool ExecuteInternal();
|
||||
private:
|
||||
PolyOutList m_PolyOuts;
|
||||
JoinList m_Joins;
|
||||
HorzJoinList m_HorizJoins;
|
||||
JoinList m_GhostJoins;
|
||||
IntersectList m_IntersectList;
|
||||
ClipType m_ClipType;
|
||||
Scanbeam *m_Scanbeam;
|
||||
std::set< cInt, std::greater<cInt> > m_Scanbeam;
|
||||
TEdge *m_ActiveEdges;
|
||||
TEdge *m_SortedEdges;
|
||||
IntersectNode *m_IntersectNodes;
|
||||
bool m_ExecuteLocked;
|
||||
PolyFillType m_ClipFillType;
|
||||
PolyFillType m_SubjFillType;
|
||||
bool m_ReverseOutput;
|
||||
bool m_UsingPolyTree;
|
||||
void DisposeScanbeamList();
|
||||
bool m_StrictSimple;
|
||||
#ifdef use_xyz
|
||||
TZFillCallback m_ZFill; //custom callback
|
||||
#endif
|
||||
void SetWindingCount(TEdge& edge);
|
||||
bool IsEvenOddFillType(const TEdge& edge) const;
|
||||
bool IsEvenOddAltFillType(const TEdge& edge) const;
|
||||
void InsertScanbeam(const long64 Y);
|
||||
long64 PopScanbeam();
|
||||
void InsertLocalMinimaIntoAEL(const long64 botY);
|
||||
void InsertEdgeIntoAEL(TEdge *edge);
|
||||
void InsertScanbeam(const cInt Y);
|
||||
cInt PopScanbeam();
|
||||
void InsertLocalMinimaIntoAEL(const cInt botY);
|
||||
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
|
||||
void AddEdgeToSEL(TEdge *edge);
|
||||
void CopyAELToSEL();
|
||||
void DeleteFromSEL(TEdge *e);
|
||||
|
@ -279,48 +303,79 @@ private:
|
|||
void UpdateEdgeIntoAEL(TEdge *&e);
|
||||
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
|
||||
bool IsContributing(const TEdge& edge) const;
|
||||
bool IsTopHorz(const long64 XPos);
|
||||
bool IsTopHorz(const cInt XPos);
|
||||
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
|
||||
void DoMaxima(TEdge *e, long64 topY);
|
||||
void ProcessHorizontals();
|
||||
void ProcessHorizontal(TEdge *horzEdge);
|
||||
void DoMaxima(TEdge *e);
|
||||
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
|
||||
void ProcessHorizontals(bool IsTopOfScanbeam);
|
||||
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
|
||||
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
void AddLocalMinPoly(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 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, const IntersectProtects protects);
|
||||
const IntPoint &pt, bool protect = false);
|
||||
OutRec* CreateOutRec();
|
||||
void AddOutPt(TEdge *e, const IntPoint &pt);
|
||||
void DisposeAllPolyPts();
|
||||
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
|
||||
void DisposeAllOutRecs();
|
||||
void DisposeOutRec(PolyOutList::size_type index);
|
||||
bool ProcessIntersections(const long64 botY, const long64 topY);
|
||||
void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
void BuildIntersectList(const long64 botY, const long64 topY);
|
||||
bool ProcessIntersections(const cInt botY, const cInt topY);
|
||||
void BuildIntersectList(const cInt botY, const cInt topY);
|
||||
void ProcessIntersectList();
|
||||
void ProcessEdgesAtTopOfScanbeam(const long64 topY);
|
||||
void BuildResult(Polygons& polys);
|
||||
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
|
||||
void BuildResult(Paths& polys);
|
||||
void BuildResult2(PolyTree& polytree);
|
||||
void SetHoleState(TEdge *e, OutRec *OutRec);
|
||||
void SetHoleState(TEdge *e, OutRec *outrec);
|
||||
void DisposeIntersectNodes();
|
||||
bool FixupIntersectionOrder();
|
||||
void FixupOutPolygon(OutRec &outRec);
|
||||
void FixupOutPolygon(OutRec &outrec);
|
||||
bool IsHole(TEdge *e);
|
||||
void FixHoleLinkage(OutRec &outRec);
|
||||
void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1);
|
||||
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
|
||||
void FixHoleLinkage(OutRec &outrec);
|
||||
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
|
||||
void ClearJoins();
|
||||
void AddHorzJoin(TEdge *e, int idx);
|
||||
void ClearHorzJoins();
|
||||
bool JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2);
|
||||
void FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx);
|
||||
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* OldOutRec, OutRec* NewOutRec);
|
||||
#ifdef use_xyz
|
||||
void SetZ(IntPoint& pt, TEdge& e);
|
||||
#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
|
||||
|
|
|
@ -32,63 +32,74 @@
|
|||
#include <iostream>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
Triangle::Triangle(Point& a, Point& b, Point& c)
|
||||
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;
|
||||
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)
|
||||
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);
|
||||
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)
|
||||
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);
|
||||
}
|
||||
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++ )
|
||||
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;
|
||||
points_[0] = points_[1] = points_[2] = NULL;
|
||||
}
|
||||
|
||||
void Triangle::ClearNeighbor(Triangle *triangle )
|
||||
|
||||
void Triangle::ClearNeighbor( Triangle* triangle )
|
||||
{
|
||||
if( neighbors_[0] == triangle )
|
||||
{
|
||||
|
@ -104,263 +115,379 @@ void Triangle::ClearNeighbor(Triangle *triangle )
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void Triangle::ClearNeighbors()
|
||||
{
|
||||
neighbors_[0] = NULL;
|
||||
neighbors_[1] = NULL;
|
||||
neighbors_[2] = NULL;
|
||||
neighbors_[0] = NULL;
|
||||
neighbors_[1] = NULL;
|
||||
neighbors_[2] = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Triangle::ClearDelunayEdges()
|
||||
{
|
||||
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
|
||||
delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
|
||||
}
|
||||
|
||||
Point* Triangle::OppositePoint(Triangle& t, Point& p)
|
||||
|
||||
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);
|
||||
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)
|
||||
void Triangle::Legalize( Point& point )
|
||||
{
|
||||
points_[1] = points_[0];
|
||||
points_[0] = points_[2];
|
||||
points_[2] = &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)
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
if( &opoint == points_[0] )
|
||||
{
|
||||
points_[1] = points_[0];
|
||||
points_[0] = points_[2];
|
||||
points_[2] = &npoint;
|
||||
}
|
||||
} else if (points_[1] == p1) {
|
||||
if (points_[2] == p2) {
|
||||
return 0;
|
||||
} else if (points_[0] == p2) {
|
||||
return 2;
|
||||
else if( &opoint == points_[1] )
|
||||
{
|
||||
points_[2] = points_[1];
|
||||
points_[1] = points_[0];
|
||||
points_[0] = &npoint;
|
||||
}
|
||||
} else if (points_[2] == p1) {
|
||||
if (points_[0] == p2) {
|
||||
return 1;
|
||||
} else if (points_[1] == p2) {
|
||||
return 0;
|
||||
else if( &opoint == points_[2] )
|
||||
{
|
||||
points_[0] = points_[2];
|
||||
points_[2] = points_[1];
|
||||
points_[1] = &npoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( 0 );
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Triangle::MarkConstrainedEdge(const int index)
|
||||
|
||||
int Triangle::Index( const Point* p )
|
||||
{
|
||||
constrained_edge[index] = true;
|
||||
if( p == points_[0] )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if( p == points_[1] )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if( p == points_[2] )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
void Triangle::MarkConstrainedEdge(Edge& edge)
|
||||
|
||||
int Triangle::EdgeIndex( const Point* p1, const Point* p2 )
|
||||
{
|
||||
MarkConstrainedEdge(edge.p, edge.q);
|
||||
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)
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// The point counter-clockwise to given point
|
||||
Point* Triangle::PointCCW(Point& point)
|
||||
Point* Triangle::PointCW( 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);
|
||||
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 );
|
||||
}
|
||||
|
||||
|
||||
// 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 );
|
||||
}
|
||||
|
||||
|
||||
// The neighbor clockwise to given point
|
||||
Triangle* Triangle::NeighborCW(Point& point)
|
||||
Triangle* Triangle::NeighborCW( Point& point )
|
||||
{
|
||||
if (&point == points_[0]) {
|
||||
return neighbors_[1];
|
||||
} else if (&point == points_[1]) {
|
||||
return neighbors_[2];
|
||||
}
|
||||
return neighbors_[0];
|
||||
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)
|
||||
Triangle* Triangle::NeighborCCW( Point& point )
|
||||
{
|
||||
if (&point == points_[0]) {
|
||||
return neighbors_[2];
|
||||
} else if (&point == points_[1]) {
|
||||
return neighbors_[0];
|
||||
}
|
||||
return neighbors_[1];
|
||||
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)
|
||||
bool Triangle::GetConstrainedEdgeCCW( Point& p )
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return constrained_edge[2];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return constrained_edge[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)
|
||||
|
||||
bool Triangle::GetConstrainedEdgeCW( Point& p )
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
constrained_edge[2] = ce;
|
||||
} else if (&p == points_[1]) {
|
||||
constrained_edge[0] = ce;
|
||||
} else {
|
||||
constrained_edge[1] = ce;
|
||||
}
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return constrained_edge[1];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return constrained_edge[2];
|
||||
}
|
||||
|
||||
return constrained_edge[0];
|
||||
}
|
||||
|
||||
void Triangle::SetConstrainedEdgeCW(Point& p, bool ce)
|
||||
|
||||
void Triangle::SetConstrainedEdgeCCW( 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;
|
||||
}
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
constrained_edge[2] = ce;
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
constrained_edge[0] = ce;
|
||||
}
|
||||
else
|
||||
{
|
||||
constrained_edge[1] = ce;
|
||||
}
|
||||
}
|
||||
|
||||
bool Triangle::GetDelunayEdgeCCW(Point& p)
|
||||
|
||||
void Triangle::SetConstrainedEdgeCW( Point& p, bool ce )
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
return delaunay_edge[2];
|
||||
} else if (&p == points_[1]) {
|
||||
return delaunay_edge[0];
|
||||
}
|
||||
return delaunay_edge[1];
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
constrained_edge[1] = ce;
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
constrained_edge[2] = ce;
|
||||
}
|
||||
else
|
||||
{
|
||||
constrained_edge[0] = ce;
|
||||
}
|
||||
}
|
||||
|
||||
bool Triangle::GetDelunayEdgeCW(Point& p)
|
||||
|
||||
bool Triangle::GetDelunayEdgeCCW( Point& p )
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return delaunay_edge[2];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return delaunay_edge[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)
|
||||
|
||||
bool Triangle::GetDelunayEdgeCW( Point& p )
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
delaunay_edge[2] = e;
|
||||
} else if (&p == points_[1]) {
|
||||
delaunay_edge[0] = e;
|
||||
} else {
|
||||
delaunay_edge[1] = e;
|
||||
}
|
||||
if( &p == points_[0] )
|
||||
{
|
||||
return delaunay_edge[1];
|
||||
}
|
||||
else if( &p == points_[1] )
|
||||
{
|
||||
return delaunay_edge[2];
|
||||
}
|
||||
|
||||
return delaunay_edge[0];
|
||||
}
|
||||
|
||||
void Triangle::SetDelunayEdgeCW(Point& p, bool e)
|
||||
|
||||
void Triangle::SetDelunayEdgeCCW( 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;
|
||||
}
|
||||
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)
|
||||
Triangle& Triangle::NeighborAcross( Point& opoint )
|
||||
{
|
||||
if (&opoint == points_[0]) {
|
||||
return *neighbors_[0];
|
||||
} else if (&opoint == points_[1]) {
|
||||
return *neighbors_[1];
|
||||
}
|
||||
return *neighbors_[2];
|
||||
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";
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -39,287 +39,313 @@
|
|||
#include <cmath>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
struct Edge;
|
||||
|
||||
struct Point {
|
||||
struct Point
|
||||
{
|
||||
double x, y;
|
||||
|
||||
double x, y;
|
||||
/// Default constructor does nothing (for performance).
|
||||
Point()
|
||||
{
|
||||
x = 0.0;
|
||||
y = 0.0;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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 ) {}
|
||||
|
||||
/// 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 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_;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Negate this point.
|
||||
Point operator -() const
|
||||
{
|
||||
Point v;
|
||||
v.set(-x, -y);
|
||||
return v;
|
||||
}
|
||||
v.set( -x, -y );
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Add a point to this point.
|
||||
void operator +=(const Point& v)
|
||||
{
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
}
|
||||
/// 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;
|
||||
}
|
||||
/// 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;
|
||||
}
|
||||
/// 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);
|
||||
}
|
||||
/// 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;
|
||||
}
|
||||
/// 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 {
|
||||
struct Edge
|
||||
{
|
||||
Point* p, * q;
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 );
|
||||
}
|
||||
|
||||
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 {
|
||||
// "Triangulations in CGAL"
|
||||
class Triangle
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
Triangle(Point& a, Point& b, Point& c);
|
||||
Triangle( Point& a, Point& b, Point& c );
|
||||
|
||||
/// Flags to determine if an edge is a Constrained edge
|
||||
bool constrained_edge[3];
|
||||
bool constrained_edge[3];
|
||||
/// Flags to determine if an edge is a Delauney edge
|
||||
bool delaunay_edge[3];
|
||||
bool delaunay_edge[3];
|
||||
|
||||
Point* GetPoint(const int& index);
|
||||
Point* PointCW(Point& point);
|
||||
Point* PointCCW(Point& point);
|
||||
Point* OppositePoint(Triangle& t, Point& p);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 );
|
||||
|
||||
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();
|
||||
void Clear();
|
||||
void ClearNeighbor( Triangle* triangle );
|
||||
void ClearNeighbors();
|
||||
void ClearDelunayEdges();
|
||||
|
||||
inline bool IsInterior();
|
||||
inline void IsInterior(bool b);
|
||||
inline bool IsInterior();
|
||||
inline void IsInterior( bool b );
|
||||
|
||||
Triangle& NeighborAcross(Point& opoint);
|
||||
Triangle& NeighborAcross( Point& opoint );
|
||||
|
||||
void DebugPrint();
|
||||
void DebugPrint();
|
||||
|
||||
private:
|
||||
|
||||
/// Triangle points
|
||||
Point* points_[3];
|
||||
Point* points_[3];
|
||||
/// Neighbor list
|
||||
Triangle* neighbors_[3];
|
||||
Triangle* neighbors_[3];
|
||||
|
||||
/// Has this triangle been marked as an interior triangle?
|
||||
bool interior_;
|
||||
bool interior_;
|
||||
};
|
||||
|
||||
inline bool cmp(const Point* a, const Point* b)
|
||||
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;
|
||||
if( a->y < b->y )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
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)
|
||||
inline Point operator +( const Point& a, const Point& b )
|
||||
{
|
||||
return Point(a.x + b.x, a.y + b.y);
|
||||
return Point( a.x + b.x, a.y + b.y );
|
||||
}
|
||||
|
||||
|
||||
/// Subtract two points_ component-wise.
|
||||
inline Point operator -(const Point& a, const Point& b)
|
||||
inline Point operator -( const Point& a, const Point& b )
|
||||
{
|
||||
return Point(a.x - b.x, a.y - b.y);
|
||||
return Point( a.x - b.x, a.y - b.y );
|
||||
}
|
||||
|
||||
|
||||
/// Multiply point by scalar
|
||||
inline Point operator *(double s, const Point& a)
|
||||
inline Point operator *( double s, const Point& a )
|
||||
{
|
||||
return Point(s * a.x, s * a.y);
|
||||
return Point( s * a.x, s * a.y );
|
||||
}
|
||||
|
||||
inline bool operator ==(const Point& a, const Point& b)
|
||||
|
||||
inline bool operator ==( const Point& a, const Point& b )
|
||||
{
|
||||
return a.x == b.x && a.y == b.y;
|
||||
return a.x == b.x && a.y == b.y;
|
||||
}
|
||||
|
||||
inline bool operator !=(const Point& a, const Point& b)
|
||||
|
||||
inline bool operator !=( const Point& a, const Point& b )
|
||||
{
|
||||
return !(a.x == b.x) && !(a.y == b.y);
|
||||
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)
|
||||
inline double Dot( const Point& a, const Point& b )
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
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)
|
||||
inline double Cross( const Point& a, const Point& b )
|
||||
{
|
||||
return a.x * b.y - a.y * b.x;
|
||||
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)
|
||||
inline Point Cross( const Point& a, double s )
|
||||
{
|
||||
return Point(s * a.y, -s * a.x);
|
||||
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)
|
||||
inline Point Cross( const double s, const Point& a )
|
||||
{
|
||||
return Point(-s * a.y, s * a.x);
|
||||
return Point( -s * a.y, s * a.x );
|
||||
}
|
||||
|
||||
inline Point* Triangle::GetPoint(const int& index)
|
||||
|
||||
inline Point* Triangle::GetPoint( const int& index )
|
||||
{
|
||||
return points_[index];
|
||||
return points_[index];
|
||||
}
|
||||
|
||||
inline Triangle* Triangle::GetNeighbor(const int& index)
|
||||
|
||||
inline Triangle* Triangle::GetNeighbor( const int& index )
|
||||
{
|
||||
return neighbors_[index];
|
||||
return neighbors_[index];
|
||||
}
|
||||
|
||||
inline bool Triangle::Contains(Point* p)
|
||||
|
||||
inline bool Triangle::Contains( Point* p )
|
||||
{
|
||||
return p == points_[0] || p == points_[1] || p == points_[2];
|
||||
return p == points_[0] || p == points_[1] || p == points_[2];
|
||||
}
|
||||
|
||||
inline bool Triangle::Contains(const Edge& e)
|
||||
|
||||
inline bool Triangle::Contains( const Edge& e )
|
||||
{
|
||||
return Contains(e.p) && Contains(e.q);
|
||||
return Contains( e.p ) && Contains( e.q );
|
||||
}
|
||||
|
||||
inline bool Triangle::Contains(Point* p, Point* q)
|
||||
|
||||
inline bool Triangle::Contains( Point* p, Point* q )
|
||||
{
|
||||
return Contains(p) && Contains(q);
|
||||
return Contains( p ) && Contains( q );
|
||||
}
|
||||
|
||||
|
||||
inline bool Triangle::IsInterior()
|
||||
{
|
||||
return interior_;
|
||||
return interior_;
|
||||
}
|
||||
|
||||
inline void Triangle::IsInterior(bool b)
|
||||
|
||||
inline void Triangle::IsInterior( bool b )
|
||||
{
|
||||
interior_ = b;
|
||||
interior_ = b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -39,12 +39,13 @@
|
|||
#include <math.h>
|
||||
|
||||
namespace p2t {
|
||||
const double PI_3div4 = 3 * M_PI / 4;
|
||||
const double PI_div2 = 1.57079632679489661923;
|
||||
const double EPSILON = 1e-12;
|
||||
|
||||
const double PI_3div4 = 3 * M_PI / 4;
|
||||
const double PI_div2 = 1.57079632679489661923;
|
||||
const double EPSILON = 1e-12;
|
||||
|
||||
enum Orientation { CW, CCW, COLLINEAR };
|
||||
enum Orientation {
|
||||
CW, CCW, COLLINEAR
|
||||
};
|
||||
|
||||
/**
|
||||
* Forumla to calculate signed area<br>
|
||||
|
@ -56,68 +57,77 @@ enum Orientation { CW, CCW, COLLINEAR };
|
|||
* = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
|
||||
* </pre>
|
||||
*/
|
||||
Orientation Orient2d(Point& pa, Point& pb, Point& pc)
|
||||
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;
|
||||
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)
|
||||
* 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 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 oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y);
|
||||
|
||||
double adxbdy = adx * bdy;
|
||||
double bdxady = bdx * ady;
|
||||
double oabd = adxbdy - bdxady;
|
||||
if( oadb >= -EPSILON )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oabd <= EPSILON) {
|
||||
return false;
|
||||
}
|
||||
double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y);
|
||||
|
||||
double cdx = pc.x - pdx;
|
||||
double cdy = pc.y - pdy;
|
||||
if( oadc <= EPSILON )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double cdxady = cdx * ady;
|
||||
double adxcdy = adx * cdy;
|
||||
double ocad = cdxady - adxcdy;
|
||||
|
||||
if (ocad <= EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in New Issue