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