Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+4691
File diff suppressed because it is too large.
Load diff
+411
@@ -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
|
||||
|
||||
|
||||
Executable
+736
@@ -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
|
||||
Executable
+288
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
Executable
+413
@@ -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
@@ -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
@@ -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
@@ -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 ¢er, 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 ¢er, 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 ¢er, 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> ¢erLine,
|
||||
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 ¢erPoint, 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
@@ -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 ¢er, 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 ¢er, 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 ¢erPoint, 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 ¢er,
|
||||
double angleDegree);
|
||||
|
||||
|
||||
|
||||
|
||||
QList<QPointF> SITUATIONMAP_EXPORT mineFieldCenterLineToOutline(const QList<QPointF> ¢erLine,
|
||||
double width);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace MathUtils
|
||||
} // namespace SituationMap
|
||||
|
||||
Reference in new issue
Block a user