]> Shamusworld >> Repos - architektonas/blob - src/base/rs_math.cpp.bak
Initial import
[architektonas] / src / base / rs_math.cpp.bak
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 RS_String& expr, bool* ok);
265 /**
266  * Evaluates a mathematical expression and returns the result.
267  * If an error occured, ok will be set to false (if ok isn't NULL).
268  */
269 // Keep that in the header file for dynamic inclusion/exclusion.
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                 {
277                         *ok = false;
278                 }
279
280                 return 0.0;
281         }
282
283         FunctionParser fp;
284         fp.AddConstant("pi", M_PI);
285
286         // replace '14 3/4' with '14+3/4'
287         QString s = expr;
288         bool done;
289
290         do
291         {
292                 done = true;
293                 int i = s.find(RS_RegExp("[0-9]* [0-9]*/[0-9]*"));
294                 if (i!=-1)
295                 {
296                         int i2 = s.find(' ', i);
297
298                         if (i2 != -1)
299                         {
300                                 s.replace(i2, 1, "+");
301                                 done = false;
302                         }
303                 }
304         }
305         while (!done);
306
307         int ret = fp.Parse(s.latin1(), "", true);
308
309         if (ret >= 0)
310         {
311                 if (ok != NULL)
312                 {
313                         *ok = false;
314                 }
315
316                 return 0.0;
317         }
318
319         if (ok != NULL)
320         {
321                 *ok = true;
322         }
323
324         return fp.Eval(NULL);
325 #else
326         //std::cerr << "RS_Math::eval: No FParser support compiled in.\n";
327         return expr.toDouble();
328 #endif
329 }
330
331 /**
332  * Converts a double into a string which is as short as possible
333  *
334  * @param value The double value
335  * @param prec Precision e.g. a precision of 1 would mean that a
336  *     value of 2.12030 will be converted to "2.1". 2.000 is always just "2").
337  */
338 RS_String RS_Math::doubleToString(double value, double prec)
339 {
340         if (prec < 1.0e-12)
341         {
342                 std::cerr << "RS_Math::doubleToString: invalid precision\n";
343                 return "";
344         }
345
346         RS_String ret;
347         RS_String exaStr;
348         int dotPos;
349         int num = RS_Math::round(value / prec);
350
351         exaStr = RS_Math::doubleToString(prec, 10);
352         dotPos = exaStr.find('.');
353
354         if (dotPos == -1)
355         {
356                 ret.sprintf("%d", RS_Math::round(num * prec));
357         }
358         else
359         {
360                 int digits = exaStr.length() - dotPos - 1;
361                 ret = RS_Math::doubleToString(num * prec, digits);
362         }
363
364         return ret;
365 }
366
367 /**
368  * Converts a double into a string which is as short as possible.
369  *
370  * @param value The double value
371  * @param prec Precision
372  */
373 QString RS_Math::doubleToString(double value, int prec)
374 {
375         QString valStr;
376
377         valStr.setNum(value, 'f', prec);
378
379         if (valStr.contains('.'))
380         {
381                 // Remove zeros at the end:
382                 while (valStr.at(valStr.length() - 1) == '0')
383                 {
384                         valStr.truncate(valStr.length() - 1);
385                 }
386
387                 if (valStr.at(valStr.length() - 1) == '.')
388                 {
389                         valStr.truncate(valStr.length() - 1);
390                 }
391         }
392
393         return valStr;
394 }
395
396 /**
397  * Performs some testing for the math class.
398  */
399 void RS_Math::test()
400 {
401         std::cout << "RS_Math::test: doubleToString:\n";
402
403         double v = 0.1;
404         QString s = RS_Math::doubleToString(v, 0.1);
405         assert(s == "0.1");
406         s = RS_Math::doubleToString(v, 0.01);
407         assert(s == "0.1");
408         s = RS_Math::doubleToString(v, 0.0);
409         assert(s == "0");
410
411         v = 0.01;
412         s = RS_Math::doubleToString(v, 0.1);
413         assert(s == "0");
414         s = RS_Math::doubleToString(v, 0.01);
415         assert(s == "0.01");
416         s = RS_Math::doubleToString(v, 0.0);
417         assert(s == "0");
418
419         v = 0.001;
420         s = RS_Math::doubleToString(v, 0.1);
421         assert(s == "0");
422         s = RS_Math::doubleToString(v, 0.01);
423         assert(s == "0");
424         s = RS_Math::doubleToString(v, 0.001);
425         assert(s == "0.001");
426         s = RS_Math::doubleToString(v, 0.0);
427         assert(s == "0");
428
429         std::cout << "RS_Math::test: complete\n";
430 }