File indexing completed on 2024-06-23 04:27:04

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2007 Rob Buis <buis@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "SpiralShape.h"
0008 
0009 #include <KoParameterShape_p.h>
0010 #include <KoPathPoint.h>
0011 #include <KoShapeSavingContext.h>
0012 #include <KoXmlWriter.h>
0013 #include <KoXmlNS.h>
0014 
0015 #include <math.h>
0016 #include "kis_assert.h"
0017 
0018 
0019 SpiralShape::SpiralShape()
0020     : m_fade(.9)
0021     , m_kindAngle(M_PI)
0022     , m_radii(100.0, 100.0)
0023     , m_type(Curve)
0024     , m_clockwise(true)
0025 {
0026     //m_handles.push_back(QPointF(50, 0));
0027     //m_handles.push_back(QPointF(50, 50));
0028     //m_handles.push_back(QPointF(0, 50));
0029     createPath(QSizeF(m_radii.x(), m_radii.y()));
0030 }
0031 
0032 SpiralShape::SpiralShape(const SpiralShape &rhs)
0033     : KoParameterShape(rhs),
0034       m_fade(rhs.m_fade),
0035       m_kindAngle(rhs.m_kindAngle),
0036       m_center(rhs.m_center),
0037       m_radii(rhs.m_radii),
0038       m_type(rhs.m_type),
0039       m_clockwise(rhs.m_clockwise)
0040 
0041 {
0042     Q_FOREACH(KoPathPoint *point, rhs.m_points) {
0043         KIS_ASSERT_RECOVER(point) { continue; }
0044         m_points << new KoPathPoint(*point, this);
0045     }
0046 }
0047 
0048 
0049 SpiralShape::~SpiralShape()
0050 {
0051 }
0052 
0053 KoShape *SpiralShape::cloneShape() const
0054 {
0055     return new SpiralShape(*this);
0056 }
0057 
0058 void SpiralShape::setSize(const QSizeF &newSize)
0059 {
0060     QTransform matrix(resizeMatrix(newSize));
0061     m_center = matrix.map(m_center);
0062     m_radii = matrix.map(m_radii);
0063     KoParameterShape::setSize(newSize);
0064 }
0065 
0066 QPointF SpiralShape::normalize()
0067 {
0068     QPointF offset(KoParameterShape::normalize());
0069     QTransform matrix;
0070     matrix.translate(-offset.x(), -offset.y());
0071     m_center = matrix.map(m_center);
0072     return offset;
0073 }
0074 
0075 void SpiralShape::moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers)
0076 {
0077     Q_UNUSED(handleId);
0078     Q_UNUSED(point);
0079     Q_UNUSED(modifiers);
0080 #if 0
0081     QPointF p(point);
0082 
0083     QPointF diff(m_center - point);
0084     diff.setX(-diff.x());
0085     qreal angle = 0;
0086     if (diff.x() == 0) {
0087         angle = (diff.y() < 0 ? 270 : 90) * M_PI / 180.0;
0088     } else {
0089         diff.setY(diff.y() * m_radii.x() / m_radii.y());
0090         angle = atan(diff.y() / diff.x());
0091         if (angle < 0) {
0092             angle = M_PI + angle;
0093         }
0094         if (diff.y() < 0) {
0095             angle += M_PI;
0096         }
0097     }
0098 
0099     switch (handleId) {
0100     case 0:
0101         p = QPointF(m_center + QPointF(cos(angle) * m_radii.x(), -sin(angle) * m_radii.y()));
0102         m_handles[handleId] = p;
0103         updateKindHandle();
0104         break;
0105     case 1:
0106         p = QPointF(m_center + QPointF(cos(angle) * m_radii.x(), -sin(angle) * m_radii.y()));
0107         m_handles[handleId] = p;
0108         updateKindHandle();
0109         break;
0110     case 2: {
0111         QList<QPointF> kindHandlePositions;
0112         kindHandlePositions.push_back(QPointF(m_center + QPointF(cos(m_kindAngle) * m_radii.x(), -sin(m_kindAngle) * m_radii.y())));
0113         kindHandlePositions.push_back(m_center);
0114         kindHandlePositions.push_back((m_handles[0] + m_handles[1]) / 2.0);
0115 
0116         QPointF diff = m_center * 2.0;
0117         int handlePos = 0;
0118         for (int i = 0; i < kindHandlePositions.size(); ++i) {
0119             QPointF pointDiff(p - kindHandlePositions[i]);
0120             if (i == 0 || qAbs(pointDiff.x()) + qAbs(pointDiff.y()) < qAbs(diff.x()) + qAbs(diff.y())) {
0121                 diff = pointDiff;
0122                 handlePos = i;
0123             }
0124         }
0125         m_handles[handleId] = kindHandlePositions[handlePos];
0126         m_type = SpiralType(handlePos);
0127     } break;
0128     }
0129 #endif
0130 }
0131 
0132 void SpiralShape::updatePath(const QSizeF &size)
0133 {
0134     createPath(size);
0135     normalize();
0136 #if 0
0137     Q_UNUSED(size);
0138     QPointF startpoint(m_handles[0]);
0139 
0140     QPointF curvePoints[12];
0141 
0142     int pointCnt = arcToCurve(m_radii.x(), m_radii.y(), m_startAngle, sweepAngle(), startpoint, curvePoints);
0143 
0144     int cp = 0;
0145     m_points[cp]->setPoint(startpoint);
0146     m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint1);
0147     for (int i = 0; i < pointCnt; i += 3) {
0148         m_points[cp]->setControlPoint2(curvePoints[i]);
0149         m_points[++cp]->setControlPoint1(curvePoints[i + 1]);
0150         m_points[cp]->setPoint(curvePoints[i + 2]);
0151         m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint2);
0152     }
0153     if (m_type == Curve) {
0154         m_points[++cp]->setPoint(m_center);
0155         m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint1);
0156         m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint2);
0157     } else if (m_type == Line && m_startAngle == m_endAngle) {
0158         m_points[0]->setControlPoint1(m_points[cp]->controlPoint1());
0159         m_points[0]->setPoint(m_points[cp]->point());
0160         --cp;
0161     }
0162 
0163     d->m_subpaths[0]->clear();
0164     for (int i = 0; i <= cp; ++i) {
0165         if (i < cp || (m_type == Line && m_startAngle != m_endAngle)) {
0166             m_points[i]->unsetProperty(KoPathPoint::CloseSubpath);
0167         } else {
0168             m_points[i]->setProperty(KoPathPoint::CloseSubpath);
0169         }
0170         d->m_subpaths[0]->push_back(m_points[i]);
0171     }
0172 
0173 #endif
0174 }
0175 
0176 void SpiralShape::createPath(const QSizeF &size)
0177 {
0178     Q_UNUSED(size);
0179     clear();
0180     QPointF center = QPointF(m_radii.x() / 2.0, m_radii.y() / 2.0);
0181     //moveTo(QPointF(size.width(), m_radii.y()));
0182     qreal adv_ang = (m_clockwise ? -1.0 : 1.0) * M_PI_2;
0183     // radius of first segment is non-faded radius:
0184     qreal m_radius = m_radii.x() / 2.0;
0185     qreal r = m_radius;
0186 
0187     QPointF oldP(center.x(), (m_clockwise ? -1.0 : 1.0) * m_radius + center.y());
0188     QPointF newP;
0189     QPointF newCenter(center);
0190     moveTo(oldP);
0191     uint m_segments = 10;
0192     //m_handles[0] = oldP;
0193 
0194     for (uint i = 0; i < m_segments; ++i) {
0195         newP.setX(r * cos(adv_ang * (i + 2)) + newCenter.x());
0196         newP.setY(r * sin(adv_ang * (i + 2)) + newCenter.y());
0197 
0198         if (m_type == Curve) {
0199             qreal rx = qAbs(oldP.x() - newP.x());
0200             qreal ry = qAbs(oldP.y() - newP.y());
0201             if (m_clockwise) {
0202                 arcTo(rx, ry, ((i + 1) % 4) * 90, 90);
0203             } else {
0204                 arcTo(rx, ry, 360 - ((i + 1) % 4) * 90, -90);
0205             }
0206         } else {
0207             lineTo(newP);
0208         }
0209 
0210         newCenter += (newP - newCenter) * (1.0 - m_fade);
0211         oldP = newP;
0212         r *= m_fade;
0213     }
0214     //m_handles[1] = QPointF(center.x(), (m_clockwise ? -1.0 : 1.0) * m_radius + center.y());
0215     m_points = *subpaths()[0];
0216 
0217     notifyPointsChanged();
0218 }
0219 
0220 void SpiralShape::updateKindHandle()
0221 {
0222     /*
0223        m_kindAngle = (m_startAngle + m_endAngle) * M_PI / 360.0;
0224        if (m_startAngle > m_endAngle)
0225        {
0226            m_kindAngle += M_PI;
0227        }
0228        switch (m_type)
0229        {
0230            case Curve:
0231                m_handles[2] = m_center + QPointF(cos(m_kindAngle) * m_radii.x(), -sin(m_kindAngle) * m_radii.y());
0232                break;
0233            case Line:
0234                m_handles[2] = m_center;
0235                break;
0236        }
0237        */
0238 }
0239 
0240 void SpiralShape::updateAngleHandles()
0241 {
0242 //    qreal startRadian = m_startAngle * M_PI / 180.0;
0243 //    qreal endRadian = m_endAngle * M_PI / 180.0;
0244 //    m_handles[0] = m_center + QPointF(cos(startRadian) * m_radii.x(), -sin(startRadian) * m_radii.y());
0245 //    m_handles[1] = m_center + QPointF(cos(endRadian) * m_radii.x(), -sin(endRadian) * m_radii.y());
0246 }
0247 
0248 void SpiralShape::setType(SpiralType type)
0249 {
0250     m_type = type;
0251     updateKindHandle();
0252     updatePath(size());
0253 }
0254 
0255 SpiralShape::SpiralType SpiralShape::type() const
0256 {
0257     return m_type;
0258 }
0259 
0260 void SpiralShape::setFade(qreal fade)
0261 {
0262     m_fade = fade;
0263     updateKindHandle();
0264     //updateAngleHandles();
0265     updatePath(size());
0266 }
0267 
0268 qreal SpiralShape::fade() const
0269 {
0270     return m_fade;
0271 }
0272 
0273 bool SpiralShape::clockWise() const
0274 {
0275     return m_clockwise;
0276 }
0277 
0278 void SpiralShape::setClockWise(bool clockWise)
0279 {
0280     m_clockwise = clockWise;
0281     updateKindHandle();
0282     //updateAngleHandles();
0283     updatePath(size());
0284 }
0285 
0286 QString SpiralShape::pathShapeId() const
0287 {
0288     return SpiralShapeId;
0289 }