QuadricSurface.h 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/Logger.h>
  9. #include <Mathematics/ArbitraryPrecision.h>
  10. #include <Mathematics/Matrix3x3.h>
  11. // A quadric surface is defined implicitly by
  12. //
  13. // 0 = a0 + a1*x[0] + a2*x[1] + a3*x[2] + a4*x[0]^2 + a5*x[0]*x[1] +
  14. // a6*x[0]*x[2] + a7*x[1]^2 + a8*x[1]*x[2] + a9*x[2]^2
  15. //
  16. // = a0 + [a1 a2 a3]*X + X^T*[a4 a5/2 a6/2]*X
  17. // [a5/2 a7 a8/2]
  18. // [a6/2 a8/2 a9 ]
  19. // = C + B^T*X + X^T*A*X
  20. //
  21. // The matrix A is symmetric.
  22. namespace WwiseGTE
  23. {
  24. template <typename Real>
  25. class QuadricSurface
  26. {
  27. public:
  28. // Construction and destruction. The default constructor sets all
  29. // coefficients to zero.
  30. QuadricSurface()
  31. {
  32. mCoefficient.fill((Real)0);
  33. mC = (Real)0;
  34. mB.MakeZero();
  35. mA.MakeZero();
  36. }
  37. QuadricSurface(std::array<Real, 10> const& coefficient)
  38. :
  39. mCoefficient(coefficient)
  40. {
  41. mC = mCoefficient[0];
  42. mB[0] = mCoefficient[1];
  43. mB[1] = mCoefficient[2];
  44. mB[2] = mCoefficient[3];
  45. mA(0, 0) = mCoefficient[4];
  46. mA(0, 1) = (Real)0.5 * mCoefficient[5];
  47. mA(0, 2) = (Real)0.5 * mCoefficient[6];
  48. mA(1, 0) = mA(0, 1);
  49. mA(1, 1) = mCoefficient[7];
  50. mA(1, 2) = (Real)0.5 * mCoefficient[8];
  51. mA(2, 0) = mA(0, 2);
  52. mA(2, 1) = mA(1, 2);
  53. mA(2, 2) = mCoefficient[9];
  54. }
  55. // Member access.
  56. inline std::array<Real, 10> const& GetCoefficients() const
  57. {
  58. return mCoefficient;
  59. }
  60. inline Real const& GetC() const
  61. {
  62. return mC;
  63. }
  64. inline Vector3<Real> const& GetB() const
  65. {
  66. return mB;
  67. }
  68. inline Matrix3x3<Real> const& GetA() const
  69. {
  70. return mA;
  71. }
  72. // Evaluate the function.
  73. Real F(Vector3<Real> const& position) const
  74. {
  75. Real f = Dot(position, mA * position + mB) + mC;
  76. return f;
  77. }
  78. // Evaluate the first-order partial derivatives (gradient).
  79. Real FX(Vector3<Real> const& position) const
  80. {
  81. Real sum = mA(0, 0) * position[0] + mA(0, 1) * position[1] + mA(0, 2) * position[2];
  82. Real fx = (Real)2 * sum + mB[0];
  83. return fx;
  84. }
  85. Real FY(Vector3<Real> const& position) const
  86. {
  87. Real sum = mA(1, 0) * position[0] + mA(1, 1) * position[1] + mA(1, 2) * position[2];
  88. Real fy = (Real)2 * sum + mB[1];
  89. return fy;
  90. }
  91. Real FZ(Vector3<Real> const& position) const
  92. {
  93. Real sum = mA(2, 0) * position[0] + mA(2, 1) * position[1] + mA(2, 2) * position[2];
  94. Real fz = (Real)2 * sum + mB[2];
  95. return fz;
  96. }
  97. // Evaluate the second-order partial derivatives (Hessian).
  98. Real FXX(Vector3<Real> const&) const
  99. {
  100. Real fxx = (Real)2 * mA(0, 0);
  101. return fxx;
  102. }
  103. Real FXY(Vector3<Real> const&) const
  104. {
  105. Real fxy = (Real)2 * mA(0, 1);
  106. return fxy;
  107. }
  108. Real FXZ(Vector3<Real> const&) const
  109. {
  110. Real fxz = (Real)2 * mA(0, 2);
  111. return fxz;
  112. }
  113. Real FYY(Vector3<Real> const&) const
  114. {
  115. Real fyy = (Real)2 * mA(1, 1);
  116. return fyy;
  117. }
  118. Real FYZ(Vector3<Real> const&) const
  119. {
  120. Real fyz = (Real)2 * mA(1, 2);
  121. return fyz;
  122. }
  123. Real FZZ(Vector3<Real> const&) const
  124. {
  125. Real fzz = (Real)2 * mA(2, 2);
  126. return fzz;
  127. }
  128. // Classification of the quadric. The implementation uses exact
  129. // rational arithmetic to avoid misclassification due to
  130. // floating-point rounding errors.
  131. enum Classification
  132. {
  133. QT_NONE,
  134. QT_POINT,
  135. QT_LINE,
  136. QT_PLANE,
  137. QT_TWO_PLANES,
  138. QT_PARABOLIC_CYLINDER,
  139. QT_ELLIPTIC_CYLINDER,
  140. QT_HYPERBOLIC_CYLINDER,
  141. QT_ELLIPTIC_PARABOLOID,
  142. QT_HYPERBOLIC_PARABOLOID,
  143. QT_ELLIPTIC_CONE,
  144. QT_HYPERBOLOID_ONE_SHEET,
  145. QT_HYPERBOLOID_TWO_SHEETS,
  146. QT_ELLIPSOID
  147. };
  148. Classification GetClassification() const
  149. {
  150. // Convert the coefficients to their rational representations and
  151. // compute various derived quantities.
  152. RReps reps(mCoefficient);
  153. // Use Sturm sequences to determine the signs of the roots.
  154. int positiveRoots, negativeRoots, zeroRoots;
  155. GetRootSigns(reps, positiveRoots, negativeRoots, zeroRoots);
  156. // Classify the solution set to the equation.
  157. Classification type = QT_NONE;
  158. switch (zeroRoots)
  159. {
  160. case 0:
  161. type = ClassifyZeroRoots0(reps, positiveRoots);
  162. break;
  163. case 1:
  164. type = ClassifyZeroRoots1(reps, positiveRoots);
  165. break;
  166. case 2:
  167. type = ClassifyZeroRoots2(reps, positiveRoots);
  168. break;
  169. case 3:
  170. type = ClassifyZeroRoots3(reps);
  171. break;
  172. }
  173. return type;
  174. }
  175. private:
  176. typedef BSRational<UIntegerAP32> Rational;
  177. typedef Vector<3, Rational> RVector3;
  178. class RReps
  179. {
  180. public:
  181. RReps(std::array<Real, 10> const& coefficient)
  182. {
  183. Rational half = (Real)0.5;
  184. c = Rational(coefficient[0]);
  185. b0 = Rational(coefficient[1]);
  186. b1 = Rational(coefficient[2]);
  187. b2 = Rational(coefficient[3]);
  188. a00 = Rational(coefficient[4]);
  189. a01 = half * Rational(coefficient[5]);
  190. a02 = half * Rational(coefficient[6]);
  191. a11 = Rational(coefficient[7]);
  192. a12 = half * Rational(coefficient[8]);
  193. a22 = Rational(coefficient[9]);
  194. sub00 = a11 * a22 - a12 * a12;
  195. sub01 = a01 * a22 - a12 * a02;
  196. sub02 = a01 * a12 - a02 * a11;
  197. sub11 = a00 * a22 - a02 * a02;
  198. sub12 = a00 * a12 - a02 * a01;
  199. sub22 = a00 * a11 - a01 * a01;
  200. k0 = a00 * sub00 - a01 * sub01 + a02 * sub02;
  201. k1 = sub00 + sub11 + sub22;
  202. k2 = a00 + a11 + a22;
  203. k3 = Rational(0);
  204. k4 = Rational(0);
  205. k5 = Rational(0);
  206. }
  207. // Quadratic coefficients.
  208. Rational a00, a01, a02, a11, a12, a22, b0, b1, b2, c;
  209. // 2-by-2 determinants
  210. Rational sub00, sub01, sub02, sub11, sub12, sub22;
  211. // Characteristic polynomial L^3 - k2 * L^2 + k1 * L - k0.
  212. Rational k0, k1, k2;
  213. // For Sturm sequences.
  214. Rational k3, k4, k5;
  215. };
  216. static void GetRootSigns(RReps& reps, int& positiveRoots, int& negativeRoots, int& zeroRoots)
  217. {
  218. // Use Sturm sequences to determine the signs of the roots.
  219. int signChangeMI, signChange0, signChangePI, distinctNonzeroRoots;
  220. std::array<Rational, 4> value;
  221. Rational const zero(0);
  222. if (reps.k0 != zero)
  223. {
  224. reps.k3 = Rational(2, 9) * reps.k2 * reps.k2 - Rational(2, 3) * reps.k1;
  225. reps.k4 = reps.k0 - Rational(1, 9) * reps.k1 * reps.k2;
  226. if (reps.k3 != zero)
  227. {
  228. reps.k5 = -(reps.k1 + ((Rational(2) * reps.k2 * reps.k3 +
  229. Rational(3) * reps.k4) * reps.k4) / (reps.k3 * reps.k3));
  230. value[0] = 1;
  231. value[1] = -reps.k3;
  232. value[2] = reps.k5;
  233. signChangeMI = 1 + GetSignChanges(3, value);
  234. value[0] = -reps.k0;
  235. value[1] = reps.k1;
  236. value[2] = reps.k4;
  237. value[3] = reps.k5;
  238. signChange0 = GetSignChanges(4, value);
  239. value[0] = 1;
  240. value[1] = reps.k3;
  241. value[2] = reps.k5;
  242. signChangePI = GetSignChanges(3, value);
  243. }
  244. else
  245. {
  246. value[0] = -reps.k0;
  247. value[1] = reps.k1;
  248. value[2] = reps.k4;
  249. signChange0 = GetSignChanges(3, value);
  250. value[0] = 1;
  251. value[1] = reps.k4;
  252. signChangePI = GetSignChanges(2, value);
  253. signChangeMI = 1 + signChangePI;
  254. }
  255. positiveRoots = signChange0 - signChangePI;
  256. LogAssert(positiveRoots >= 0, "Unexpected condition.");
  257. negativeRoots = signChangeMI - signChange0;
  258. LogAssert(negativeRoots >= 0, "Unexpected condition.");
  259. zeroRoots = 0;
  260. distinctNonzeroRoots = positiveRoots + negativeRoots;
  261. if (distinctNonzeroRoots == 2)
  262. {
  263. if (positiveRoots == 2)
  264. {
  265. positiveRoots = 3;
  266. }
  267. else if (negativeRoots == 2)
  268. {
  269. negativeRoots = 3;
  270. }
  271. else
  272. {
  273. // One root is positive and one is negative. One root
  274. // has multiplicity 2, the other of multiplicity 1.
  275. // Distinguish between the two cases by computing the
  276. // sign of the polynomial at the inflection point
  277. // L = k2/3.
  278. Rational X = Rational(1, 3) * reps.k2;
  279. Rational poly = X * (X * (X - reps.k2) + reps.k1) - reps.k0;
  280. if (poly > zero)
  281. {
  282. positiveRoots = 2;
  283. }
  284. else
  285. {
  286. negativeRoots = 2;
  287. }
  288. }
  289. }
  290. else if (distinctNonzeroRoots == 1)
  291. {
  292. // Root of multiplicity 3.
  293. if (positiveRoots == 1)
  294. {
  295. positiveRoots = 3;
  296. }
  297. else
  298. {
  299. negativeRoots = 3;
  300. }
  301. }
  302. return;
  303. }
  304. if (reps.k1 != zero)
  305. {
  306. reps.k3 = Rational(1, 4) * reps.k2 * reps.k2 - reps.k1;
  307. value[0] = -1;
  308. value[1] = reps.k3;
  309. signChangeMI = 1 + GetSignChanges(2, value);
  310. value[0] = reps.k1;
  311. value[1] = -reps.k2;
  312. value[2] = reps.k3;
  313. signChange0 = GetSignChanges(3, value);
  314. value[0] = 1;
  315. value[1] = reps.k3;
  316. signChangePI = GetSignChanges(2, value);
  317. positiveRoots = signChange0 - signChangePI;
  318. LogAssert(positiveRoots >= 0, "Unexpected condition.");
  319. negativeRoots = signChangeMI - signChange0;
  320. LogAssert(negativeRoots >= 0, "Unexpected condition.");
  321. zeroRoots = 1;
  322. distinctNonzeroRoots = positiveRoots + negativeRoots;
  323. if (distinctNonzeroRoots == 1)
  324. {
  325. positiveRoots = 2;
  326. }
  327. return;
  328. }
  329. if (reps.k2 != zero)
  330. {
  331. zeroRoots = 2;
  332. if (reps.k2 > zero)
  333. {
  334. positiveRoots = 1;
  335. negativeRoots = 0;
  336. }
  337. else
  338. {
  339. positiveRoots = 0;
  340. negativeRoots = 1;
  341. }
  342. return;
  343. }
  344. positiveRoots = 0;
  345. negativeRoots = 0;
  346. zeroRoots = 3;
  347. }
  348. static int GetSignChanges(int quantity, std::array<Rational, 4> const& value)
  349. {
  350. int signChanges = 0;
  351. Rational const zero(0);
  352. Rational prev = value[0];
  353. for (int i = 1; i < quantity; ++i)
  354. {
  355. Rational next = value[i];
  356. if (next != zero)
  357. {
  358. if (prev * next < zero)
  359. {
  360. ++signChanges;
  361. }
  362. prev = next;
  363. }
  364. }
  365. return signChanges;
  366. }
  367. static Classification ClassifyZeroRoots0(RReps const& reps, int positiveRoots)
  368. {
  369. // The inverse matrix is
  370. // +- -+
  371. // | sub00 -sub01 sub02 |
  372. // | -sub01 sub11 -sub12 | * (1/det)
  373. // | sub02 -sub12 sub22 |
  374. // +- -+
  375. Rational fourDet = Rational(4) * reps.k0;
  376. Rational qForm = reps.b0 * (reps.sub00 * reps.b0 -
  377. reps.sub01 * reps.b1 + reps.sub02 * reps.b2) -
  378. reps.b1 * (reps.sub01 * reps.b0 - reps.sub11 * reps.b1 +
  379. reps.sub12 * reps.b2) + reps.b2 * (reps.sub02 * reps.b0 -
  380. reps.sub12 * reps.b1 + reps.sub22 * reps.b2);
  381. Rational r = Rational(1, 4) * qForm / fourDet - reps.c;
  382. Rational const zero(0);
  383. if (r > zero)
  384. {
  385. if (positiveRoots == 3)
  386. {
  387. return QT_ELLIPSOID;
  388. }
  389. else if (positiveRoots == 2)
  390. {
  391. return QT_HYPERBOLOID_ONE_SHEET;
  392. }
  393. else if (positiveRoots == 1)
  394. {
  395. return QT_HYPERBOLOID_TWO_SHEETS;
  396. }
  397. else
  398. {
  399. return QT_NONE;
  400. }
  401. }
  402. else if (r < zero)
  403. {
  404. if (positiveRoots == 3)
  405. {
  406. return QT_NONE;
  407. }
  408. else if (positiveRoots == 2)
  409. {
  410. return QT_HYPERBOLOID_TWO_SHEETS;
  411. }
  412. else if (positiveRoots == 1)
  413. {
  414. return QT_HYPERBOLOID_ONE_SHEET;
  415. }
  416. else
  417. {
  418. return QT_ELLIPSOID;
  419. }
  420. }
  421. // else r == 0
  422. if (positiveRoots == 3 || positiveRoots == 0)
  423. {
  424. return QT_POINT;
  425. }
  426. return QT_ELLIPTIC_CONE;
  427. }
  428. static Classification ClassifyZeroRoots1(RReps const& reps, int positiveRoots)
  429. {
  430. // Generate an orthonormal set {p0,p1,p2}, where p0 is an
  431. // eigenvector of A corresponding to eigenvalue zero.
  432. RVector3 P0, P1, P2;
  433. Rational const zero(0);
  434. if (reps.sub00 != zero || reps.sub01 != zero || reps.sub02 != zero)
  435. {
  436. // Rows 1 and 2 are linearly independent.
  437. P0 = { reps.sub00, -reps.sub01, reps.sub02 };
  438. P1 = { reps.a01, reps.a11, reps.a12 };
  439. P2 = Cross(P0, P1);
  440. return ClassifyZeroRoots1(reps, positiveRoots, P0, P1, P2);
  441. }
  442. if (reps.sub01 != zero || reps.sub11 != zero || reps.sub12 != zero)
  443. {
  444. // Rows 2 and 0 are linearly independent.
  445. P0 = { -reps.sub01, reps.sub11, -reps.sub12 };
  446. P1 = { reps.a02, reps.a12, reps.a22 };
  447. P2 = Cross(P0, P1);
  448. return ClassifyZeroRoots1(reps, positiveRoots, P0, P1, P2);
  449. }
  450. // Rows 0 and 1 are linearly independent.
  451. P0 = { reps.sub02, -reps.sub12, reps.sub22 };
  452. P1 = { reps.a00, reps.a01, reps.a02 };
  453. P2 = Cross(P0, P1);
  454. return ClassifyZeroRoots1(reps, positiveRoots, P0, P1, P2);
  455. }
  456. static Classification ClassifyZeroRoots1(RReps const& reps, int positiveRoots,
  457. RVector3 const& P0, RVector3 const& P1, RVector3 const& P2)
  458. {
  459. Rational const zero(0);
  460. Rational e0 = P0[0] * reps.b0 + P0[1] * reps.b1 + P0[2] * reps.b2;
  461. if (e0 != zero)
  462. {
  463. if (positiveRoots == 1)
  464. {
  465. return QT_HYPERBOLIC_PARABOLOID;
  466. }
  467. else
  468. {
  469. return QT_ELLIPTIC_PARABOLOID;
  470. }
  471. }
  472. // Matrix F.
  473. Rational f11 = P1[0] * (reps.a00 * P1[0] + reps.a01 * P1[1] +
  474. reps.a02 * P1[2]) + P1[1] * (reps.a01 * P1[0] +
  475. reps.a11 * P1[1] + reps.a12 * P1[2]) + P1[2] * (
  476. reps.a02 * P1[0] + reps.a12 * P1[1] + reps.a22 * P1[2]);
  477. Rational f12 = P2[0] * (reps.a00 * P1[0] + reps.a01 * P1[1] +
  478. reps.a02 * P1[2]) + P2[1] * (reps.a01 * P1[0] +
  479. reps.a11 * P1[1] + reps.a12 * P1[2]) + P2[2] * (
  480. reps.a02 * P1[0] + reps.a12 * P1[1] + reps.a22 * P1[2]);
  481. Rational f22 = P2[0] * (reps.a00 * P2[0] + reps.a01 * P2[1] +
  482. reps.a02 * P2[2]) + P2[1] * (reps.a01 * P2[0] +
  483. reps.a11 * P2[1] + reps.a12 * P2[2]) + P2[2] * (
  484. reps.a02 * P2[0] + reps.a12 * P2[1] + reps.a22 * P2[2]);
  485. // Vector g.
  486. Rational g1 = P1[0] * reps.b0 + P1[1] * reps.b1 + P1[2] * reps.b2;
  487. Rational g2 = P2[0] * reps.b0 + P2[1] * reps.b1 + P2[2] * reps.b2;
  488. // Compute g^T*F^{-1}*g/4 - c.
  489. Rational fourDet = Rational(4) * (f11 * f22 - f12 * f12);
  490. Rational r = (g1 * (f22 * g1 - f12 * g2) + g2 * (f11 * g2 - f12 * g1)) / fourDet - reps.c;
  491. if (r > zero)
  492. {
  493. if (positiveRoots == 2)
  494. {
  495. return QT_ELLIPTIC_CYLINDER;
  496. }
  497. else if (positiveRoots == 1)
  498. {
  499. return QT_HYPERBOLIC_CYLINDER;
  500. }
  501. else
  502. {
  503. return QT_NONE;
  504. }
  505. }
  506. else if (r < zero)
  507. {
  508. if (positiveRoots == 2)
  509. {
  510. return QT_NONE;
  511. }
  512. else if (positiveRoots == 1)
  513. {
  514. return QT_HYPERBOLIC_CYLINDER;
  515. }
  516. else
  517. {
  518. return QT_ELLIPTIC_CYLINDER;
  519. }
  520. }
  521. // else r == 0
  522. return (positiveRoots == 1 ? QT_TWO_PLANES : QT_LINE);
  523. }
  524. static Classification ClassifyZeroRoots2(const RReps& reps, int positiveRoots)
  525. {
  526. // Generate an orthonormal set {p0,p1,p2}, where p0 and p1 are
  527. // eigenvectors of A corresponding to eigenvalue zero. The vector
  528. // p2 is an eigenvector of A corresponding to eigenvalue c2.
  529. Rational const zero(0);
  530. RVector3 P0, P1, P2;
  531. if (reps.a00 != zero || reps.a01 != zero || reps.a02 != zero)
  532. {
  533. // row 0 is not zero
  534. P2 = { reps.a00, reps.a01, reps.a02 };
  535. }
  536. else if (reps.a01 != zero || reps.a11 != zero || reps.a12 != zero)
  537. {
  538. // row 1 is not zero
  539. P2 = { reps.a01, reps.a11, reps.a12 };
  540. }
  541. else
  542. {
  543. // row 2 is not zero
  544. P2 = { reps.a02, reps.a12, reps.a22 };
  545. }
  546. if (P2[0] != zero)
  547. {
  548. P1[0] = P2[1];
  549. P1[1] = -P2[0];
  550. P1[2] = zero;
  551. }
  552. else
  553. {
  554. P1[0] = zero;
  555. P1[1] = P2[2];
  556. P1[2] = -P2[1];
  557. }
  558. P0 = Cross(P1, P2);
  559. return ClassifyZeroRoots2(reps, positiveRoots, P0, P1, P2);
  560. }
  561. static Classification ClassifyZeroRoots2(RReps const& reps, int positiveRoots,
  562. RVector3 const& P0, RVector3 const& P1, RVector3 const& P2)
  563. {
  564. Rational const zero(0);
  565. Rational e0 = P0[0] * reps.b0 + P0[1] * reps.b1 + P0[2] * reps.b1;
  566. if (e0 != zero)
  567. {
  568. return QT_PARABOLIC_CYLINDER;
  569. }
  570. Rational e1 = P1[0] * reps.b0 + P1[1] * reps.b1 + P1[2] * reps.b1;
  571. if (e1 != zero)
  572. {
  573. return QT_PARABOLIC_CYLINDER;
  574. }
  575. Rational f1 = reps.k2 * Dot(P2, P2);
  576. Rational e2 = P2[0] * reps.b0 + P2[1] * reps.b1 + P2[2] * reps.b1;
  577. Rational r = e2 * e2 / (Rational(4) * f1) - reps.c;
  578. if (r > zero)
  579. {
  580. if (positiveRoots == 1)
  581. {
  582. return QT_TWO_PLANES;
  583. }
  584. else
  585. {
  586. return QT_NONE;
  587. }
  588. }
  589. else if (r < zero)
  590. {
  591. if (positiveRoots == 1)
  592. {
  593. return QT_NONE;
  594. }
  595. else
  596. {
  597. return QT_TWO_PLANES;
  598. }
  599. }
  600. // else r == 0
  601. return QT_PLANE;
  602. }
  603. static Classification ClassifyZeroRoots3(RReps const& reps)
  604. {
  605. Rational const zero(0);
  606. if (reps.b0 != zero || reps.b1 != zero || reps.b2 != zero)
  607. {
  608. return QT_PLANE;
  609. }
  610. return QT_NONE;
  611. }
  612. std::array<Real, 10> mCoefficient;
  613. Real mC;
  614. Vector3<Real> mB;
  615. Matrix3x3<Real> mA;
  616. };
  617. }