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

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 "cubic_struts.hpp"
0008 
0009 #include "math/math.hpp"
0010 #include "math/geom.hpp"
0011 
0012 #include "operations.hpp"
0013 
0014 using namespace glaxnimate;
0015 
0016 // see https://pomax.github.io/bezierinfo/#abc (this returns A given B)
0017 QPointF math::bezier::get_quadratic_handle(const math::bezier::BezierSegment& segment, const QPointF& B, qreal t)
0018 {
0019     qreal t1 = (1-t);
0020     qreal t13 = t1 * t1 * t1;
0021     qreal t3 = t * t * t;
0022     qreal u = t13 / (t3 + t13);
0023     qreal ratio = math::abs((t3 + t13 - 1) / (t3 + t13));
0024     QPointF C = math::lerp(segment[3], segment[0], u);
0025     QPointF A;
0026     if ( t == 0 )
0027         A = segment[1];
0028     else if ( t == 1 )
0029         A = segment[2];
0030     else
0031         A = B + ( B - C ) / ratio;
0032 
0033     return A;
0034 }
0035 
0036 
0037 math::bezier::BezierSegment math::bezier::cubic_segment_from_struts(
0038     const math::bezier::BezierSegment& segment,
0039     const BezierStruts& struts
0040 )
0041 {
0042     if ( struts.t == 0  || struts.t == 1)
0043         return segment;
0044 
0045     QPointF A = get_quadratic_handle(segment, struts.B, struts.t);
0046     QPointF v1 = A + (struts.e1 - A) / (1-struts.t);
0047     QPointF v2 = A + (struts.e2 - A) / struts.t;
0048     return {
0049         segment[0],
0050         segment[0] + (v1 - segment[0]) / struts.t,
0051         segment[3] + (v2 - segment[3]) / (1-struts.t),
0052         segment[3]
0053     };
0054 }
0055 
0056 // see https://pomax.github.io/bezierinfo/#pointcurves
0057 math::bezier::BezierStruts math::bezier::cubic_struts_idealized(const math::bezier::BezierSegment& segment, const QPointF& B)
0058 {
0059     BezierStruts struts;
0060     struts.B = B;
0061     qreal d1 = math::length(segment[0] - B);
0062     qreal d2 = math::length(segment[3] - B);
0063     struts.t = d1 / (d1+d2);
0064     QPointF center = circle_center(segment[0], B, segment[3]);
0065     qreal tanlen  = math::length(segment[3] - segment[0]) / 3;
0066     qreal phi = math::fmod(
0067         math::atan2(segment[3].y() - segment[0].y(), segment[3].x() - segment[0].x())
0068         - math::atan2(B.y() - segment[0].y(), B.x() - segment[0].x())
0069         + math::tau,
0070         math::tau
0071     );
0072     if ( phi < math::pi )
0073         tanlen = -tanlen;
0074 
0075     qreal de1 = struts.t * tanlen;
0076     qreal de2 = (1-struts.t) * tanlen;
0077     QPointF tangent = struts.B - center;
0078     tangent /= math::length(tangent);
0079     tangent = { -tangent.y(), tangent.x() };
0080     struts.e1 = struts.B + de1 * tangent;
0081     struts.e2 = struts.B - de2 * tangent;
0082 
0083     return struts;
0084 }
0085 
0086 math::bezier::BezierStruts math::bezier::cubic_struts_projection(
0087     const math::bezier::BezierSegment& segment,
0088     const QPointF& B,
0089     const math::bezier::ProjectResult& projection
0090 )
0091 {
0092     BezierStruts struts;
0093     struts.B = B;
0094     struts.t = projection.factor;
0095 
0096     auto v1 = math::lerp(segment[0], segment[1], struts.t);
0097     auto v2 = math::lerp(segment[2], segment[3], struts.t);
0098     auto A = get_quadratic_handle(segment, projection.point, struts.t);
0099     struts.e1 = math::lerp(v1, A, struts.t) - projection.point + B;
0100     struts.e2 = math::lerp(A, v2, struts.t) - projection.point + B;
0101 
0102     return struts;
0103 }