File indexing completed on 2024-12-15 04:01:13

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "geom.hpp"
0008 #include <array>
0009 #include "math.hpp"
0010 
0011 using namespace glaxnimate;
0012 
0013 
0014 QPointF math::line_closest_point(const QPointF& line_a, const QPointF& line_b, const QPointF& p)
0015 {
0016     QPointF a_to_p = p - line_a;
0017     QPointF a_to_b = line_b - line_a;
0018 
0019     qreal atb2 = length_squared(a_to_b);
0020     qreal atp_dot_atb = QPointF::dotProduct(a_to_p, a_to_b);
0021     qreal t = atp_dot_atb / atb2;
0022 
0023     return line_a + a_to_b * t;
0024 }
0025 
0026 // Algorithm from http://ambrsoft.com/TrigoCalc/Circle3D.htm
0027 QPointF math::circle_center(const QPointF& p1, const QPointF& p2, const QPointF& p3)
0028 {
0029     qreal x1 = p1.x();
0030     qreal x2 = p2.x();
0031     qreal x3 = p3.x();
0032     qreal y1 = p1.y();
0033     qreal y2 = p2.y();
0034     qreal y3 = p3.y();
0035     qreal A = 2 * (x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2);
0036     qreal p12 = x1*x1 + y1*y1;
0037     qreal p22 = x2*x2 + y2*y2;
0038     qreal p32 = x3*x3 + y3*y3;
0039     qreal B = p12 * (y3 - y2) + p22 * (y1 - y3) + p32 * (y2 - y1);
0040     qreal C = p12 * (x2 - x3) + p22 * (x3 - x1) + p32 * (x1 - x2);
0041 
0042     return {
0043         - B / A,
0044         - C / A
0045     };
0046 }
0047 
0048 // Custom implementation rather than using QVector3D to keep precision
0049 static std::array<qreal, 3> cross_product(const std::array<qreal, 3>& a, const std::array<qreal, 3>& b)
0050 {
0051     return {
0052         a[1] * b[2] - a[2] * b[1],
0053         a[2] * b[0] - a[0] * b[2],
0054         a[0] * b[1] - a[1] * b[0],
0055     };
0056 }
0057 
0058 std::optional<QPointF> math::line_intersection(const QPointF& start1, const QPointF& end1, const QPointF& start2, const QPointF& end2)
0059 {
0060     std::array<qreal, 3> v1{start1.x(), start1.y(), 1};
0061     std::array<qreal, 3> v2{end1.x(), end1.y(), 1};
0062     std::array<qreal, 3> v3{start2.x(), start2.y(), 1};
0063     std::array<qreal, 3> v4{end2.x(), end2.y(), 1};
0064 
0065     std::array<qreal, 3> cp = cross_product(
0066         cross_product(v1, v2),
0067         cross_product(v3, v4)
0068     );
0069 
0070     // More forgiving than qFuzzyIsNull
0071     if ( math::abs(cp[2]) <= 0.00001 )
0072         return {};
0073 
0074     return QPointF(cp[0] / cp[2], cp[1] / cp[2]);
0075 }