1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2012, 2014, 2015, 2019 Kai Pastor
4  *
5  *    This file is part of OpenOrienteering.
6  *
7  *    OpenOrienteering is free software: you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation, either version 3 of the License, or
10  *    (at your option) any later version.
11  *
12  *    OpenOrienteering is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #ifndef OPENORIENTEERING_UTIL_H
23 #define OPENORIENTEERING_UTIL_H
24 
25 #include <cmath>
26 #include <functional>
27 #include <type_traits>
28 
29 #include <QtGlobal>
30 #include <QtMath>
31 #include <QPointF>
32 #include <QRectF>
33 
34 class QObject;
35 class QRect;
36 // IWYU pragma: no_forward_declare QPointF
37 // IWYU pragma: no_forward_declare QRectF
38 
39 namespace std
40 {
41 	/**
42 	 * Fallback for missing std::log2 in some distributions of gcc.
43 	 *
44 	 * This template will be selected if std::log2 is not found. The function
45 	 * std::log2 is part of C++11, but the following distributions of gcc are
46 	 * known lack this function:
47 	 *
48 	 * - GCC 4.8 in Android NDK R10d
49 	 *
50 	 * The argument must be a floating point value in order to avoid ambiguity
51 	 * when the regular std::log2 is present.
52 	 */
53 	template< class T >
log2(T value)54 	constexpr double log2(T value)
55 	{
56 		static_assert(::std::is_floating_point<T>::value,
57 					  "The argument to std::log2 must be called a floating point value");
58 		return log(value)/M_LN2;
59 	}
60 }
61 
62 
63 
64 namespace OpenOrienteering {
65 
66 class MapCoord;
67 class MapCoordF;
68 
69 
70 // clazy:excludeall=missing-qobject-macro
71 
72 
73 /** Value to calculate the optimum handle distance of 4 cubic bezier curves
74  *  used to approximate a circle. */
75 #define BEZIER_KAPPA 0.5522847498
76 
77 /** When drawing a cubic bezier curve, the distance between start and end point
78  *  is multiplied by this value to get the handle distance from start respectively
79  *  end points.
80  *
81  *  Calculated as BEZIER_HANDLE_DISTANCE = BEZIER_KAPPA / sqrt(2) */
82 #define BEZIER_HANDLE_DISTANCE 0.390524291729
83 
84 
85 
86 /** (Un-)blocks recursively all signals from a QObject and its child-objects. */
87 void blockSignalsRecursively(QObject* obj, bool block);
88 
89 /** Returns a practically "infinitely" big QRectF. */
infiniteRectF()90 inline QRectF infiniteRectF()
91 {
92 	return QRectF(-10e10, -10e10, 20e10, 20e10);
93 }
94 
95 /** Modulus calculation like fmod(x, y), but works correctly for negative x. */
fmod_pos(double x,double y)96 inline double fmod_pos(double x, double y)
97 {
98 	return x - y * floor(x / y);
99 }
100 
101 /**
102  * Enlarges the rect to include the given point.
103  *
104  * The given rect must be valid.
105  *
106  * \see QRectF::isValid()
107  */
108 void rectInclude(QRectF& rect, const QPointF& point);
109 
110 /**
111  * Enlarges the rect to include the given point.
112  *
113  * If the given rect isn't valid, width and height are set to a small positive value.
114  */
115 void rectIncludeSafe(QRectF& rect, const QPointF& point);
116 
117 /**
118  * Enlarges the rect to include the given other_rect.
119  *
120  * Both rectangles must be valid.
121  *
122  * \see QRectF::isValid()
123  */
124 void rectInclude(QRectF& rect, const QRectF& other_rect);
125 
126 /**
127  * Enlarges the rect to include the given other_rect.
128  *
129  * At least one rectangle must be valid.
130  */
131 void rectIncludeSafe(QRectF& rect, const QRectF& other_rect);
132 
133 /**
134  * Enlarges the rect to include the given other_rect.
135  *
136  * At least one rectangle must be valid.
137  *
138  * \todo Check if QRegion could be used instead.
139  */
140 void rectIncludeSafe(QRect& rect, const QRect& other_rect);
141 
142 
143 /** Checks for line - rect intersection. */
144 bool lineIntersectsRect(const QRectF& rect, const QPointF& p1, const QPointF& p2);
145 
146 /**
147  * Calculates the line parameter for a point on a straight line.
148  *
149  * Sets ok to false in case of an error (if the line is actually a point,
150  * or if the test point is not on the line).
151  *
152  * x0, y0: line start
153  * dx, dy: vector from line start to line end
154  * x, y: suspected point on the line to calculate the parameter for so:
155  *       x0 + parameter * dx = x (and the same for y).
156  */
157 double parameterOfPointOnLine(double x0, double y0, double dx, double dy, double x, double y, bool& ok);
158 
159 /** Checks if the point is on the segment defined by
160  *  the given start and end coordinates. */
161 bool isPointOnSegment(const MapCoordF& seg_start, const MapCoordF& seg_end, const MapCoordF& point);
162 
163 
164 // TODO: Refactor: put remaining stuff into this namespace, too
165 namespace Util
166 {
167 
168 /**
169  * Generates a pattern of parallel lines inside the box given by extent.
170  *
171  * @see gridOperation()
172  *
173  * @param extent       Extent of the box.
174  * @param spacing      Spacing of the lines.
175  * @param offset       Offset of the first line from the origin.
176  * @param rotation     Angle used to rotate the lines.
177  * @param process_line Function object which will be called with start and
178  *                     end point for each line.
179  */
180 void hatchingOperation(const QRectF& extent, double spacing, double offset, double rotation,
181                        std::function<void (const QPointF&, const QPointF&)>& process_line);
182 
183 /**
184  * Generates a grid of lines inside the given box.
185  *
186  * @see hatchingOperation()
187  *
188  * @param extent       Extent of the box.
189  * @param horz_spacing Horizontal spacing of the lines.
190  * @param vert_spacing Vertical spacing of the lines.
191  * @param horz_offset  Horizontal offset of the first line from the origin.
192  * @param vert_offset  Vertical offset of the first line from the origin.
193  * @param rotation     Angle used to rotate the lines.
194  * @param process_line Function object which will be called with start and
195  *                     end point for each line.
196  */
197 inline
gridOperation(const QRectF & extent,double horz_spacing,double vert_spacing,double horz_offset,double vert_offset,double rotation,std::function<void (const QPointF &,const QPointF &)> & process_line)198 void gridOperation(const QRectF& extent, double horz_spacing, double vert_spacing,
199                    double horz_offset, double vert_offset, double rotation,
200                    std::function<void (const QPointF&, const QPointF&)>& process_line)
201 {
202 	hatchingOperation(extent, horz_spacing, horz_offset, rotation, process_line);
203 	hatchingOperation(extent, vert_spacing, vert_offset, rotation - M_PI / 2, process_line);
204 }
205 
206 /**
207  * Tests whether three points form what we would call a corner. The function
208  * returns true when point2 lies at least quantum_size from continuation of
209  * line from point1 to anchor_point. quantum_size parameter is essential for
210  * use of this function in GUI code as for various zoom levels point1 position
211  * get quantized with varying step size.
212  *
213  * @param point1 First point on a line.
214  * @param anchor_point Point that is being tested as corner candidate.
215  * @param point2 Final point on potentially bent line.
216  * @param quantum_size How far can point2 lie from the line defined by
217  *        point1--anchor to be considered unaligned with the two other points.
218  * @return True if point2 is further than quantum_size from the line defined
219  *         by point1--anchor_point or point2 is on the same side of
220  *         anchor_point as point1.
221  */
222 bool pointsFormCorner(const MapCoord& point1, const MapCoord& anchor_point,
223                       const MapCoord& point2, qreal quantum_size);
224 
225 }  // namespace Util
226 
227 
228 }  // namespace OpenOrienteering
229 
230 #endif
231