File indexing completed on 2024-05-05 04:35:17

0001 /* This file is part of the TikZKit project.
0002  *
0003  * Copyright (C) 2013 Dominik Haumann <dhaumann@kde.org>
0004  *
0005  * This library is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU Library General Public License as published
0007  * by the Free Software Foundation, either version 2 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, see
0017  * <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "BezierCurve.h"
0021 
0022 #include <cmath>
0023 
0024 BezierCurve::BezierCurve()
0025 {
0026 }
0027 
0028 BezierCurve::BezierCurve(const QPointF& p1,
0029                          const QPointF& p2,
0030                          const QPointF& c1,
0031                          const QPointF& c2)
0032     : m_p1(p1)
0033     , m_p2(p2)
0034     , m_c1(c1)
0035     , m_c2(c2)
0036 {
0037 }
0038 
0039 BezierCurve::~BezierCurve()
0040 {
0041 }
0042 
0043 void BezierCurve::setP1(const QPointF& p1)
0044 {
0045     m_p1 = p1;
0046 }
0047 
0048 void BezierCurve::setP2(const QPointF& p2)
0049 {
0050     m_p2 = p2;
0051 }
0052 
0053 void BezierCurve::setC1(const QPointF& c1)
0054 {
0055     m_c1 = c1;
0056 }
0057 
0058 void BezierCurve::setC2(const QPointF& c2)
0059 {
0060     m_c2 = c2;
0061 }
0062 
0063 QPointF BezierCurve::p1() const
0064 {
0065     return m_p1;
0066 }
0067 
0068 QPointF BezierCurve::p2() const
0069 {
0070     return m_p2;
0071 }
0072 
0073 QPointF BezierCurve::c1() const
0074 {
0075     return m_c1;
0076 }
0077 
0078 QPointF BezierCurve::c2() const
0079 {
0080     return m_c2;
0081 }
0082 
0083 inline static qreal interpolateBezierCurve(qreal t,
0084                                            qreal p1,
0085                                            qreal p2,
0086                                            qreal c1,
0087                                            qreal c2)
0088 {
0089     return (-p1 + 3*c1 -3*c2 + p2) * t*t*t + (3*p1 - 6*c1 + 3*c2) * t*t + (-3*p1 + 3*c1) * t + p1;
0090 }
0091 
0092 inline static QPointF interpolateBezierCurve(qreal t,
0093                                              const QPointF& p1,
0094                                              const QPointF& p2,
0095                                              const QPointF& c1,
0096                                              const QPointF& c2)
0097 {
0098     return (-p1 + 3*c1 -3*c2 + p2) * t*t*t + (3*p1 - 6*c1 + 3*c2) * t*t + (-3*p1 + 3*c1) * t + p1;
0099 }
0100 
0101 QPointF BezierCurve::pointAtPercent(qreal t) const
0102 {
0103     Q_ASSERT(t >= 0.0);
0104     Q_ASSERT(t <= 1.0);
0105 
0106     return interpolateBezierCurve(t, m_p1, m_p2, m_c1, m_c2);
0107 }
0108 
0109 QPainterPath BezierCurve::toPath(int samplePoints) const
0110 {
0111     QPainterPath path;
0112     path.moveTo(m_p1);
0113     for (int i = 1; i <= samplePoints; ++i) {
0114         path.lineTo(pointAtPercent(static_cast<qreal>(i) / samplePoints));
0115     }
0116     return path;
0117 }
0118 
0119 QPainterPath BezierCurve::toPath(qreal t1, qreal t2, int samplePoints) const
0120 {
0121     Q_ASSERT(t1 >= 0);
0122     Q_ASSERT(t1 <= 1);
0123     Q_ASSERT(t2 >= 0);
0124     Q_ASSERT(t2 <= 1);
0125 
0126     QPainterPath path;
0127     path.moveTo(pointAtPercent(t1));
0128 
0129     const qreal tau = (t2 - t1) / samplePoints;
0130     for (int i = 1; i <= samplePoints; ++i) {
0131         path.lineTo(pointAtPercent(t1 + i * tau));
0132     }
0133     return path;
0134 }
0135 
0136 BezierCurve BezierCurve::subPath(qreal t1, qreal t2) const
0137 {
0138     Q_ASSERT(t1 >= 0.0);
0139     Q_ASSERT(t1 < 1.0);
0140     Q_ASSERT(t2 > 0.0);
0141     Q_ASSERT(t2 <= 1.0);
0142     Q_ASSERT(t1 < t2);
0143 
0144     // copy this curve
0145     BezierCurve c(*this);
0146 
0147     // adapt start, if requried
0148     if (t1 > 0.0) {
0149         const QPointF x0 = c.m_p1 + t1 * (c.m_c1 - c.m_p1);
0150         const QPointF x1 = c.m_c1 + t1 * (c.m_c2 - c.m_c1);
0151         const QPointF x2 = c.m_c2 + t1 * (c.m_p2 - c.m_c2);
0152 
0153         const QPointF x3 = x0 + t1 * (x1 - x0);
0154         const QPointF x4 = x1 + t1 * (x2 - x1);
0155 
0156         c.m_p1 = x3 + t1 * (x4 - x3);
0157         c.m_c1 = x4;
0158         c.m_c2 = x2;
0159     }
0160 
0161     // adapt end, if requried
0162     if (t2 < 1.0) {
0163         const QPointF x0 = c.m_p1 + t2 * (c.m_c1 - c.m_p1);
0164         const QPointF x1 = c.m_c1 + t2 * (c.m_c2 - c.m_c1);
0165         const QPointF x2 = c.m_c2 + t2 * (c.m_p2 - c.m_c2);
0166 
0167         const QPointF x3 = x0 + t2 * (x1 - x0);
0168         const QPointF x4 = x1 + t2 * (x2 - x1);
0169 
0170         c.m_c1 = x0;
0171         c.m_c2 = x3;
0172         c.m_p2 = x3 + t2 * (x4 - x3);
0173     }
0174 
0175     return c;
0176 }
0177 
0178 qreal BezierCurve::intersect(const QPainterPath & path)
0179 {
0180     const int samplePoints = 50;
0181 
0182     const qreal tau = 1.0 / samplePoints;
0183 
0184     for (int a = 0; a < samplePoints; ++a) {
0185         QLineF line1(pointAtPercent(a * tau),
0186                      pointAtPercent((a + 1) * tau));
0187         for (int b = 0; b < samplePoints; ++b) {
0188             QLineF line2(path.pointAtPercent(b * tau),
0189                          path.pointAtPercent((b + 1) * tau));
0190 
0191             QPointF crossing;
0192             if (QLineF::BoundedIntersection == line1.intersects(line2, &crossing)) {
0193                 return a * tau;
0194             }
0195         }
0196     }
0197 
0198     return 0.0;
0199 }
0200 
0201 // kate: indent-width 4; replace-tabs on;