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 }