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 }