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 }