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