LibreCAD
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
lcmath.cpp
Go to the documentation of this file.
1 #include <cmath>
2 
3 #include "lcmath.h"
4 #include "cad/const.h"
5 #include <Eigen/Dense>
6 #include <unsupported/Eigen/Polynomials>
7 #include <iostream>
8 
9 using namespace lc;
10 using namespace geo;
11 
21 bool Math::isAngleBetween(double angle,
22  double start, double end,
23  bool CCW) {
24  double diffA = getAngleDifference(start, end, CCW);
25  double diffB = getAngleDifference(start, angle, CCW);
26  return diffB <= diffA;
27 }
28 
29 
33 double Math::correctAngle(double a) {
34  return std::remainder(a, 2. * M_PI);
35 }
36 
41 double Math::getAngleDifferenceShort(double a1, double a2, bool CCW) {
42  if (!CCW) {
43  std::swap(a1, a2);
44  }
45 
46  auto angle = correctAngle(a2 - a1);
47  if(angle < 0) {
48  angle += 2*M_PI;
49  }
50 
51  return angle;
52 }
53 
54 double Math::getAngleDifference(double start, double end, bool CCW) {
55  double difference;
56 
57  if(CCW) {
58  difference = end - start;
59  }
60  else {
61  difference = start - end;
62  }
63 
64  if (difference < 0.) {
65  difference = 2.0 * M_PI - std::abs(difference);
66  }
67 
68  return difference;
69 }
70 
71 
77 std::vector<double> Math::quadraticSolver(const std::vector<double>& ce) {
78  std::vector<double> ans(0, 0.);
79 
80  if (ce.size() != 2) {
81  return ans;
82  }
83 
84  Eigen::PolynomialSolver<double, Eigen::Dynamic> solver;
85  Eigen::VectorXd coeff(3);
86 
87  coeff[0] = ce[1];
88  coeff[1] = ce[0];
89  coeff[2] = 1;
90 
91  solver.compute(coeff);
92 
93  solver.realRoots(ans);
94  return ans;
95 }
96 
102 std::vector<double> Math::cubicSolver(const std::vector<double>& ce) {
103  std::vector<double> ans(0, 0.);
104  if (ce.size() != 3) {
105  return ans;
106  }
107 
108  Eigen::PolynomialSolver<double, Eigen::Dynamic> solver;
109  Eigen::VectorXd coeff(4);
110 
111  coeff[0] = ce[2];
112  coeff[1] = ce[1];
113  coeff[2] = ce[0];
114  coeff[3] = 1;
115 
116  solver.compute(coeff);
117 
118  solver.realRoots(ans);
119  return ans;
120 }
121 
127 std::vector<double> Math::quarticSolver(const std::vector<double>& ce) {
128  // std::cout<<"x^4+("<<ce[0]<<")*x^3+("<<ce[1]<<")*x^2+("<<ce[2]<<")*x+("<<ce[3]<<")==0"<<std::endl;
129 
130  std::vector<double> ans(0, 0.);
131  Eigen::PolynomialSolver<double, Eigen::Dynamic> solver;
132  Eigen::VectorXd coeff(5);
133 
134  if (ce.size() != 4) {
135  return ans;
136  }
137 
138  coeff[0] = ce[3];
139  coeff[1] = ce[2];
140  coeff[2] = ce[1];
141  coeff[3] = ce[0];
142  coeff[4] = 1;
143 
144  solver.compute(coeff);
145 
146  solver.realRoots(ans);
147  return ans;
148 
149 }
150 
151 std::vector<double> Math::sexticSolver(const std::vector<double>& ce) {
152 
153  std::vector<double> ans(0, 0.);
154  Eigen::PolynomialSolver<double, Eigen::Dynamic> solver;
155  Eigen::VectorXd coeff(7);
156 
157  if (ce.size() != 7) {
158  return ans;
159  }
160 
161  coeff[0] = ce[6];
162  coeff[1] = ce[5];
163  coeff[2] = ce[4];
164  coeff[3] = ce[3];
165  coeff[4] = ce[2];
166  coeff[5] = ce[1];
167  coeff[6] = ce[0];
168 
169  solver.compute(coeff);
170 
171  solver.realRoots(ans);
172 
173  return ans;
174 }
175 
176 
183 std::vector<double> Math::quarticSolverFull(const std::vector<double>& ce) {
184  // std::cout<<ce[4]<<"*y^4+("<<ce[3]<<")*y^3+("<<ce[2]<<"*y^2+("<<ce[1]<<")*y+("<<ce[0]<<")==0"<<std::endl;
185 
186  std::vector<double> roots(0, 0.);
187 
188  if (ce.size() != 5) {
189  return roots;
190  }
191 
192  std::vector<double> ce2(4, 0.);
193 
194  if (std::abs(ce[4]) < 1.0e-14) { // this should not happen
195  if (std::abs(ce[3]) < 1.0e-14) { // this should not happen
196  if (std::abs(ce[2]) < 1.0e-14) { // this should not happen
197  if (std::abs(ce[1]) > 1.0e-14) {
198  roots.push_back(-ce[0] / ce[1]);
199  } else {
200  // can not determine y. this means overlapped, but overlap should have been detected before, therefore return empty set
201  return roots;
202  }
203  } else {
204  ce2.resize(2);
205  ce2[0] = ce[1] / ce[2];
206  ce2[1] = ce[0] / ce[2];
207  roots = Math::quadraticSolver(ce2);
208  }
209  } else {
210  ce2.resize(3);
211  ce2[0] = ce[2] / ce[3];
212  ce2[1] = ce[1] / ce[3];
213  ce2[2] = ce[0] / ce[3];
214  roots = Math::cubicSolver(ce2);
215  }
216  } else {
217  ce2[0] = ce[3] / ce[4];
218  ce2[1] = ce[2] / ce[4];
219  ce2[2] = ce[1] / ce[4];
220  ce2[3] = ce[0] / ce[4];
221 
222  if (std::abs(ce2[3]) <= TOLERANCE15) {
223  //constant term is zero, factor 0 out, solve a cubic equation
224  ce2.resize(3);
225  roots = Math::cubicSolver(ce2);
226  roots.push_back(0.);
227  } else {
228  roots = Math::quarticSolver(ce2);
229  }
230  }
231 
232  return roots;
233 }
234 
236 /* solve the following quadratic simultaneous equations,
237  * ma000 x^2 + ma011 y^2 - 1 =0
238  * ma100 x^2 + 2 ma101 xy + ma111 y^2 + mb10 x + mb11 y +mc1 =0
239  *
240  *@m, a vector of size 8 contains coefficients in the strict order of:
241  ma000 ma011 ma100 ma101 ma111 mb10 mb11 mc1
242  * m[0] m[1] must be positive
243  *@return a vector contains real roots
244  */
245 std::vector<lc::geo::Coordinate> Math::simultaneousQuadraticSolver(const std::vector<double>& m) {
246  std::vector<lc::geo::Coordinate> ret(0);
247 
248  if (m.size() != 8) {
249  return ret; // valid m should contain exact 8 elements
250  }
251 
252  std::vector< double> c1(0, 0.);
253  std::vector< std::vector<double> > m1(0, c1);
254  c1.resize(6);
255  c1[0] = m[0];
256  c1[1] = 0.;
257  c1[2] = m[1];
258  c1[3] = 0.;
259  c1[4] = 0.;
260  c1[5] = -1.;
261  m1.push_back(c1);
262  c1[0] = m[2];
263  c1[1] = 2.*m[3];
264  c1[2] = m[4];
265  c1[3] = m[5];
266  c1[4] = m[6];
267  c1[5] = m[7];
268  m1.push_back(c1);
269 
270  return simultaneousQuadraticSolverFull(m1);
271 }
272 
274 /* solve the following quadratic simultaneous equations,
275  * ma000 x^2 + ma001 xy + ma011 y^2 + mb00 x + mb01 y + mc0 =0
276  * ma100 x^2 + ma101 xy + ma111 y^2 + mb10 x + mb11 y + mc1 =0
277  *
278  *@m, a vector of size 2 each contains a vector of size 6 coefficients in the strict order of:
279  ma000 ma001 ma011 mb00 mb01 mc0
280  ma100 ma101 ma111 mb10 mb11 mc1
281  *@return a CoordinateSolutions contains real roots (x,y)
282  */
283 std::vector<lc::geo::Coordinate> Math::simultaneousQuadraticSolverFull(const std::vector<std::vector<double> >& m) {
284  std::vector<lc::geo::Coordinate> ret;
285 
286  if (m.size() != 2) {
287  return ret;
288  }
289 
290  if (m[0].size() == 3 || m[1].size() == 3) {
291  return simultaneousQuadraticSolverMixed(m);
292  }
293 
294  if (m[0].size() != 6 || m[1].size() != 6) {
295  return ret;
296  }
297 
299  auto& a = m[0][0];
300  auto& b = m[0][1];
301  auto& c = m[0][2];
302  auto& d = m[0][3];
303  auto& e = m[0][4];
304  auto& f = m[0][5];
305 
306  auto& g = m[1][0];
307  auto& h = m[1][1];
308  auto& i = m[1][2];
309  auto& j = m[1][3];
310  auto& k = m[1][4];
311  auto& l = m[1][5];
315  /*
316  f^2 g^2 - d f g j + a f j^2 - 2 a f g l + (2 e f g^2 - d f g h - b f g j + 2 a f h j - 2 a f g k) y + (2 c f g^2 - b f g h + a f h^2 - 2 a f g i) y^2
317  ==
318  -(d^2 g l) + a d j l - a^2 l^2
319  +
320  (d e g j - a e j^2 - d^2 g k + a d j k - 2 b d g l + 2 a e g l + a d h l + a b j l - 2 a^2 k l) y
321  +
322  (-(e^2 g^2) + d e g h - d^2 g i + c d g j + b e g j - 2 a e h j + a d i j - a c j^2 - 2 b d g k + 2 a e g k + a d h k + a b j k - a^2 k^2 - b^2 g l + 2 a c g l + a b h l - 2 a^2 i l) y^2
323  +
324  (-2 c e g^2 + c d g h + b e g h - a e h^2 - 2 b d g i + 2 a e g i + a d h i + b c g j - 2 a c h j + a b i j - b^2 g k + 2 a c g k + a b h k - 2 a^2 i k) y^3
325  +
326  (-(c^2 g^2) + b c g h - a c h^2 - b^2 g i + 2 a c g i + a b h i - a^2 i^2) y^4
327 
328 
329  */
330  double a2 = a * a;
331  double b2 = b * b;
332  double c2 = c * c;
333  double d2 = d * d;
334  double e2 = e * e;
335  double f2 = f * f;
336 
337  double g2 = g * g;
338  double h2 = h * h;
339  double i2 = i * i;
340  double j2 = j * j;
341  double k2 = k * k;
342  double l2 = l * l;
343  std::vector<double> qy(5, 0.);
344  //y^4
345  qy[4] = -c2 * g2 + b * c * g * h - a * c * h2 - b2 * g * i + 2.*a * c * g * i + a * b * h * i - a2 * i2;
346  //y^3
347  qy[3] = -2.*c * e * g2 + c * d * g * h + b * e * g * h - a * e * h2 - 2.*b * d * g * i + 2.*a * e * g * i + a * d * h * i +
348  b * c * g * j - 2.*a * c * h * j + a * b * i * j - b2 * g * k + 2.*a * c * g * k + a * b * h * k - 2.*a2 * i * k;
349  //y^2
350  qy[2] = (-e2 * g2 + d * e * g * h - d2 * g * i + c * d * g * j + b * e * g * j - 2.*a * e * h * j + a * d * i * j - a * c * j2 -
351  2.*b * d * g * k + 2.*a * e * g * k + a * d * h * k + a * b * j * k - a2 * k2 - b2 * g * l + 2.*a * c * g * l + a * b * h * l - 2.*a2 * i * l)
352  - (2.*c * f * g2 - b * f * g * h + a * f * h2 - 2.*a * f * g * i);
353  //y
354  qy[1] = (d * e * g * j - a * e * j2 - d2 * g * k + a * d * j * k - 2.*b * d * g * l + 2.*a * e * g * l + a * d * h * l + a * b * j * l - 2.*a2 * k * l)
355  - (2.*e * f * g2 - d * f * g * h - b * f * g * j + 2.*a * f * h * j - 2.*a * f * g * k);
356  //y^0
357  qy[0] = -d2 * g * l + a * d * j * l - a2 * l2
358  - (f2 * g2 - d * f * g * j + a * f * j2 - 2.*a * f * g * l);
359  //quarticSolver
360  auto&& roots = quarticSolverFull(qy);
361 
362  if (roots.size() == 0) { // no intersection found
363  return ret;
364  }
365 
366  std::vector<double> ce(0, 0.);
367 
368  for (size_t i0 = 0; i0 < roots.size(); i0++) {
369  /*
370  Collect[Eliminate[{ a*x^2 + b*x*y+c*y^2+d*x+e*y+f==0,g*x^2+h*x*y+i*y^2+j*x+k*y+l==0},x],y]
371  */
372  ce.resize(3);
373  ce[0] = a;
374  ce[1] = b * roots[i0] + d;
375  ce[2] = c * roots[i0] * roots[i0] + e * roots[i0] + f;
376 
377  if (std::abs(ce[0]) < 1e-75 && std::abs(ce[1]) < 1e-75) {
378  ce[0] = g;
379  ce[1] = h * roots[i0] + j;
380  ce[2] = i * roots[i0] * roots[i0] + k * roots[i0] + f;
381  }
382 
383  if (std::abs(ce[0]) < 1e-75 && std::abs(ce[1]) < 1e-75) {
384  continue;
385  }
386 
387  if (std::abs(a) > 1e-75) {
388  std::vector<double> ce2(2, 0.);
389  ce2[0] = ce[1] / ce[0];
390  ce2[1] = ce[2] / ce[0];
391  auto&& xRoots = quadraticSolver(ce2);
392 
393  for (size_t j0 = 0; j0 < xRoots.size(); j0++) {
394  geo::Coordinate vp(xRoots[j0], roots[i0]);
395 
396  if (simultaneousQuadraticVerify(m, vp)) {
397  ret.push_back(vp);
398  }
399  }
400 
401  continue;
402  }
403 
404  geo::Coordinate vp(-ce[2] / ce[1], roots[i0]);
405 
406  if (simultaneousQuadraticVerify(m, vp)) {
407  ret.push_back(vp);
408  }
409  }
410 
411  return ret;
412 }
413 
414 std::vector<lc::geo::Coordinate> Math::simultaneousQuadraticSolverMixed(const std::vector<std::vector<double> >& m) {
415  std::vector<lc::geo::Coordinate> ret;
416  auto p0 = & (m[0]);
417  auto p1 = & (m[1]);
418 
419  if (p1->size() == 3) {
420  std::swap(p0, p1);
421  }
422 
423  if (p1->size() == 3) {
424  //linear
425  Eigen::Matrix2d M;
426  Eigen::Vector2d V;
427  M << m[0][0], m[0][1],
428  m[1][0], m[1][1];
429  V << -m[0][2], -m[1][2];
430  Eigen::Vector2d sn = M.colPivHouseholderQr().solve(V);
431  ret.push_back(geo::Coordinate(sn[0], sn[1]));
432  return ret;
433  }
434 
435  const double& a = p0->at(0);
436  const double& b = p0->at(1);
437  const double& c = p0->at(2);
438  const double& d = p1->at(0);
439  const double& e = p1->at(1);
440  const double& f = p1->at(2);
441  const double& g = p1->at(3);
442  const double& h = p1->at(4);
443  const double& i = p1->at(5);
447  std::vector<double> ce(3, 0.);
448  const double&& a2 = a * a;
449  const double&& b2 = b * b;
450  const double&& c2 = c * c;
451  ce[0] = -f * a2 + a * b * e - b2 * d;
452  ce[1] = a * b * g - a2 * h - (2 * b * c * d - a * c * e);
453  ce[2] = a * c * g - c2 * d - a2 * i;
454  std::vector<double> roots(0, 0.);
455 
456  if (std::abs(ce[0]) < 1e-75) {
457  if (std::abs(ce[1]) > 1e-75) {
458  roots.push_back(- ce[2] / ce[1]);
459  }
460  } else {
461  std::vector<double> ce2(2, 0.);
462  ce2[0] = ce[1] / ce[0];
463  ce2[1] = ce[2] / ce[0];
464  roots = quadraticSolver(ce2);
465  }
466 
467  if (roots.size() == 0) {
468  return ret;
469  }
470 
471  for (size_t i = 0; i < roots.size(); i++) {
472  ret.push_back(geo::Coordinate(-(b * roots.at(i) + c) / a, roots.at(i)));
473  }
474 
475  return ret;
476 
477 }
478 
484 bool Math::simultaneousQuadraticVerify(const std::vector<std::vector<double> >& m, const geo::Coordinate& v) {
485  const double& x = v.x();
486  const double& y = v.y();
487  const double x2 = x * x;
488  const double y2 = y * y;
489  auto& a = m[0][0];
490  auto& b = m[0][1];
491  auto& c = m[0][2];
492  auto& d = m[0][3];
493  auto& e = m[0][4];
494  auto& f = m[0][5];
495 
496  auto& g = m[1][0];
497  auto& h = m[1][1];
498  auto& i = m[1][2];
499  auto& j = m[1][3];
500  auto& k = m[1][4];
501  auto& l = m[1][5];
506  double terms0[12] = { a * x2, b* x * y, c * y2, d * x, e * y, f, g * x2, h* x * y, i * y2, j * x, k * y, l};
507  double amax0 = std::abs(terms0[0]), amax1 = std::abs(terms0[6]);
508  double sum0 = 0., sum1 = 0.;
509 
510  for (int i = 0; i < 6; i++) {
511  if (amax0 < std::abs(terms0[i])) {
512  amax0 = std::abs(terms0[i]);
513  }
514 
515  sum0 += terms0[i];
516  }
517 
518  for (int i = 6; i < 12; i++) {
519  if (amax1 < std::abs(terms0[i])) {
520  amax1 = std::abs(terms0[i]);
521  }
522 
523  sum1 += terms0[i];
524  }
525 
526  const double tols = 2.*sqrt(6.) * sqrt(DBL_EPSILON); //experimental tolerances to verify simultaneous quadratic
527 
528  return (amax0 <= tols || std::abs(sum0) / amax0 < tols) && (amax1 <= tols || std::abs(sum1) / amax1 < tols);
529 }
static double getAngleDifferenceShort(double a1, double a2, bool CCW)
getAngleDifference, Angle difference between 2 angles
Definition: lcmath.cpp:41
static std::vector< double > quadraticSolver(const std::vector< double > &ce)
quadraticSolver, Quadratic equations solver
Definition: lcmath.cpp:77
static std::vector< double > cubicSolver(const std::vector< double > &ce)
Definition: lcmath.cpp:102
static std::vector< lc::geo::Coordinate > simultaneousQuadraticSolverMixed(const std::vector< std::vector< double > > &m)
Definition: lcmath.cpp:414
static std::vector< lc::geo::Coordinate > simultaneousQuadraticSolver(const std::vector< double > &m)
Definition: lcmath.cpp:245
double x() const
Returns x of Coordinate.
Definition: geocoordinate.h:26
static std::vector< double > sexticSolver(const std::vector< double > &ce)
Definition: lcmath.cpp:151
double y() const
Returns y of Coordinate.
Definition: geocoordinate.h:34
static bool simultaneousQuadraticVerify(const std::vector< std::vector< double > > &m, const geo::Coordinate &v)
Definition: lcmath.cpp:484
Definition: cadentity.h:12
static std::vector< double > quarticSolverFull(const std::vector< double > &ce)
Definition: lcmath.cpp:183
static double getAngleDifference(double a1, double a2, bool CCW)
getAngleDifference, Angle difference between 2 angles
Definition: lcmath.cpp:54
static std::vector< lc::geo::Coordinate > simultaneousQuadraticSolverFull(const std::vector< std::vector< double > > &m)
simultaneousQuadraticSolverFull
Definition: lcmath.cpp:283
#define M_PI
Definition: const.h:16
static double correctAngle(double a)
correctAngle, Corrects angle to be in -PI to PI
Definition: lcmath.cpp:33
static bool isAngleBetween(double a, double start, double end, bool CCW)
isAngleBetween, checks if angle is between
Definition: lcmath.cpp:21
#define TOLERANCE15
Definition: const.h:11
static std::vector< double > quarticSolver(const std::vector< double > &ce)
Definition: lcmath.cpp:127