]> Shamusworld >> Repos - architektonas/blob - src/base/mathextra.cpp
Bugfixes related to removing Snapper class.
[architektonas] / src / base / mathextra.cpp
1 // mathextra.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  06/01/2010  Added this text. :-)
15 //
16
17 #include "mathextra.h"
18
19 #include <assert.h>                                                             // For test()
20 #include <math.h>
21 #include <errno.h>
22 #include "fparser.hh"
23 #include "debug.h"
24
25 /**
26  * Rounds the given double to the next int.
27  */
28 int Math::round(double v)
29 {
30         return (v - floor(v) < 0.5 ? (int)floor(v) : (int)ceil(v));
31 }
32
33 /**
34  * Save pow function
35  */
36 double Math::pow(double x, double y)
37 {
38         errno = 0;
39         double ret = ::pow(x, y);
40
41         if (errno == EDOM)
42         {
43         DEBUG->print(Debug::D_ERROR, "Math::pow: EDOM in pow");
44                 ret = 0.0;
45         }
46         else if (errno == ERANGE)
47         {
48         DEBUG->print(Debug::D_WARNING, "Math::pow: ERANGE in pow");
49                 ret = 0.0;
50         }
51
52         return ret;
53 }
54
55 /**
56  * Converts radians to degrees.
57  */
58 double Math::rad2deg(double a)
59 {
60         return (a / (2.0 * M_PI) * 360.0);
61 }
62
63 /**
64  * Converts degrees to radians.
65  */
66 double Math::deg2rad(double a)
67 {
68         return ((a / 360.0) * (2.0 * M_PI));
69 }
70
71 /**
72  * Converts radians to gradians.
73  */
74 double Math::rad2gra(double a)
75 {
76         return (a / (2.0 * M_PI) * 400.0);
77 }
78
79 /**
80  * Finds greatest common divider using Euclid's algorithm.
81  */
82 int Math::findGCD(int a, int b)
83 {
84         while (b != 0)
85         {
86                 int rem = a % b;
87                 a = b;
88                 b = rem;
89         }
90
91         return a;
92 }
93
94 /**
95  * Tests if angle a is between a1 and a2. a, a1 and a2 must be in the
96  * range between 0 and 2*PI.
97  * All angles in rad.
98  *
99  * @param reversed true for clockwise testing. false for ccw testing.
100  * @return true if the angle a is between a1 and a2.
101  */
102 bool Math::isAngleBetween(double a, double a1, double a2, bool reversed)
103 {
104         bool ret = false;
105
106         if (reversed)
107         {
108                 double tmp = a1;
109                 a1 = a2;
110                 a2 = tmp;
111         }
112
113         if (a1 >= a2 - 1.0e-12)
114         {
115                 if (a >= a1 - 1.0e-12 || a <= a2 + 1.0e-12)
116                         ret = true;
117         }
118         else
119         {
120                 if (a >= a1 - 1.0e-12 && a <= a2 + 1.0e-12)
121                         ret = true;
122         }
123
124         //DEBUG->print("angle %f is %sbetween %f and %f",
125         //                a, ret ? "" : "not ", a1, a2);
126         return ret;
127 }
128
129 /**
130  * Corrects the given angle to the range of 0-2*Pi.
131  */
132 double Math::correctAngle(double a)
133 {
134         while (a > 2 * M_PI)
135                 a -= 2 * M_PI;
136
137         while (a < 0)
138                 a += 2 * M_PI;
139
140         return a;
141 }
142
143 /**
144  * @return The angle that needs to be added to a1 to reach a2.
145  *         Always positive and less than 2*pi.
146  */
147 double Math::getAngleDifference(double a1, double a2)
148 {
149         double ret;
150
151         if (a1 >= a2)
152                 a2 += 2 * M_PI;
153
154         ret = a2 - a1;
155
156         if (ret >= 2 * M_PI)
157                 ret = 0.0;
158
159         return ret;
160 }
161
162 /**
163  * Makes a text constructed with the given angle readable. Used
164  * for dimension texts and for mirroring texts.
165  *
166  * @param readable true: make angle readable, false: unreadable
167  * @param corrected Will point to true if the given angle was
168  *   corrected, false otherwise.
169  *
170  * @return The given angle or the given angle+PI, depending which on
171  * is readable from the bottom or right.
172  */
173 double Math::makeAngleReadable(double angle, bool readable, bool * corrected)
174 {
175         double ret;
176         bool cor = isAngleReadable(angle) ^ readable;
177
178         // quadrant 1 & 4
179         if (!cor)
180                 ret = angle;
181         // quadrant 2 & 3
182         else
183                 ret = angle + M_PI;
184
185         if (corrected)
186                 *corrected = cor;
187
188         return ret;
189 }
190
191 /**
192  * @return true: if the given angle is in a range that is readable
193  * for texts created with that angle.
194  */
195 bool Math::isAngleReadable(double angle)
196 {
197         if (angle > M_PI /2.0 * 3.0 + 0.001 || angle < M_PI / 2.0 + 0.001)
198                 return true;
199
200         return false;
201 }
202
203 /**
204  * @param tol Tolerance in rad.
205  * @retval true The two angles point in the same direction.
206  */
207 bool Math::isSameDirection(double dir1, double dir2, double tol)
208 {
209         double diff = fabs(dir1 - dir2);
210
211         if (diff < tol || diff > 2 * M_PI - tol)
212                 return true;
213
214         return false;
215 }
216
217 /**
218  * Compares two double values with a tolerance.
219  */
220 bool Math::cmpDouble(double v1, double v2, double tol)
221 {
222     return (fabs(v2 - v1) < tol);
223 }
224
225 /**
226  * Evaluates a mathematical expression and returns the result.
227  * If an error occured, the given default value 'def' will be returned.
228  */
229 double Math::eval(const QString & expr, double def)
230 {
231         bool ok;
232         double res = Math::eval(expr, &ok);
233
234         if (!ok)
235                 return def;
236
237         return res;
238 }
239
240 /**
241  * Evaluates a mathematical expression and returns the result.
242  * If an error occured, ok will be set to false (if ok isn't NULL).
243  */
244 double Math::eval(const QString & expr, bool * ok)
245 {
246         if (expr.isEmpty())
247         {
248                 if (ok != NULL)
249                         *ok = false;
250
251                 return 0.0;
252         }
253
254         FunctionParser fp;
255         fp.AddConstant("pi", M_PI);
256
257         // replace '14 3/4' with '14+3/4'
258         QString s = expr;
259         bool done;
260
261         do
262         {
263                 done = true;
264                 int i = s.indexOf(QRegExp("[0-9]* [0-9]*/[0-9]*"));
265
266                 if (i != -1)
267                 {
268                         int i2 = s.indexOf(' ', i);
269
270                         if (i2 != -1)
271                         {
272                                 s.replace(i2, 1, "+");
273                                 done = false;
274                         }
275                 }
276         }
277         while (!done);
278
279         int ret = fp.Parse(s.toAscii().data(), "", true);
280
281         if (ret >= 0)
282         {
283                 if (ok)
284                         *ok = false;
285
286                 return 0.0;
287         }
288
289         if (ok)
290                 *ok = true;
291
292         return fp.Eval(NULL);
293 }
294
295 /**
296  * Converts a double into a string which is as short as possible
297  *
298  * @param value The double value
299  * @param prec Precision e.g. a precision of 1 would mean that a
300  *     value of 2.12030 will be converted to "2.1". 2.000 is always just "2").
301  */
302 QString Math::doubleToString(double value, double prec)
303 {
304         if (prec < 1.0e-12)
305         {
306                 std::cerr << "Math::doubleToString: invalid precision\n";
307                 return "";
308         }
309
310         QString ret;
311         int num = Math::round(value / prec);
312
313         QString exaStr = Math::doubleToString(prec, 10);
314         int dotPos = exaStr.indexOf('.');
315
316         if (dotPos == -1)
317                 ret.sprintf("%d", Math::round(num * prec));
318         else
319         {
320                 int digits = exaStr.length() - dotPos - 1;
321                 ret = Math::doubleToString(num * prec, digits);
322         }
323
324         return ret;
325 }
326
327 /**
328  * Converts a double into a string which is as short as possible.
329  *
330  * @param value The double value
331  * @param prec Precision
332  */
333 QString Math::doubleToString(double value, int prec)
334 {
335         QString valStr;
336
337         valStr.setNum(value, 'f', prec);
338
339         if (valStr.contains('.'))
340         {
341                 // Remove zeros at the end:
342                 while (valStr.at(valStr.length() - 1) == '0')
343                         valStr.truncate(valStr.length() - 1);
344
345                 if (valStr.at(valStr.length() - 1) == '.')
346                         valStr.truncate(valStr.length() - 1);
347         }
348
349         return valStr;
350 }
351
352 /**
353  * Performs some testing for the math class.
354  */
355 void Math::test()
356 {
357         std::cout << "Math::test: doubleToString:\n";
358
359         double v = 0.1;
360         QString s = Math::doubleToString(v, 0.1);
361         assert(s == "0.1");
362         s = Math::doubleToString(v, 0.01);
363         assert(s == "0.1");
364         s = Math::doubleToString(v, 0.0);
365         assert(s == "0");
366
367         v = 0.01;
368         s = Math::doubleToString(v, 0.1);
369         assert(s == "0");
370         s = Math::doubleToString(v, 0.01);
371         assert(s == "0.01");
372         s = Math::doubleToString(v, 0.0);
373         assert(s == "0");
374
375         v = 0.001;
376         s = Math::doubleToString(v, 0.1);
377         assert(s == "0");
378         s = Math::doubleToString(v, 0.01);
379         assert(s == "0");
380         s = Math::doubleToString(v, 0.001);
381         assert(s == "0.001");
382         s = Math::doubleToString(v, 0.0);
383         assert(s == "0");
384
385         std::cout << "Math::test: complete\n";
386 }