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