Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

File diff suppressed because it is too large. Load diff
+411
View File
@@ -0,0 +1,411 @@
/**
**
** @RDE_HEADER@
** Clipping and Offsetting of lines and polygons
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
** To use under Boost Software License Vers. 1
** see also http://www.angusj.com/delphi/clipper/documentation/Docs/_Body.htm
** -----------------------------------------------------------------------------------------------
*/
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.2.1 *
* Date : 31 October 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
* 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.0"
//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 <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) {}
explicit DoublePoint(const IntPoint &ip) : X(ip.X), Y(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 Scanbeam;
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();
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() const {return m_PreserveCollinear;};
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
protected:
void DisposeLocalMinimaList();
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
void PopLocalMinima();
virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
typedef std::vector<LocalMinimum> MinimaList;
MinimaList::iterator m_CurrentLM;
MinimaList m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
bool m_HasOpenPaths;
};
//------------------------------------------------------------------------------
class Clipper : public virtual ClipperBase
{
public:
explicit Clipper(int initOptions = 0);
~Clipper();
bool Execute(ClipType clipType,
Paths &solution,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool ReverseSolution() const {return m_ReverseOutput;};
void ReverseSolution(bool value) {m_ReverseOutput = value;};
bool StrictlySimple() const {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:
void Reset();
virtual bool ExecuteInternal();
private:
PolyOutList m_PolyOuts;
JoinList m_Joins;
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
typedef std::priority_queue<cInt> ScanbeamList;
ScanbeamList m_Scanbeam;
TEdge *m_ActiveEdges;
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) const;
bool IsEvenOddFillType(const TEdge& edge) const;
bool IsEvenOddAltFillType(const TEdge& edge) const;
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);
void DeleteFromAEL(TEdge *e);
void UpdateEdgeIntoAEL(TEdge *&e);
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
bool IsContributing(const TEdge& edge) const;
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void ProcessHorizontals(bool IsTopOfScanbeam);
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
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) const;
void IntersectEdges(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
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) const;
void DisposeIntersectNodes();
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
bool IsHole(TEdge *e);
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
static 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) const;
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const;
#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);
bool PathOffset( const Path& path, Path & offsetPath, double delta ); // calc offset of one path
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:
explicit 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
+736
View File
@@ -0,0 +1,736 @@
/**
**
** @RDE_HEADER@
** Rasterize functional path geometries to polylines
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 12.08.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "curve.h"
#include "path_rasterer.h"
#include <Common/exception.h>
namespace SituationMap { namespace MathUtils
{
static QPointF calcBezierVertex( double t, const QPointF control[4] )
{
double u, u2, u3, t2, t3;
double xx, yy;
t2 = t*t;
t3 = t2*t;
u = 1.0 - t;
u2 = u*u;
u3 = u2*u;
xx = control[0].x()*u3 + 3.0*(control[1].x()*t*u2 + control[2].x()*t2*u) + control[3].x()*t3;
yy = control[0].y()*u3 + 3.0*(control[1].y()*t*u2 + control[2].y()*t2*u) + control[3].y()*t3;
return QPointF(xx,yy);
} /* calcBezierVertex */
static QPointF calcBezierTangent( double t, const QPointF control[4] )
{
double t2(t*t);
double u(1.0-t);
double u2(u*u);
double xx(3.0*((control[1].x()-control[0].x())*u2 + (control[2].x()-control[1].x())*t*u*2.0 + (control[3].x()-control[2].x())*t2));
double yy(3.0*((control[1].y()-control[0].y())*u2 + (control[2].y()-control[1].y())*t*u*2.0 + (control[3].y()-control[2].y())*t2));
return QPointF(xx,yy);
} /* calcBezierTangent */
static double hypotBezierTangent( double t, const QPointF control[4] )
{
QPointF p(calcBezierTangent(t,control));
return hypot(p.x(),p.y());
} /* hypotBezierTangent */
static double simpsonApproximation( const QPointF control[4], double a, double b )
{
const unsigned int NLenSteps = 800;
double t( b-a );
double h( t/NLenSteps );
double sum( 0.0 );
// Zusammengesetzte Integration nach der Simpson-Regel
double s1( 0.0 );
double s2( 0.0 );
for (unsigned int i = 0; i < NLenSteps; i++)
{
s1 += hypotBezierTangent( a+h*i, control );
s2 += hypotBezierTangent( a+h*(double(i)-0.5), control );
}
s2 += hypotBezierTangent( a+h*(double(NLenSteps)-0.5), control );
sum += h/6.0 *( hypotBezierTangent(a,control)+hypotBezierTangent(b,control)+2.0*s1+4.0*s2);
return sum;
} /* simpsonApproximation */
static inline void buildControlPoints( const QPointF & p_i,
const QPointF & p_i1,
const QPointF & p_i2,
QPointF control[4],
bool startSegm = false,
bool endSegm = false )
{
if (startSegm)
{
control[0] = p_i;
control[1] = p_i*0.333 + p_i1*0.667;
}
else
{
control[0] = p_i*0.5 + p_i1*0.5;
control[1] = p_i*0.167 + p_i1*0.833;
}
if (endSegm)
{
control[2] = p_i1*0.667 + p_i2*0.333;
control[3] = p_i2;
}
else
{
control[2] = p_i1*0.833 + p_i2*0.167;
control[3] = p_i1*0.5 + p_i2*0.5;
}
} /* buildControlPoints */
SmoothLine::SmoothLine(const QVector<QPointF> & vertexVector, bool closedCurve )
{
SmoothLine::vertexVector( vertexVector, closedCurve );
} /* BezierSpline::SmoothLine */
void SmoothLine::vertexVector( const QVector<QPointF> & vertexVector, bool closedCurve )
{
_closedCurve = closedCurve;
_vertexVector = vertexVector;
} /* SmoothLine::vertexVector */
qreal SmoothLine::maximumCurveParameter() const
{
if (_vertexVector.size() > 2)
{
return (_closedCurve) ? (qreal)_vertexVector.size() : (qreal)_vertexVector.size() - 2.0;
}
return 0;
} /* SmoothLine::maximumCurveParameter */
QPointF SmoothLine::positionOnCurve( qreal t ) const
{
if (_vertexVector.size() < 3)
{
if (_vertexVector.size() > 1)
{
// It's just a straight line
if (t < 0.0) t = 1.0-t;
if ((t >= 0.0) && (t <= 1.0))
{
const QPointF & v0 = _vertexVector[0];
const QPointF & v1 = _vertexVector[1];
return v0 + (v1-v0)*t;
}
}
}
else
{
int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 3);
if (t < 0.0) t = double(maxSegm+1) - t;
int segment( floor(t) );
if ((segment >= 0) && (segment <= maxSegm))
{
QPointF control[4];
QPointF p[3];
if ((_closedCurve) && (segment == (maxSegm-1)))
{
p[0] = _vertexVector[_vertexVector.size()-2];
p[1] = _vertexVector.back();
p[2] = _vertexVector[0];
buildControlPoints( p[0], p[1], p[2], control, false, false );
}
else if ((_closedCurve) && (segment == maxSegm))
{
p[0] = _vertexVector.back();
p[1] = _vertexVector[0];
p[2] = _vertexVector[1];
buildControlPoints( p[0], p[1], p[2], control, false, false );
}
else
{
p[0] = _vertexVector[segment ];
p[1] = _vertexVector[segment+1];
p[2] = _vertexVector[segment+2];
buildControlPoints( p[0], p[1], p[2], control, ((segment == 0) && (!_closedCurve)), (segment == maxSegm) );
}
return calcBezierVertex( t-double(segment), control );
}
else
{
if (t == (maxSegm+1))
{
QPointF control[4];
QPointF p[3];
if (_closedCurve)
{
p[0] = _vertexVector[0];
p[1] = _vertexVector[1];
p[2] = _vertexVector[2];
buildControlPoints( p[0], p[1], p[2], control, true, false );
return calcBezierVertex( 0.0, control );
}
else
{
p[0] = _vertexVector[maxSegm ];
p[1] = _vertexVector[maxSegm+1];
p[2] = _vertexVector[maxSegm+2];
buildControlPoints( p[0], p[1], p[2], control, (maxSegm == 0), true );
return calcBezierVertex( 1.0, control );
}
}
}
}
return QPointF(0.0,0.0);
} /* SmoothLine::positionOnCurve */
QPointF SmoothLine::directionOnCurve( qreal t ) const
{
if (_vertexVector.size() < 3)
{
if (_vertexVector.size() >1)
{
// It's just a straight line
if (t < 0.0) t = 1.0-t;
if ((t >= 0.0) && (t <= 1.0))
{
return (_vertexVector[1]-_vertexVector[0]);
}
}
}
else
{
int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 3);
if (t < 0.0) t = double(maxSegm+1) - t;
int segment( floor(t) );
if ((segment >= 0) && (segment <= maxSegm))
{
QPointF control[4];
QPointF p[3];
if ((_closedCurve) && (segment == (maxSegm-1)))
{
p[0] = _vertexVector[_vertexVector.size()-2];
p[1] = _vertexVector.back();
p[2] = _vertexVector[0];
buildControlPoints( p[0], p[1], p[2], control, false, false );
}
else if ((_closedCurve) && (segment == maxSegm))
{
p[0] = _vertexVector.back();
p[1] = _vertexVector[0];
p[2] = _vertexVector[1];
buildControlPoints( p[0], p[1], p[2], control, false, false );
}
else
{
p[0] = _vertexVector[segment ];
p[1] = _vertexVector[segment+1];
p[2] = _vertexVector[segment+2];
buildControlPoints( p[0], p[1], p[2], control, ((segment == 0) && (!_closedCurve)), (segment == maxSegm) );
}
return calcBezierTangent( t-double(segment), control );
}
else
{
if (t == (maxSegm+1))
{
QPointF control[4];
QPointF p[3];
if (_closedCurve)
{
p[0] = _vertexVector[0];
p[1] = _vertexVector[1];
p[2] = _vertexVector[2];
buildControlPoints( p[0], p[1], p[2], control, true, false );
return calcBezierTangent( 0.0, control );
}
else
{
p[0] = _vertexVector[maxSegm ];
p[1] = _vertexVector[maxSegm+1];
p[2] = _vertexVector[maxSegm+2];
buildControlPoints( p[0], p[1], p[2], control, (maxSegm == 0), true );
return calcBezierTangent( 1.0, control );
}
}
}
}
return QPointF(0.0,0.0);
} /* SmoothLine::directionOnCurve */
qreal SmoothLine::arcLength( qreal t) const
{
if (t < 0.0) return 0.0;
QPointF control[4];
QPointF p[3];
QVector<QPointF>::const_iterator it = _vertexVector.begin();
if (_closedCurve)
{
int i = 0;
p[i++] = _vertexVector.back();
while ((i<3) && (it != _vertexVector.end()))
{
p[i] = *it;
++it;
++i;
}
buildControlPoints( p[0], p[1], p[2], control, false, false );
}
else
{
int i = 0;
while ((i<3) && (it != _vertexVector.end()))
{
p[i] = *it;
++it;
++i;
}
buildControlPoints( p[0], p[1], p[2], control, true, (it == _vertexVector.end()) );
}
if (t < 1.0) return simpsonApproximation( control, 0.0, t );
double sum = simpsonApproximation( control, 0.0, 1.0 ); t -= 1.0;
while (it != _vertexVector.end())
{
p[0] = p[1];
p[1] = p[2];
p[2] = *it;
++it;
if (_closedCurve) buildControlPoints( p[0], p[1], p[2], control, false, false );
else buildControlPoints( p[0], p[1], p[2], control, false, (it == _vertexVector.end()) );
if (t < 1.0)
{
sum += simpsonApproximation( control, 0.0, t );
break;
}
else sum += simpsonApproximation( control, 0.0, 1.0 );
}
if ((t > 0.0) && (_closedCurve))
{
p[0] = p[1];
p[1] = p[2];
p[2] = _vertexVector.front();
buildControlPoints( p[0], p[1], p[2], control, false, false );
sum += simpsonApproximation( control, 0.0, t );
}
return sum;
} /* SmoothLine::arcLength */
HermiteSpline::HermiteSpline( const QVector<QPointF> & vertexVector
, const QVector<QPointF> & tangentVector, bool closedCurve )
{
HermiteSpline::vertexVector(vertexVector, tangentVector, closedCurve );
} /* HermiteSpline::HermiteSpline */
void HermiteSpline::vertexVector( const QVector<QPointF> & vertexVector
, const QVector<QPointF> & tangentVector, bool closedCurve )
{
_closedCurve = closedCurve;
_vertexVector = vertexVector;
_tangentVector = tangentVector;
int countOfTangents = (_closedCurve) ? _vertexVector.size()*2 : (_vertexVector.size()-1) * 2;
if (_tangentVector.size() >= countOfTangents)
{
// fatal error
throw
SituationMap::Exception( __FILE__, __LINE__,
"No enough tangents defined to calculate spline. Each spline segment must have defined 2 tangents per vertex, excepting the border segments where only 1 tangent must be defined.");
}
} /* HermiteSpline::vertexVector */
qreal HermiteSpline::maximumCurveParameter() const
{
if (_vertexVector.size() < 2) return 0.0;
return (_closedCurve) ? _vertexVector.size() : (_vertexVector.size() - 1.0);
} /* HermiteSpline::maximumCurveParameter */
QPointF HermiteSpline::positionOnCurve( qreal t ) const
{
if (_vertexVector.size() < 2) return QPointF(0.0,0.0);
int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 2);
if (t < 0.0) t = double(maxSegm+1) - t;
int segment( floor(t) );
if ((segment >= 0) && (segment <= maxSegm))
{
QPointF control[4];
if ((_closedCurve) && (segment == maxSegm))
{
control[0] = _vertexVector.back();
control[3] = _vertexVector.front();
control[1] = control[0] + _tangentVector[_tangentVector.size()-2]/3.0;
control[2] = control[3] - _tangentVector[_tangentVector.size()-1]/3.0;
}
else
{
control[0] = _vertexVector[segment];
control[3] = _vertexVector[segment+1];
control[1] = control[0] + _tangentVector[segment*2]/3.0;
control[2] = control[3] - _tangentVector[segment*2+1]/3.0;
}
return calcBezierVertex( t-double(segment), control );
}
return QPointF(0.0,0.0);
} /* HermiteSpline::positionOnCurve */
QPointF HermiteSpline::directionOnCurve( qreal t ) const
{
if (_vertexVector.size() < 2) return QPointF(0.0,0.0);
int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 2);
if (t < 0.0) t = double(maxSegm+1) - t;
int segment( floor(t) );
if ((segment >= 0) && (segment <= maxSegm))
{
QPointF control[4];
if ((_closedCurve) && (segment == maxSegm))
{
control[0] = _vertexVector.back();
control[3] = _vertexVector.front();
control[1] = control[0] + _tangentVector[_tangentVector.size()-2]/3.0;
control[2] = control[3] - _tangentVector[_tangentVector.size()-1]/3.0;
}
else
{
control[0] = _vertexVector[segment];
control[3] = _vertexVector[segment+1];
control[1] = control[0] + _tangentVector[segment*2]/3.0;
control[2] = control[3] - _tangentVector[segment*2+1]/3.0;
}
return calcBezierTangent( t-double(segment), control );
}
return QPointF(0.0,0.0);
} /* HermiteSpline::directionOnCurve */
qreal HermiteSpline::arcLength( qreal t ) const
{
if (t < 0.0) return 0.0;
if (_vertexVector.size() < 2) return 0.0;
double sum = 0.0;
QPointF control[4];
QVector<QPointF>::const_iterator itv = _vertexVector.begin();
QVector<QPointF>::const_iterator itt = _vertexVector.begin();
if (_closedCurve) { if (2*_vertexVector.size() != _tangentVector.size()) return 0.0; }
else { if (2*_vertexVector.size()-2 != _tangentVector.size()) return 0.0; }
control[3] = *itv; ++itv;
while (itv != _vertexVector.end())
{
control[0] = control[3];
control[1] = control[0] + *itt / 3.0; ++itt;
control[2] = *itv - *itt / 3.0; ++itt;
control[3] = *itv;
if (t < 1.0) return sum + simpsonApproximation( control, 0.0, t );
sum = simpsonApproximation( control, 0.0, 1.0 ); t -= 1.0;
++itv;
} // while
if (_closedCurve)
{
control[0] = control[3];
control[1] = control[0] + *itt / 3.0; ++itt;
control[2] = _vertexVector.front() - *itt / 3.0; ++itt;
control[3] = _vertexVector.front();
if (t < 1.0) return sum + simpsonApproximation( control, 0.0, t );
sum = simpsonApproximation( control, 0.0, 1.0 ); // t -= 1.0; // 'ScanBuild: 't is never read'
}
return sum;
} /* HermiteSpline::arcLength */
BezierSpline::BezierSpline( const QVector<QPointF> & ctrlPntVector, bool closedCurve )
: HermiteSpline()
{
controlPointVector(ctrlPntVector,closedCurve);
} /* BezierSpline::BezierSpline */
void BezierSpline::controlPointVector( const QVector<QPointF> & ctrlPntVector, bool closedCurve )
{
if (ctrlPntVector.size() < 4) return;
QVector<QPointF> vertexVector;
QVector<QPointF> tangentVector;
QVector<QPointF>::const_iterator it = ctrlPntVector.begin();
vertexVector.append( *it );
++it;
while (it != ctrlPntVector.end())
{
// set first tangent
tangentVector.append( ((*it) - vertexVector.back()) * 3.0 );
++it;
if (it == ctrlPntVector.end())
{
throw SituationMap::Exception(__FILE__, __LINE__, "Missing bezier spline tangent control point." );
}
++it;
if (it == ctrlPntVector.end())
{
// error
if (closedCurve)
{
tangentVector.append( ((it[-1]) - vertexVector.front()) * 3.0 );
}
else throw SituationMap::Exception(__FILE__, __LINE__, "Missing bezier spline vertex control point." );
break;
}
else
{
vertexVector.append( *it );
tangentVector.append( ((it[-1]) - vertexVector.back()) * 3.0 );
++it;
}
} // while
HermiteSpline::vertexVector(vertexVector,tangentVector,closedCurve);
} /* BezierSpline::controlPointVector */
void BezierSpline::vertexVector( const QVector<QPointF>&, const QVector<QPointF>&, bool )
{
throw SituationMap::Exception(__FILE__, __LINE__, "Function is not defined for BezierSpline. Use controlPointVector instead." );
} /* BezierSpline::vertexVector */
Ellipse::Ellipse( QPointF center, qreal rx, qreal ry, qreal phi, qreal sectorStart, qreal sectorEnd )
: _center(center)
, _radiusX(fabs(rx))
, _radiusY(fabs(ry))
, _sinPhi(sin(phi))
, _cosPhi(cos(phi))
, _phiSectorStart(sectorStart)
, _phiSectorEnd(sectorEnd)
{
if (_radiusX < _radiusY)
{
// swap _radiusX and _radiusY
_radiusX = fabs(ry);
_radiusY = fabs(rx);
// and rotate
phi += MathConst::pi_half;
_sinPhi = sin(phi);
_cosPhi = cos(phi);
}
qreal a2 = _radiusX * _radiusX;
qreal b2 = _radiusY * _radiusY;
_eccentricity2 = (a2-b2) / a2;
if (fabs(_phiSectorStart) > MathConst::pi_two) _phiSectorStart /= MathConst::pi_two;
if (fabs(_phiSectorEnd) > MathConst::pi_two) _phiSectorEnd /= MathConst::pi_two;
if (_phiSectorStart >= 0)
{
if (_phiSectorStart > _phiSectorEnd) _phiSectorStart -= MathConst::pi_two;
}
else
{
if (_phiSectorStart > _phiSectorEnd)_phiSectorEnd += MathConst::pi_two;
}
} /* Ellipse::Ellipse */
qreal Ellipse::maximumCurveParameter() const { return _phiSectorEnd; }
qreal Ellipse::minimumCurveParameter() const { return _phiSectorStart; }
QPointF Ellipse::positionOnCurve(qreal t) const
{
// Using ellipse equation in polar coordinates relative to center point(0,0)
// because in this case the parameter t is identical with polar angle.
qreal sin_t = sin(t);
qreal cos_t = cos(t);
/*
qreal r = _radiusY / (1.0 - _eccentricity2 * cos_t*cos_t);
qreal x = cos_t*r;
qreal y = sin_t*r;
*/
qreal x = cos_t*_radiusX;
qreal y = sin_t*_radiusY;
return _center + QPointF( x*_cosPhi - y*_sinPhi, y*_cosPhi + x*_sinPhi );
} /* Ellipse::positionOnCurve */
QPointF Ellipse::directionOnCurve(qreal t) const
{
qreal sin_t = sin(t);
qreal cos_t = cos(t);
if (cos_t == 0.0) return QPointF( -sin_t, 0.0 );
qreal tan_t = sin_t / cos_t;
qreal tan_n = tan_t / 1.0 - _eccentricity2; // tan normal
// normal QPointF( 1.0/sqrt(1+tan_n*tan_n), tan_n/sqrt(1+tan_n*tan_n) )
QPointF tangente( -tan_n/sqrt(1+tan_n*tan_n), 1.0/sqrt(1+tan_n*tan_n) );
return QPointF( tangente.x()*_cosPhi - tangente.y()*_sinPhi, tangente.y()*_cosPhi + tangente.x()*_sinPhi );
} /* Ellipse::directionOnCurve */
qreal Ellipse::arcLength(qreal t) const
{
return arcLength( 0.0, t );
} /* Ellipse::arcLength */
qreal Ellipse::arcLength(qreal tmin, qreal tmax ) const
{
Q_UNUSED(tmin);
Q_UNUSED(tmax);
// ToDo: calculate convergenting formula based on polar center points equation of ellipse if needed
return 0.0;
} /* Ellipse::arcLength */
class CurvePathRasterer : public PathRasterer
{
public:
CurvePathRasterer() : PathRasterer(), _curveFunction(nullptr) {}
explicit CurvePathRasterer( const Curve * splineFunction ) : PathRasterer(), _curveFunction(splineFunction) {}
~CurvePathRasterer() = default;
void function( qreal t, QPointF & result ) override
{
if (_curveFunction) result = _curveFunction->positionOnCurve(t);
else
{
throw SituationMap::Exception(__FILE__, __LINE__, "Curve function is undefined for PathRasterer." );
}
} /* function */
protected:
const Curve * _curveFunction;
}; /* CurvePathRasterer */
PathRasterer * Ellipse::createPathRaster() const { return new CurvePathRasterer(this); }
PathRasterer * SmoothLine::createPathRaster() const { return new CurvePathRasterer(this); }
PathRasterer * HermiteSpline::createPathRaster() const { return new CurvePathRasterer(this); }
PathRasterer * BezierSpline::createPathRaster() const { return new CurvePathRasterer(this); }
} } // namespace SituationMap::MathUtils
+288
View File
@@ -0,0 +1,288 @@
/**
**
** @RDE_HEADER@
** Implementation of Splines
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 12.08.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <QtCore/QPoint>
#include <QtCore/QVector>
#include <MathUtils/math_const.h>
#include <situation_map_export.h>
namespace SituationMap { namespace MathUtils
{
class PathRasterer;
/**
@class Curve curve.h
@brief Base class to calculate a polyline from curve functions like splines, circles, ellpises ...
Pure virtual base class to calculate a polyline from curve functions. The curve function is defined
in derived classes. The resolution of rasterization can be set in PathRasterer class.
@see PathRasterer, Ellipse, Spline, SmoothLine, HermiteSpline, BezierSpline
*/
class SITUATIONMAP_EXPORT Curve
{
public:
/**
Returns the maximum curve paramter t. The curve is defined in interval tmin ... tmax
*/
virtual qreal maximumCurveParameter() const = 0;
/**
Returns the minimum curve paramter t. The curve is defined in interval tmin ... tmax
*/
virtual qreal minimumCurveParameter() const = 0;
/**
Returns the position on curve at \c t
@param t curve parameter, should be in range of 0 ... tmax
*/
virtual QPointF positionOnCurve ( qreal t ) const = 0;
/**
Returns the tangent at position \c t
@param t curve parameter, should be in range of 0 ... tmax
*/
virtual QPointF directionOnCurve( qreal t ) const = 0;
/**
Returns the length of curve from start to position t
@param t curve parameter, should be in range of 0 ... tmax
*/
virtual qreal arcLength( qreal t ) const = 0;
/**
Returns the length of curve from position \c tmin to position \c tmax
@param tmin curve parameter, should be in range of 0 ... maximumCurveParameter()
@param tmax curve parameter, should be in range of tmin ... maximumCurveParameter()
*/
virtual qreal arcLength( qreal tmin, qreal tmax ) const { return arcLength(tmax)-arcLength(tmin); }
/**
Returns the length of complete curve
*/
inline qreal arcLength() const { return arcLength( maximumCurveParameter() ); }
/**
Create instance of class \c PathRasterer to rasterize the spline curve. After usage the
caller should use delete to free memory and avoid memory leaks. This methode is pure virtual
and has to be override by the derived class.
@see PathRasterer
*/
virtual PathRasterer * createPathRaster() const = 0;
}; /* Curve */
/**
@class SmoothLine curve.h
@brief Class to calculate a ellipse. A angle have to be defined in RAD in mathematical manner (counter clockwise and (1,0) is zero angle)
*/
class SITUATIONMAP_EXPORT Ellipse : public Curve
{
public:
Ellipse() = default;
Ellipse( QPointF center, qreal rx, qreal ry, qreal phi, qreal sectorStart = 0, qreal sectorEnd = MathConst::pi_two );
virtual ~Ellipse() = default;
qreal maximumCurveParameter() const override;
qreal minimumCurveParameter() const override;
QPointF positionOnCurve ( qreal t ) const override;
QPointF directionOnCurve( qreal t ) const override;
qreal arcLength( qreal t ) const override;
qreal arcLength( qreal tmin, qreal tmax ) const override;
PathRasterer * createPathRaster() const override;
protected:
QPointF _center;
qreal _radiusX;
qreal _radiusY;
qreal _eccentricity2;
qreal _sinPhi;
qreal _cosPhi;
qreal _phiSectorStart;
qreal _phiSectorEnd;
}; /* Ellipse */
/**
@class SmoothLine curve.h
@brief Class to calculate a line with rounded verticies based on spline functions
*/
class SITUATIONMAP_EXPORT SmoothLine : public Curve
{
public:
/**
@name Construction & Destruction
@{
*/
SmoothLine() = default;
explicit SmoothLine( const QVector<QPointF> & vertexVector, bool closedCurve = false );
virtual ~SmoothLine() = default;
/** @} */
/**
Defines the predicated polyline
@param vertexVector verticies of the polyline
@param closedCurve if \c closedCurve is true the polyline is closed to a polygon
*/
void vertexVector( const QVector<QPointF> & vertexVector, bool closedCurve = false );
qreal maximumCurveParameter() const override;
qreal minimumCurveParameter() const override { return 0.0; }
QPointF positionOnCurve ( qreal t ) const override;
QPointF directionOnCurve( qreal t ) const override;
qreal arcLength( qreal t ) const override;
PathRasterer * createPathRaster() const override;
protected:
bool _closedCurve;
QVector<QPointF> _vertexVector;
}; /* SmoothLine */
/**
@class HermitSpline curve.h
@brief Class to calculate 2D hermit spline curves
*/
class SITUATIONMAP_EXPORT HermiteSpline : public Curve
{
public:
/**
@name Construction & Destruction
@{
*/
HermiteSpline() = default;
HermiteSpline( const QVector<QPointF> & vertexVector
, const QVector<QPointF> & tangentVector
, bool closedCurve = false );
~HermiteSpline() = default;
/** @} */
/**
Defines the gemeotry of the hermite spline
@param vertexVector verticies to define the hermite spline
@param tangentVector tangents to define the hermite spline
@param closedCurve if \c closedCurve is true the spline curve is closed
*/
virtual void vertexVector( const QVector<QPointF> & vertexVector
, const QVector<QPointF> & tangentVector
, bool closedCurve = false );
qreal maximumCurveParameter() const override;
qreal minimumCurveParameter() const override { return 0.0; }
QPointF positionOnCurve ( qreal t ) const override;
QPointF directionOnCurve( qreal t ) const override;
qreal arcLength( qreal t ) const override;
PathRasterer * createPathRaster() const override;
private:
bool _closedCurve;
QVector<QPointF> _vertexVector;
QVector<QPointF> _tangentVector;
}; /* HermiteSpline */
/**
@class BezierSpline curve.h
@brief Class to calculate 2D bezier spline curves
*/
class SITUATIONMAP_EXPORT BezierSpline : public HermiteSpline
{
public:
/**
@name Construction & Destruction
@{
*/
BezierSpline() = default;
explicit BezierSpline( const QVector<QPointF> & ctrlPntVector, bool closedCurve = false );
~BezierSpline() = default;
/** @} */
/**
Defines the control points of the bezier curve
@param ctrlPntVector control points of bezier curve
@param closedCurve if \c closedCurve is true the spline curve is closed
*/
void controlPointVector( const QVector<QPointF> & ctrlPntVector, bool closedCurve = false );
/**
Don't call vertexVector methode from class HermiteSpline. It is not defined for bezier curves.
If you call it anyway, you will get an execption.
*/
void vertexVector( const QVector<QPointF> & vertexVector
, const QVector<QPointF> & tangentVector
, bool closedCurve = false ) override;
PathRasterer * createPathRaster() const override;
}; /* BezierSpline */
} } // namespace SituationMap::MathUtils
+419
View File
@@ -0,0 +1,419 @@
/**
**
** @RDE_HEADER@
** Implementation of 2d matrix operations
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 22.06.2016, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "geometry_engine.h"
#include <Map/coord_translation_functor.h>
#include <Map/map.h>
#include <MapImpl/map_impl.h>
#include <MapImpl/abstract_map_object_impl.h>
#include <MathUtils/clipper.hpp>
// ESRI
#include <arcgis/Geometry.h>
#include <arcgis/GeometryEngine.h>
//#include <arcgis/Line.h>
#include <arcgis/Polyline.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/Polygon.h>
#include <arcgis/PolygonBuilder.h>
#include <QPointF>
#include <QVector>
#include <QDebug>
namespace SituationMap { namespace MathUtils
{
//
// Line
//
GeometryEngine::Geometry *GeometryEngine::createLine(Map *map, const QPointF &p1, const QPointF &p2)
{
QVector<QPointF> vertices;
vertices << p1 << p2;
return SituationMap::MathUtils::GeometryEngine::createPolyline(map, vertices);
} // GeometryEngine::Geometry *GeometryEngine::createLine( Map * map, QPointF p1, QPointF p2 )
template<class T>
inline GeometryEngine::Geometry * createPolyline(Map * map, const T & verticies)
{
MapImpl * mapImpl = dynamic_cast<MapImpl*>(map);
if (! mapImpl)
return nullptr;
SituationMap::CoordTranslationFunctor *mapCtFunctor = map->coordTranslationFunctor();
if (! mapCtFunctor)
return nullptr;
Esri::ArcGISRuntime::PolylineBuilder builder ( mapImpl->getEsriSpatialReference() );
for (auto pit = verticies.cbegin(); pit != verticies.cend(); ++pit)
{
const QPointF p(mapCtFunctor->geographicToMap(*pit));
builder.addPoint(p.x(),p.y());
}
Esri::ArcGISRuntime::Polyline * esriPolyline = new Esri::ArcGISRuntime::Polyline( builder.toGeometry() );
return esriPolyline;
} // GeometryEngine::Geometry * createPolyline(Map * map, const T & verticies)
GeometryEngine::Geometry *GeometryEngine::createPolyline(Map *map, const QVector<QPointF> &vertices )
{
return SituationMap::MathUtils::createPolyline<QVector<QPointF>>(map, vertices);
} // GeometryEngine::Geometry * GeometryEngine::createPolyline( Map * map, const QVector<QPointF> & verticies )
GeometryEngine::Geometry *GeometryEngine::createPolyline(Map *map, const QList<QPointF> &vertices)
{
return SituationMap::MathUtils::createPolyline<QList<QPointF>>(map, vertices);
} // GeometryEngine::Geometry * GeometryEngine::createPolyline( Map * map, const QVector<QPointF> & verticies )
//
// Polygon
//
template<class T>
inline GeometryEngine::Geometry *createPolygon(Map *map, const T &verticies)
{
MapImpl * mapImpl = dynamic_cast<MapImpl*>(map);
if (! mapImpl) return nullptr;
SituationMap::CoordTranslationFunctor * mapCtFunctor = map->coordTranslationFunctor();
if (! mapCtFunctor) return nullptr;
Esri::ArcGISRuntime::PolygonBuilder builder( mapImpl->getEsriSpatialReference() );
for (auto pit = verticies.cbegin(); pit != verticies.cend(); ++pit )
{
const QPointF p(mapCtFunctor->geographicToMap(*pit));
builder.addPoint(p.x(),p.y());
}
Esri::ArcGISRuntime::Polygon * esriPolygon = new Esri::ArcGISRuntime::Polygon( builder.toGeometry() );
return esriPolygon;
} // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector<QPointF> & verticies )
GeometryEngine::Geometry * GeometryEngine::createPolygon(Map * map, const QVector<QPointF> & vertices )
{
return SituationMap::MathUtils::createPolygon<QVector<QPointF>>( map, vertices );
} // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector<QPointF> & verticies )
GeometryEngine::Geometry * GeometryEngine::createPolygon(Map * map, const QList<QPointF> & vertices )
{
return SituationMap::MathUtils::createPolygon<QList<QPointF>>( map, vertices );
} // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector<QPointF> & verticies )
//
// cleanup etc.
//
void GeometryEngine::deleteGeometry(Geometry *geom)
{
delete geom;
} // void GeometryEngine::deleteGeoemtry( Geometry * geom )
bool GeometryEngine::contains( AbstractMapObject *mapObj, const Geometry &hull)
{
if (nullptr == mapObj) return false;
return mapObj->geometryIsInHull( hull );
} //bool GeometryEngine::contains( AbstractMapObject * mapObj, const Geometry & hull )
bool GeometryEngine::contains(const Geometry &geom, const Geometry &hull)
{
if (geom.isEmpty() || hull.isEmpty())
return false;
return Esri::ArcGISRuntime::GeometryEngine::contains(hull, geom);
} // bool GeometryEngine::contains(const Geometry & geom, const Geometry & hull)
bool GeometryEngine::intersect( AbstractMapObject *mapObj, const Geometry &intersector)
{
if (! mapObj)
return false;
return mapObj->geometryIntersect(intersector);
} // bool GeometryEngine::intersect( const Geometry & inputGeom, const Geometry & intersector )
bool GeometryEngine::intersect(const Geometry &inputGeom, const Geometry &intersector)
{
if (inputGeom.isEmpty() || intersector.isEmpty())
return false;
#ifdef DEBUGGERTEST
if (inputGeom.type() == EsriRuntimeQt::GeometryType::Polyline)
{
const EsriRuntimeQt::Polyline & l = inputGeom;
int numPoints = l.pointCount();
for (int i = 0; i < numPoints; ++i)
{
QPointF p = l.qxy(i);
__asm int 3
}
}
if (intersector.type() == EsriRuntimeQt::GeometryType::Line)
{
const EsriRuntimeQt::Line & l = intersector;
double startx = l.startX();
double starty = l.startY();
double endx = l.endX();
double endy = l.endY();
__asm int 3
}
#endif
return Esri::ArcGISRuntime::GeometryEngine::crosses(inputGeom, intersector);
} // bool GeometryEngine::intersect( const Geometry & inputGeom, const Geometry & intersector )
QVector<QPointF> GeometryEngine::parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector<QPointF> &line, double distance)
{
/* ~ first lokal functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
auto length = [](const QPointF &v) -> double { return sqrt(v.x()*v.x() + v.y()*v.y()); };
auto normalize = [](const QPointF &v) -> QPointF { return v / sqrt(v.x()*v.x() + v.y()*v.y()); };
auto intersection = [](const QPointF &p1, const QPointF &r1, const QPointF &p2, const QPointF &r2) -> QPointF
{
double f = r2.y()*r1.x() - r1.y()*r2.x();
double a = (p1.x() + r1.x()) * p1.y() - p1.x() * (p1.y() + r1.y());
double b = (p2.x() + r2.x()) * p2.y() - p2.x() * (p2.y() + r2.y());
double sx = (r2.x() * a - r1.x() * b) / f;
double sy = (-r1.y() * b + r2.y() * a) / f;
return QPointF(sx, sy);
}; // intersection
auto orientation = [](const QPointF &p, const QPointF &q, const QPointF &r) -> int
{
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for details of below formula.
double f = (q.y() - p.y()) * (r.x() - q.x()) - (q.x() - p.x()) * (r.y() - q.y());
if (qFuzzyIsNull(f)) return 0; // colinear
return (f > 0) ? 1 : 2; // clock or counterclock wise
}; // orientation
auto testIntersection = [&orientation, &intersection, &normalize](const QPointF &p1, const QPointF &q1, const QPointF &p2, const QPointF &q2) -> bool
{
// Find the four orientations needed for general and
// special cases
int o1 = orientation(p1, q1, p2);
int o2 = orientation(p1, q1, q2);
int o3 = orientation(p2, q2, p1);
int o4 = orientation(p2, q2, q1);
// General case
if (o1 != o2 && o3 != o4)
{
const QPointF & n1 = q1 - p1;
const QPointF & n2 = q2 - p2;
const QPointF & s = intersection(p1, normalize(n1), p2, normalize(n2));
double f1 = (!qFuzzyIsNull(n1.x())) ? (s.x() - p1.x()) / n1.x() : (s.y() - p1.y()) / n1.y();
double f2 = (!qFuzzyIsNull(n2.x())) ? (s.x() - p2.x()) / n2.x() : (s.y() - p2.y()) / n2.y();
return ((f1 >= 0) && (f1 <= 1.0) && (f2 >= 0) && (f2 <= 1.0));
}
return false; // Doesn't fall in any of the above cases
}; // testIntersection
/* ~ now let's do the job ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (line.size() < 2)
{
// Nothing to do --> return input line
return line;
}
QVector<QPointF> transformedLine;
transformedLine.reserve(line.size());
for (auto &point : qAsConst(line))
{
// Let's transform the coordinates to maps coordinate system ...
transformedLine << mapCtFunctor->geographicToMap(point);
}
struct LineSegment
{
QPointF m_p1;
QPointF m_p2;
QPointF m_t;
LineSegment() = default;
LineSegment(const QPointF &p1, const QPointF &p2, const QPointF &t)
: m_p1(p1), m_p2(p2), m_t(t)
{}
};
// build single line segments to translate along it's normal
QVector<LineSegment> multiSegmLine;
multiSegmLine.reserve(transformedLine.size() - 1);
for (int i = 1; i < transformedLine.size(); ++i)
{
const QPointF & p1 = transformedLine[i - 1];
const QPointF & p2 = transformedLine[i];
const QPointF & t = normalize(p2 - p1);
QPointF n = QPointF(t.y()*distance, -t.x()*distance);
multiSegmLine << LineSegment(p1 + n, p2 + n, t);
}
transformedLine.clear();
transformedLine << multiSegmLine.first().m_p1;
for (int i = 1; i < multiSegmLine.size(); ++i)
{
const LineSegment & l1 = multiSegmLine[i - 1];
const LineSegment & l2 = multiSegmLine[i];
const QPointF & dt = l1.m_t - l2.m_t;
transformedLine << ((qFuzzyIsNull(dt.x()) && qFuzzyIsNull(dt.y())) ? l1.m_p2 : intersection(l1.m_p2, l1.m_t, l2.m_p1, l2.m_t));
}
transformedLine << multiSegmLine.last().m_p2;
// check result
for (int i = 1; i < transformedLine.size(); ++i)
{
const QPointF & p1 = transformedLine[i - 1];
const QPointF & p2 = transformedLine[i];
if (length(p2 - p1) < distance)
{
// failure segment is to short relative to distance
return QVector<QPointF>();
}
for (int j = 1; j < transformedLine.size(); ++j)
{
if (j == i) continue;
const QPointF & g1 = transformedLine[j-1];
const QPointF & g2 = transformedLine[j];
if (testIntersection(p1, p2, g1, g2))
{
// failure resulting line has self intersections
return QVector<QPointF>();
}
}
}
QVector<QPointF> pLine;
// Transform coordinates back to geographic ...
pLine.reserve(transformedLine.size());
for (auto &p : qAsConst(transformedLine))
{
pLine << mapCtFunctor->mapToGeographic(p);
}
return pLine;
}
// QVector<QPointF> GeometryEngine::parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector<QPointF> &line, double distance)
QVector<Point3D> GeometryEngine::buffer(AbstractMapObject *mapObj, double distance)
{
if (!mapObj)
return QVector<Point3D>();
AbstractMapObjectImpl *mapObjImpl = dynamic_cast<AbstractMapObjectImpl*>(mapObj);
if (!mapObjImpl)
return QVector<Point3D>();
const Esri::ArcGISRuntime::Geometry &esriGeometry = mapObjImpl->getEsriGeometry();
// Esri::ArcGISRuntime::GeometryEngine::buffer -> works for 2D
// Esri::ArcGISRuntime::GeometryEngine::bufferGeodetic -> works for 2D and 3D!?
const Esri::ArcGISRuntime::Polygon polyline = Esri::ArcGISRuntime::GeometryEngine::bufferGeodetic(esriGeometry,
distance,
Esri::ArcGISRuntime::LinearUnitId::Meters,
1,
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
if (polyline.parts().isEmpty())
return QVector<Point3D>();
const Esri::ArcGISRuntime::ImmutablePart &immutablePart = polyline.parts().part(0);
const int size = immutablePart.pointCount();
QVector<Point3D> resultVec;
resultVec.reserve(size);
for (int i = 0; i < size; ++i)
{
const Esri::ArcGISRuntime::Point &esriP = immutablePart.point(i);
resultVec.append(Point3D(esriP.x(), esriP.y(), esriP.z()));
}
// qCritical() << " resultVec = " << resultVec.size();
// qCritical() << " res = " << polyline.toJson();
return resultVec;
}
} } // namespace SituationMap::MathUtils
+88
View File
@@ -0,0 +1,88 @@
/**
**
** @RDE_HEADER@
** 2D matrix operations
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 22.06.2016, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <QtCore/QPoint>
#include <Map/point3D.h>
#include <situation_map_export.h>
namespace Esri {
namespace ArcGISRuntime {
class Geometry;
} }
namespace SituationMap
{
class Map;
class AbstractMapObject;
class CoordTranslationFunctor;
namespace MathUtils
{
class SITUATIONMAP_EXPORT GeometryEngine
{
public:
using Geometry = Esri::ArcGISRuntime::Geometry;
static Geometry *createLine (Map *map, const QPointF &p1, const QPointF &p2);
static Geometry *createPolyline(Map *map, const QVector<QPointF> &vertices);
static Geometry *createPolyline(Map *map, const QList<QPointF> &vertices);
static Geometry *createPolygon (Map *map, const QVector<QPointF> &vertices);
static Geometry *createPolygon (Map *map, const QList<QPointF> &vertices);
static void deleteGeometry(Geometry *geom);
static bool contains(const Geometry &geom, const Geometry &hull);
static bool contains(AbstractMapObject *mapObj, const Geometry &hull);
static bool intersect(const Geometry &inputGeom, const Geometry &intersector);
static bool intersect(AbstractMapObject *mapObj , const Geometry &intersector);
/**
Calculating a parallel line.
@param mapCtFunctor CoordTranslationFunctor to translate coordinates from geographic to map coordinate system and vis versa.
@param line Line to use as input, the coordinates are geograpihc coordinates.
@param distance Distance in meter.
*/
static QVector<QPointF> parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector<QPointF> &line, double distance );
static QVector<SituationMap::Point3D> buffer(AbstractMapObject *mapObj, double distance);
}; // GeometryEngine
} // namespace MathUtils
} // namespace SituationMap
+47
View File
@@ -0,0 +1,47 @@
/**
**
** @RDE_HEADER@
** Math const
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 04.12.2015, JT : First edition
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <cmath>
namespace MathConst
{
constexpr double pi = M_PI;
constexpr double pi_two = pi * 2;
constexpr double pi_half = M_PI_2;
constexpr double pi_quater = M_PI_4;
constexpr double EarthRadius = 6371001.0;
constexpr double WGS84_Major_a = 6378137.0; // WGS84 Ellipsoid, a value (Radius am Äquator, große Halbachse)
constexpr double WGS84_Minor_b = 6378137.0*( 1.0 - 1.0/298.257223563);
constexpr double WGS84_SQR_EXC = ((WGS84_Major_a * WGS84_Major_a) - (WGS84_Minor_b * WGS84_Minor_b))/(WGS84_Major_a * WGS84_Major_a);
} // namespace MathConst
+114
View File
@@ -0,0 +1,114 @@
/**
**
** @RDE_HEADER@
** Implementation of 2d matrix operations
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 22.06.2016, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "matrix2d.h"
#include <sstream>
namespace SituationMap { namespace MathUtils
{
QPointF Matrix2D::apply( const QPointF & v ) const
{
QGenericMatrix<1,3,qreal> vM;
vM(0,0) = v.x();
vM(1,0) = v.y();
vM(2,0) = 1.0;
QGenericMatrix<1,3,qreal> result = m_matrix * vM;
qreal x = result(0,0) / result(2,0);
qreal y = result(1,0) / result(2,0);
return QPointF(x,y);
} /* Matrix2D::apply */
void Matrix2D::putTranslation( const QPointF & t )
{
QGenericMatrix<3,3,qreal> tM;
tM(0,2) = t.x();
tM(1,2) = t.y();
m_matrix = tM * m_matrix;
} /* Matrix2D::putTranslation */
void Matrix2D::putScale( const QPointF & s)
{
QGenericMatrix<3,3,qreal> sM;
sM(0,0) = s.x();
sM(1,1) = s.y();
m_matrix = sM * m_matrix;
} /* Matrix2D::putScale */
void Matrix2D::putScale( const QPointF & s, qreal sinPhi, qreal cosPhi )
{
putRotate(QPointF(cosPhi,-sinPhi));
putScale(s);
putRotate(sinPhi,cosPhi);
} /* Matrix2D::putScale */
void Matrix2D::putRotate( qreal sinPhi, qreal cosPhi )
{
QGenericMatrix<3,3,qreal> rM;
rM(0,0) = cosPhi;
rM(0,1) = sinPhi;
rM(1,0) = -sinPhi;
rM(1,1) = cosPhi;
m_matrix = rM * m_matrix;
} /* Matrix2D::putRotate */
std::ostream & operator<< ( std::ostream & stream, const Matrix2D & mat )
{
stream << "Matrix2D:" << std::endl;
for (int r = 0; r < 3; ++r)
{
for (int c = 0; c < 3; ++c)
{
stream << "[" << r << "," << c << "] = " << mat(r,c) << " ";
}
stream << std::endl;
}
return stream;
} // std::ostream & operator<< ( std::ostream & stream, const Matrix2D & mat )
} } // namespace SituationMap::MathUtils
+413
View File
@@ -0,0 +1,413 @@
/**
**
** @RDE_HEADER@
** 2D matrix operations
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 22.06.2016, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <QtCore/QPointF>
#include <QtGui/QGenericMatrix>
#include <situation_map_export.h>
#include <ostream>
namespace SituationMap { namespace MathUtils
{
/**
@class Matrix2D matrix2d.h
@brief The class implements a 3x3 matrix for calculating 2D coordinate transformation with homogeneouse coordinates.
One of the most common and important tasks in computer graphics is to transform the coordinates ( position,
orientation, and size ) of either objects within the graphical scene or the camera that is viewing the scene.
It is also frequently necessary to transform coordinates from one coordinate system to another, ( e.g. world
coordinates to viewpoint coordinates to screen coordinates. )
All of these transformations can be efficiently and succintly handled using some simple matrix representations,
which we will see can be particularly useful for combining multiple transformations into a single composite
transform matrix. The base transformations for translating, scaling and rotating coordinates can be combined in
an arbitrary manner.
<b>Translation in 2D</b>
Point (X,Y) is to be translated by amount Dx and Dy to a new location (X',Y').
\code
X' = Dx + X
Y' = Dy + Y
\endcode
or P' = T + P where
\code
P' = | X' |
| Y' |
T = | Dx |
| Dy |
P = | X |
| Y |
\endcode
<b>Scaling in 2D</b>
Point (X,Y) is to be scaled by amount Sx and Sy to location (X',Y').
\code
X' = Sx * X
Y' = Sy * Y
\endcode
or P' = S * P where
\code
P' = | X' |
| Y' |
S = | Sx 0 |
| 0 Sy |
P = | X |
| Y |
\endcode
Scaling is performed about the origin (0,0) not about the center of the line/polygon/whatever.
- Scale > 1 enlarge the object and move it away from the origin.
- Scale = 1 leave the object alone
- Scale < 1 shrink the object and move it towards the origin.
- uniform scaling: Sx = Sy
- differential scaling Sx != Sy -> alters proportions
<b>Rotation in 2D</b>
point (X,Y) is to be rotated about the origin by angle theta to location (X',Y')
\code{.unparsed}
X' = X * cos(theta) - Y * sin(theta)
Y' = X * sin(theta) + Y * cos(theta)
\endcode
or P' = R * P where
\code
P' = | X' |
| Y' |
R = | cos(theta) -sin(theta) |
| sin(theta) cos(theta) |
P = | X |
| Y |
\endcode
Note that this does involve sin and cos which are much more costly than addition or multiplication.
Rotation is performed about the origin \f$(0,0)\f$ not about the center of the line/polygon/whatever.
<b>Derivation of the 2D Rotation Equations</b>
Where does this matrix come from?
(X,Y) is located r away from (0,0) at a CCW angle of phi from the X axis.
(X',Y') is located r away from (0,0) at a CCW angle of theta+phi from the X axis.
Since rotation is about the origin, (X',Y') must be the same distance from the origin as (X,Y).
From trigonometry we have:
\code
X = r * cos(phi)
Y = r * sin(phi)
\endcode
and
\code
X' = r * cos(theta+phi)
Y' = r * sin(theta+phi)
\endcode
Now making use of the following trigonometric identities:
\code
cos(a+b) = cos(a) * cos(b) - sin(a) * sin(b)
sin(a+b) = sin(a) * cos(b) + cos(a) * sin(b)
\endcode
and substituting in for the above equations for X' and Y', we get:
\code
X' = r * cos(theta) * cos(phi) - r * sin(theta) * sin(phi)
Y' = r * sin(theta) * cos(phi) + r * cos(theta) * sin(phi)
\endcode
Then we substitute in X and Y from their definitions above, and the final result simplifies to:
\code
X' = X * cos(theta) - Y * sin(theta)
Y' = X * sin(theta) + Y * cos(theta)
\endcode
<b>Homogeneous Coordinates in 2 Dimensions</b>
Scaling and rotations are both handled using matrix multiplication, which can be combined as we
will see shortly. The translations cause a difficulty, however, since they use addition instead
of multiplication. We want to be able to treat all 3 transformations (translation, scaling,
rotation) in the same way - as multiplications. The solution is to give each point a third
coordinate (X,Y,W), which will allow translations to be handled as a multiplication also. (Note
that we are not really moving into the third dimension yet. The third coordinate is being added to
the mathematics solely in order to combine the addition and multiplication of 2-D coordinates.)
Two triples (X,Y,W) and (X',Y',W') represent the same point if they are multiples of each other e.g.
(1,2,3) and (2,4,6).
At least one of the three coordinates must be nonzero. If W is 0 then the point is at infinity. This
situation will rarely occur in practice in computer graphics. If W is nonzero we can divide the triple
by W to get the cartesian coordinates of X and Y which will be identical for triples representing the
same point (X/W,Y/W,1). This step can be considered as mapping the point from 3-D space onto the
plane W=1. Conversely, if the 2-D cartesian coordinates of a point are known as (X,Y), then the
homogenous coordinates can be given as (X,Y,1)
So, how does this apply to translation, scaling, and rotation of 2D coordinates?
<b>Translation of 2D Homogenous Coordinates</b>
point (X,Y) is to be translated by amount Dx and Dy to location (X',Y')
\code
X' = Dx + X
Y' = Dy + Y
\endcode
or P' = T * P where
\code
| X' |
P' = | Y' |
| 1 |
| 1 0 Dx |
T = | 0 1 Dy | = T(Dx,Dy)
| 0 0 1 |
| X |
P = | Y |
| 1 |
\endcode
Hey Look! Translation is now a multiplication instead of an addition!
<b>Scaling of 2D Homogenous Coordinates</b>
P' = S * P where
\code
P' = | X' |
| Y' |
| 1 |
| Sx 0 0 |
S = | 0 Sy 0 | = S(Sx,Sy)
| 0 0 1 |
| X |
P = | Y |
| 1 |
\endcode
<b>Rotation of 2D Homogenous Coordinates</b>
P' = R * P where
\code
| X' |
P' = | Y' |
| 1 |
| cos(theta) -sin(theta) 0 |
R = | sin(theta) cos(theta) 0 | = R(theta)
| 0 0 1 |
| X |
P = | Y |
| 1 |
\endcode
<b>Composition of 2D Transformations</b>
There are many situations in which the final transformation of a point is a combination of several
(often many) individual transformations. For example, the position of the finger of a robot might be
a function of the rotation of the robots hand, arm, and torso, as well as the position of the robot
on the railroad train and the position of the train in the world, and the rotation of the planet
around the sun, and . . .
Applying each transformation individually to all points in a model would take a lot of time. Instead
of applying several transformations matrices to each point we want to combine the transformations to
produce 1 matrix which can be applied to each point.
In the simplest case we want to apply the same type of transformation (translation, rotation, scaling)
more than once.
- translation is additive as expected
- scaling is multiplicative as expected
- rotation is additive as expected
But what if we want to combine different types of transformations?
A very common reason for doing this is to rotate a polygon about an arbitrary point (e.g. the center of
the polygon) rather than around the origin.
- Translate so that P1 is at the origin T(-Dx,-Dy)
- Rotate R(theta)
- Translate so that the point at the origin is at P1 T(Dx,Dy)
Note the order of operations here is right to left:
\code
P' = T(Dx,Dy) * R(theta) * T(-Dx,-Dy) * P
\endcode
is equal to
\code
P' = T(Dx,Dy) * { R(theta) * [ T(-Dx,-Dy) * P ] }
\endcode
is equal to
\code
P' = [ T(Dx,Dy) * R(theta) * T(-Dx,-Dy) ] * P
\endcode
The matrix that results from these 3 steps can then be applied to all of the points in the polygon. Another
common reason for doing this is to scale a polygon about an arbitrary point (e.g. the center of the polygon)
rather than around the origin.
- Translate so that P1 is at the origin
- Scale
- Translate so that the point at the origin is at P1
A detailed description can be also found in J.D.Foley, A.Van Dam "Fundamentals of Interactive Computer Graphics"
*/
class SITUATIONMAP_EXPORT Matrix2D
{
public:
/**
@name Construction, Destruction and Assign
@{
*/
Matrix2D() = default;
explicit Matrix2D(const QGenericMatrix<3, 3, qreal> & matrix) : m_matrix(matrix) {;}
Matrix2D & operator = ( const QGenericMatrix<3, 3, qreal> & matrix) { m_matrix = matrix; return *this; }
/** @} */
/**
Set matrix to identity.
*/
void setToIdentity() { m_matrix.setToIdentity(); }
/**
Returns true if this matrix is the identity; false otherwise.
*/
bool isIdentity() const { return m_matrix.isIdentity(); }
/**
Returns true if a translation is set, otherwise false
*/
bool isTranslation() const { return (!qFuzzyCompare( m_matrix(0,2),0.0) || !qFuzzyCompare(m_matrix(1,2),0.0)); }
/**
Returns true if a rotation is set, otherwise false
*/
bool isRotation() const { return (!qFuzzyCompare( m_matrix(0,1),0.0) || !qFuzzyCompare(m_matrix(1,0),0.0)); }
/**
Apply the coordinate transformation to one coordinate and returns the result.
*/
QPointF apply( const QPointF & ) const;
/**
Add translation to the current matrix
*/
void putTranslation( const QPointF & );
/**
Add scaling to the current matrix.
*/
void putScale( const QPointF & );
/**
Add scaling to the current matrix.
*/
void putScale(const QPointF & s, const QPointF & oVec) { putScale(s, oVec.y(), oVec.x() ); }
void putScale( const QPointF & s, qreal sinPhi, qreal cosPhi );
/**
Add rotation to the current matrix.
@param oVec vector defining the rotation regarding to (1,0). The length of \a oVec has to be 1 (unit vector)
*/
void putRotate( const QPointF & oVec ) { putRotate(oVec.y(),oVec.x()); }
void putRotate( qreal sinPhi, qreal cosPhi );
/**
Returns underlying QGenericMatrix.
*/
QGenericMatrix<3,3,qreal> & operator()() { return m_matrix; }
const QGenericMatrix<3,3,qreal> & operator()() const { return m_matrix; }
qreal operator()(int row, int col) const { return m_matrix(row,col); }
qreal & operator()(int row, int col) { return m_matrix(row,col); }
protected:
QGenericMatrix<3,3,qreal> m_matrix;
}; // Matrix2D
/**
Returns the product of N*M
*/
inline Matrix2D operator* ( const Matrix2D & N, const Matrix2D & M ) { return Matrix2D( N()*M() ); }
/**
Output operator
*/
SITUATIONMAP_EXPORT std::ostream & operator<< (std::ostream & stream, const Matrix2D & mat );
} } // namespace SituationMap::MathUtils
+99
View File
@@ -0,0 +1,99 @@
/**
**
** @RDE_HEADER@
** Rasterize functional path geometries to polylines
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 12.08.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "path_rasterer.h"
#include <cmath>
namespace SituationMap { namespace MathUtils
{
PathRasterer::PathRasterer() : _eps(1.0), _maxVertexCount(0)
{
} /* PathRasterer::PathRasterer */
void PathRasterer::rasterize(qreal tMin, qreal tMax, QVector<QPointF> & vertexList) { exeRasterize( tMin, tMax, vertexList ); }
void PathRasterer::rasterize(qreal tMin, qreal tMax, QList<QPointF> & vertexList) { exeRasterize( tMin, tMax, vertexList ); }
template <class List> void PathRasterer::exeRasterize( qreal tMin, qreal tMax, List & vertexList )
{
vertexList.clear();
if (tMin >= tMax) return;
QPointF vMin, vMax;
function( tMax, vMax );
function( tMin, vMin );
vertexList.append(vMin);
splitSegment( vMin, tMin, vMax, tMax, _maxVertexCount, vertexList );
vertexList.append(vMax);
} /* PathRasterer::exeRasterize */
template <class List> void PathRasterer::splitSegment( const QPointF & v0
, qreal t0
, const QPointF & v1
, qreal t1
, unsigned int maxVertexCount
, List & vertexList )
{
if (maxVertexCount < 1)
return;
QPointF v2;
qreal t2 = (t0+t1) * 0.5;
function( t2, v2 );
auto length = []( const QPointF & v ) -> qreal { return sqrt(v.x()*v.x() + v.y()*v.y()); };
qreal l0 = length(v1-v0);
qreal l1 = length(v1-v2);
qreal l2 = length(v2-v0);
/* -------------------------------------------------------------------------
* Berechnen der Höhe des Dreiecks über l0 für den Vergleich mit
* dem Schwellwert (siehe Kosinussatz, Trigonometrische Funktionen, etc.).
* ------------------------------------------------------------------------- */
qreal cosPhi = (l2*l2+l0*l0-l1*l1)/(2.0*l2*l0);
qreal h_eps = l2*sqrt(1.0-cosPhi*cosPhi);
// (maxVertexCount > 0) -> see pre-condition
if (((std::isnan(h_eps)) || (h_eps > _eps)))
{
maxVertexCount /= 2;
splitSegment( v0, t0, v2, t2, maxVertexCount, vertexList );
vertexList.append( v2 );
splitSegment( v2, t2, v1, t1, maxVertexCount, vertexList );
}
} /* PathRasterer::splitSegment */
} } // namespace SituationMap::MathUtils
+94
View File
@@ -0,0 +1,94 @@
/**
**
** @RDE_HEADER@
** Rasterize functional path geometries to polylines
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 12.08.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <QtCore/QPoint>
#include <QtCore/QVector>
#include <QtCore/QList>
#include <situation_map_export.h>
namespace SituationMap { namespace MathUtils
{
/**
@class PathRasterer path_rasterer.h
@brief Base class to rasterize parametric functions, like splines
Base class to rasterize parametric functions, like splines, with an interval halving method
to approximate the function by a discrete polyline.
@see Ellipse, Spline, SmoothLine, HermiteSpline, BezierSpline
*/
class SITUATIONMAP_EXPORT PathRasterer
{
public:
PathRasterer();
virtual ~PathRasterer() = default;
/**
Defines a threshold values to decide ending convergence
*/
inline void epsilon( qreal eps ) { _eps = eps; }
/**
Defines the maximum of calculated verticies if \c epsilon doesn't terminate the convergence operation
*/
inline void maxVertexCount(unsigned int max) { _maxVertexCount = max; }
/**
Method to override to define the parametric function
*/
virtual void function( qreal t, QPointF & result ) = 0;
/**
Creating a vertex vector with an interval halving method to approximate a parametric function by a polyline
*/
void rasterize(qreal tMin, qreal tMax, QVector<QPointF> & vertexList);
void rasterize(qreal tMin, qreal tMax, QList<QPointF> & vertexList);
protected:
qreal _eps;
unsigned int _maxVertexCount;
template<class List> void exeRasterize( qreal tMin, qreal tMax, List & vertexList );
template<class List> void splitSegment( const QPointF & v0
, qreal t0
, const QPointF & v1
, qreal t1
, unsigned int maxVertexCount
, List & vertexList );
}; /* PathRasterer */
} } // namespace SituationMap::MathUtils
+207
View File
@@ -0,0 +1,207 @@
/**
* @RDE_HEADER@
* Helper functions related to CoordinateTranslation
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "simple_helper.h"
#include "math_const.h"
#include "clipper.hpp"
#include <LtsCoordTrans/lts_coord_trans.h>
// Qt
#include <QList>
#include <QTransform>
namespace SituationMap
{
namespace MathUtils
{
QPointF rotate(const QPointF &p, const QPointF &center, qreal sinA, qreal cosA)
{
qreal x = p.x() - center.x();
qreal y = p.y() - center.y();
return QPointF(x * cosA - y * sinA + center.x(), y * cosA + x * sinA + center.y());
}
QPointF rotate(const QPointF &p, const QPointF &center, qreal phi) { return rotate(p, center, sin(phi), cos(phi)); }
qreal getMathAngle(qreal sinPhi, qreal cosPhi)
{
qreal phi = acos(cosPhi);
if (sinPhi < 0) phi = MathConst::pi_two - phi;
return phi;
}
qreal getMathAngle(const QPointF &oVec) { return getMathAngle(oVec.y(), oVec.x()); }
double getAngleDegreeNorth(const QPointF &p1, const QPointF &p2)
{
double x = p2.x() - p1.x();
double y = p2.y() - p1.y();
// Winkel der Geraden bestimmen
double alphaRad = atan2(y, x);
// // Fuer Winkel > PI liefert atan2 math. rechts drehende (also negative) Werte.
// // Um Werte von 0-2PI zu bekommen muss deshalb hier von 2Pi abgezogen werden.
alphaRad -= M_PI / 2.0;
alphaRad *= -1.0;
if (alphaRad < 0.0)
alphaRad = 2 * M_PI + alphaRad;
return (alphaRad * 180.0 / M_PI);
}
QPointF getPointOnLine(const QPointF &p1, const QPointF &p2, double position_0_to_1)
{
QPointF pRet;
pRet.rx() = p1.x() + (p2.x() - p1.x()) * position_0_to_1;
pRet.ry() = p1.y() + (p2.y() - p1.y()) * position_0_to_1;
return pRet;
}
Point3D getPointOnLine(const Point3D &p1, const Point3D &p2, double position_0_to_1)
{
Point3D pRet;
pRet.rx() = p1.x() + (p2.x() - p1.x()) * position_0_to_1;
pRet.ry() = p1.y() + (p2.y() - p1.y()) * position_0_to_1;
pRet.rz() = p1.z() + (p2.z() - p1.z()) * position_0_to_1;
return pRet;
}
void rotatePoint(QPointF &pToBeRotated, const QPointF &center, double angleDegree)
{
// angle to rad
double radAngle = ((M_PI) / -180.0) * angleDegree;
// Translate
QPointF t = pToBeRotated - center;
// Rotate
QPointF tmp;
tmp.rx() = t.x() * cos(radAngle) - t.y() * sin(radAngle);
tmp.ry() = t.x() * sin(radAngle) + t.y() * cos(radAngle);
// Translate back and write result
pToBeRotated = tmp + center;
}
QList<QPointF> mineFieldCenterLineToOutline(const QList<QPointF> &centerLine,
double width)
{
QList<QPointF> outline;
// Clipper is using ints (typedef signed long long cInt;)
// The coordinate of centerline will have values much lower the the maximum.
// Therefore the factor can be 1000. So the resolution is bit enough (->Millimeter):
const int factorInt = 10;
const double factorDouble = 10.0;
ClipperLib::ClipperOffset pathOffset;
ClipperLib::Path borderPath; // vector< int >
ClipperLib::Paths offsetPathVec; // vector< vector< int > >
for (auto point : centerLine)
{
borderPath << ClipperLib::IntPoint(static_cast<int>(point.x()*factorInt),
static_cast<int>(point.y()*factorInt));
}
pathOffset.AddPath(borderPath, ClipperLib::JoinType::jtSquare, ClipperLib::EndType::etOpenButt);
pathOffset.Execute(offsetPathVec, static_cast<int>(fabs(width)*factorInt));
for (auto path : offsetPathVec)
{
ClipperLib::Path::const_iterator it = path.begin();
for (; it != path.end(); ++it)
{
outline.append(QPointF(static_cast<double>(it->X) / factorDouble,
static_cast<double>(it->Y) / factorDouble)
);
}
}
return outline;
}
qreal getAngleDegree2North(qreal angleDeg)
{
double rotationAngle = angleDeg < 360. ? angleDeg : std::fmod(angleDeg, 360.);
// set rotation to 0° North(12h) counter clock wise
rotationAngle = rotationAngle < 90. ? std::fabs(90 - rotationAngle) : std::fabs(360 - rotationAngle + 90) ;
return rotationAngle;
}
qreal getAngleDegree2Math(qreal angleDeg)
{
// set rotation to 0° math(3h) clockwise
const double rotationMath = angleDeg < 90. ? std::fabs(angleDeg - 90) : std::fabs(360 - angleDeg + 90) ;
return rotationMath;
}
QPolygonF getCircleSectorPolygon(const QPointF &centerPoint, qreal radius, double rotationAngleDeg, double centralAngleDeg)
{
// | <-rotationAngle
// | <StartPoint
//
// \ | /
// \ /
// \ | /
// \ /
rotationAngleDeg = 90 - rotationAngleDeg;
// rotate point with ~5 degree steps:
int steps = centralAngleDeg/5.0;
if (steps<3) // at least 3 steps
steps=3;
const double singleIncrementDegree = centralAngleDeg/steps;
QPolygonF poly;
poly.reserve(steps+1);
// CenterPunkt erstellen und einfügen;
const LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(centerPoint);
poly << centerUtm.geographic(QPointF(0,0));
// Startwinkel:
const double angle = rotationAngleDeg - (centralAngleDeg/2.0); // start angle ->.\ | /
const double angleRad = angle * LtsCoordTrans::deg2rad();
// relPosition des StartPoint (point on circle)
const QPointF position(cos(angleRad) * radius, sin(angleRad) * radius);
// StartPoint einfügen
poly << centerUtm.geographic(position);
// calculate all steps, inclusive EndPos
for (int step = 1; step<=steps; ++step)
{
double curAngle = angle + (step*singleIncrementDegree);
double curAngleRad = curAngle * LtsCoordTrans::deg2rad();
poly << centerUtm.geographic( QPointF(cos(curAngleRad) * radius, sin(curAngleRad) * radius) );
} // for
return poly;
}
} // namespace SituationMap::MathUtils
} // namespace SituationMap
+130
View File
@@ -0,0 +1,130 @@
/**
* @RDE_HEADER@
* Helper functions related to CoordinateTranslation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "situation_map_export.h"
#include <Map/point3D.h>
// Qt
#include <QPointF>
#include <QPolygonF>
// ESRI
//----------------------------------------------------------------------------
namespace SituationMap
{
namespace MathUtils
{
/**
@brief Rotate a 2 dimensional point
@param p point to rotate
@param center center point of rotation
@param sinA sinus of rotation angle
@param cosA cosinus of rotation angle
*/
QPointF SITUATIONMAP_EXPORT rotate(const QPointF &p, const QPointF &center, qreal sinA, qreal cosA);
/**
@brief Rotate a 2 dimensional point
@param p point to rotate
@param center center point of rotation
@param phi rotation angle in rad
*/
QPointF SITUATIONMAP_EXPORT rotate(const QPointF &p, const QPointF &center, qreal phi);
/**
* @brief Get math angle in radiant from orientation vector
* @param oVec orientation vector, must be normalized to length 1
* @return mathematical angle in rad
*/
qreal SITUATIONMAP_EXPORT getMathAngle(const QPointF &oVec);
/**
* @brief Get math angle in radiant from sinus and cosinus
* @param sinPhi sinus of angle, is equal to y component of orientation vector
* @param cosPhi cosinus of angle, is equal to x component of orientation vector
* @return mathematical angle in rad
*/
qreal SITUATIONMAP_EXPORT getMathAngle(qreal sinPhi, qreal cosPhi);
double SITUATIONMAP_EXPORT getAngleDegreeNorth(const QPointF &p1, const QPointF &p2);
/**
* @brief Get North angle from Math angle
* @param mathematical orientated angle in degree
* @return north (0° = 12h) orientated angle in degree
*/
qreal SITUATIONMAP_EXPORT getAngleDegree2North(qreal angleDeg);
/**
* @brief Get North angle from Math angle
* @param mathematical orientated angle in degree
* @return math (0° = 3h) orientated angle in degree
*/
qreal SITUATIONMAP_EXPORT getAngleDegree2Math(qreal angleDeg);
/**
* @brief Get the position on a given line (for positioning a text)
* @param p1 startPoint of line
* @param p2 endPoint of line
* @param position_0_to_1 value from 0.0 up to 1.0.
* 0.0 -> 0% -> startPoint
* 0.5 -> 50% -> middle between startPoint and endPoint
* 1.0 -> 100% -> endPoint
* @return position between start and end.
*/
QPointF SITUATIONMAP_EXPORT getPointOnLine(const QPointF &p1, const QPointF &p2, double position_0_to_1);
Point3D SITUATIONMAP_EXPORT getPointOnLine(const Point3D &p1, const Point3D &p2, double position_0_to_1);
/**
* @brief getCircleSectorPolygon
* @param centerPoint [geo coordinates]
* @param radius [m]
* @param rotationAngleDeg (orientation of segment)
* @param centralAngleDeg
* @return polygon describing the circle sector
*/
QPolygonF SITUATIONMAP_EXPORT getCircleSectorPolygon(const QPointF &centerPoint, qreal radius, double rotationAngleDeg, double centralAngleDeg);
/**
* @brief Rotate a point around with another point as center.
* @param pToBeRotated Point to be rotated
* @param center Center
* @param angleDegree Angle in degree (clockwise).
*/
void SITUATIONMAP_EXPORT rotatePoint(QPointF &pToBeRotated,
const QPointF &center,
double angleDegree);
QList<QPointF> SITUATIONMAP_EXPORT mineFieldCenterLineToOutline(const QList<QPointF> &centerLine,
double width);
} // namespace MathUtils
} // namespace SituationMap