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