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