1 /* This file is part of the KDE project
2    Copyright (C) 2007 Rob Buis <buis@kde.org>
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "SpiralShape.h"
21 
22 #include <KoParameterShape_p.h>
23 #include <KoPathPoint.h>
24 #include <KoShapeSavingContext.h>
25 #include <KoXmlReader.h>
26 #include <KoXmlWriter.h>
27 #include <KoXmlNS.h>
28 
29 #include <math.h>
30 #include "kis_assert.h"
31 
32 
SpiralShape()33 SpiralShape::SpiralShape()
34     : m_fade(.9)
35     , m_kindAngle(M_PI)
36     , m_radii(100.0, 100.0)
37     , m_type(Curve)
38     , m_clockwise(true)
39 {
40     //m_handles.push_back(QPointF(50, 0));
41     //m_handles.push_back(QPointF(50, 50));
42     //m_handles.push_back(QPointF(0, 50));
43     createPath(QSizeF(m_radii.x(), m_radii.y()));
44 }
45 
SpiralShape(const SpiralShape & rhs)46 SpiralShape::SpiralShape(const SpiralShape &rhs)
47     : KoParameterShape(rhs),
48       m_fade(rhs.m_fade),
49       m_kindAngle(rhs.m_kindAngle),
50       m_center(rhs.m_center),
51       m_radii(rhs.m_radii),
52       m_type(rhs.m_type),
53       m_clockwise(rhs.m_clockwise)
54 
55 {
56     Q_FOREACH(KoPathPoint *point, rhs.m_points) {
57         KIS_ASSERT_RECOVER(point) { continue; }
58         m_points << new KoPathPoint(*point, this);
59     }
60 }
61 
62 
~SpiralShape()63 SpiralShape::~SpiralShape()
64 {
65 }
66 
cloneShape() const67 KoShape *SpiralShape::cloneShape() const
68 {
69     return new SpiralShape(*this);
70 }
71 
saveOdf(KoShapeSavingContext & context) const72 void SpiralShape::saveOdf(KoShapeSavingContext &context) const
73 {
74     // TODO?
75     KoPathShape::saveOdf(context);
76 }
77 
loadOdf(const KoXmlElement & element,KoShapeLoadingContext &)78 bool SpiralShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &/*context*/)
79 {
80     Q_UNUSED(element);
81 
82     // TODO?
83     return true;
84 }
85 
setSize(const QSizeF & newSize)86 void SpiralShape::setSize(const QSizeF &newSize)
87 {
88     QTransform matrix(resizeMatrix(newSize));
89     m_center = matrix.map(m_center);
90     m_radii = matrix.map(m_radii);
91     KoParameterShape::setSize(newSize);
92 }
93 
normalize()94 QPointF SpiralShape::normalize()
95 {
96     QPointF offset(KoParameterShape::normalize());
97     QTransform matrix;
98     matrix.translate(-offset.x(), -offset.y());
99     m_center = matrix.map(m_center);
100     return offset;
101 }
102 
moveHandleAction(int handleId,const QPointF & point,Qt::KeyboardModifiers modifiers)103 void SpiralShape::moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers)
104 {
105     Q_UNUSED(handleId);
106     Q_UNUSED(point);
107     Q_UNUSED(modifiers);
108 #if 0
109     QPointF p(point);
110 
111     QPointF diff(m_center - point);
112     diff.setX(-diff.x());
113     qreal angle = 0;
114     if (diff.x() == 0) {
115         angle = (diff.y() < 0 ? 270 : 90) * M_PI / 180.0;
116     } else {
117         diff.setY(diff.y() * m_radii.x() / m_radii.y());
118         angle = atan(diff.y() / diff.x());
119         if (angle < 0) {
120             angle = M_PI + angle;
121         }
122         if (diff.y() < 0) {
123             angle += M_PI;
124         }
125     }
126 
127     switch (handleId) {
128     case 0:
129         p = QPointF(m_center + QPointF(cos(angle) * m_radii.x(), -sin(angle) * m_radii.y()));
130         m_handles[handleId] = p;
131         updateKindHandle();
132         break;
133     case 1:
134         p = QPointF(m_center + QPointF(cos(angle) * m_radii.x(), -sin(angle) * m_radii.y()));
135         m_handles[handleId] = p;
136         updateKindHandle();
137         break;
138     case 2: {
139         QList<QPointF> kindHandlePositions;
140         kindHandlePositions.push_back(QPointF(m_center + QPointF(cos(m_kindAngle) * m_radii.x(), -sin(m_kindAngle) * m_radii.y())));
141         kindHandlePositions.push_back(m_center);
142         kindHandlePositions.push_back((m_handles[0] + m_handles[1]) / 2.0);
143 
144         QPointF diff = m_center * 2.0;
145         int handlePos = 0;
146         for (int i = 0; i < kindHandlePositions.size(); ++i) {
147             QPointF pointDiff(p - kindHandlePositions[i]);
148             if (i == 0 || qAbs(pointDiff.x()) + qAbs(pointDiff.y()) < qAbs(diff.x()) + qAbs(diff.y())) {
149                 diff = pointDiff;
150                 handlePos = i;
151             }
152         }
153         m_handles[handleId] = kindHandlePositions[handlePos];
154         m_type = SpiralType(handlePos);
155     } break;
156     }
157 #endif
158 }
159 
updatePath(const QSizeF & size)160 void SpiralShape::updatePath(const QSizeF &size)
161 {
162     createPath(size);
163     normalize();
164 #if 0
165     Q_UNUSED(size);
166     QPointF startpoint(m_handles[0]);
167 
168     QPointF curvePoints[12];
169 
170     int pointCnt = arcToCurve(m_radii.x(), m_radii.y(), m_startAngle, sweepAngle(), startpoint, curvePoints);
171 
172     int cp = 0;
173     m_points[cp]->setPoint(startpoint);
174     m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint1);
175     for (int i = 0; i < pointCnt; i += 3) {
176         m_points[cp]->setControlPoint2(curvePoints[i]);
177         m_points[++cp]->setControlPoint1(curvePoints[i + 1]);
178         m_points[cp]->setPoint(curvePoints[i + 2]);
179         m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint2);
180     }
181     if (m_type == Curve) {
182         m_points[++cp]->setPoint(m_center);
183         m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint1);
184         m_points[cp]->unsetProperty(KoPathPoint::HasControlPoint2);
185     } else if (m_type == Line && m_startAngle == m_endAngle) {
186         m_points[0]->setControlPoint1(m_points[cp]->controlPoint1());
187         m_points[0]->setPoint(m_points[cp]->point());
188         --cp;
189     }
190 
191     d->m_subpaths[0]->clear();
192     for (int i = 0; i <= cp; ++i) {
193         if (i < cp || (m_type == Line && m_startAngle != m_endAngle)) {
194             m_points[i]->unsetProperty(KoPathPoint::CloseSubpath);
195         } else {
196             m_points[i]->setProperty(KoPathPoint::CloseSubpath);
197         }
198         d->m_subpaths[0]->push_back(m_points[i]);
199     }
200 
201 #endif
202 }
203 
createPath(const QSizeF & size)204 void SpiralShape::createPath(const QSizeF &size)
205 {
206     Q_UNUSED(size);
207     clear();
208     QPointF center = QPointF(m_radii.x() / 2.0, m_radii.y() / 2.0);
209     //moveTo(QPointF(size.width(), m_radii.y()));
210     qreal adv_ang = (m_clockwise ? -1.0 : 1.0) * M_PI_2;
211     // radius of first segment is non-faded radius:
212     qreal m_radius = m_radii.x() / 2.0;
213     qreal r = m_radius;
214 
215     QPointF oldP(center.x(), (m_clockwise ? -1.0 : 1.0) * m_radius + center.y());
216     QPointF newP;
217     QPointF newCenter(center);
218     moveTo(oldP);
219     uint m_segments = 10;
220     //m_handles[0] = oldP;
221 
222     for (uint i = 0; i < m_segments; ++i) {
223         newP.setX(r * cos(adv_ang * (i + 2)) + newCenter.x());
224         newP.setY(r * sin(adv_ang * (i + 2)) + newCenter.y());
225 
226         if (m_type == Curve) {
227             qreal rx = qAbs(oldP.x() - newP.x());
228             qreal ry = qAbs(oldP.y() - newP.y());
229             if (m_clockwise) {
230                 arcTo(rx, ry, ((i + 1) % 4) * 90, 90);
231             } else {
232                 arcTo(rx, ry, 360 - ((i + 1) % 4) * 90, -90);
233             }
234         } else {
235             lineTo(newP);
236         }
237 
238         newCenter += (newP - newCenter) * (1.0 - m_fade);
239         oldP = newP;
240         r *= m_fade;
241     }
242     //m_handles[1] = QPointF(center.x(), (m_clockwise ? -1.0 : 1.0) * m_radius + center.y());
243     m_points = *subpaths()[0];
244 
245     notifyPointsChanged();
246 }
247 
updateKindHandle()248 void SpiralShape::updateKindHandle()
249 {
250     /*
251        m_kindAngle = (m_startAngle + m_endAngle) * M_PI / 360.0;
252        if (m_startAngle > m_endAngle)
253        {
254            m_kindAngle += M_PI;
255        }
256        switch (m_type)
257        {
258            case Curve:
259                m_handles[2] = m_center + QPointF(cos(m_kindAngle) * m_radii.x(), -sin(m_kindAngle) * m_radii.y());
260                break;
261            case Line:
262                m_handles[2] = m_center;
263                break;
264        }
265        */
266 }
267 
updateAngleHandles()268 void SpiralShape::updateAngleHandles()
269 {
270 //    qreal startRadian = m_startAngle * M_PI / 180.0;
271 //    qreal endRadian = m_endAngle * M_PI / 180.0;
272 //    m_handles[0] = m_center + QPointF(cos(startRadian) * m_radii.x(), -sin(startRadian) * m_radii.y());
273 //    m_handles[1] = m_center + QPointF(cos(endRadian) * m_radii.x(), -sin(endRadian) * m_radii.y());
274 }
275 
setType(SpiralType type)276 void SpiralShape::setType(SpiralType type)
277 {
278     m_type = type;
279     updateKindHandle();
280     updatePath(size());
281 }
282 
type() const283 SpiralShape::SpiralType SpiralShape::type() const
284 {
285     return m_type;
286 }
287 
setFade(qreal fade)288 void SpiralShape::setFade(qreal fade)
289 {
290     m_fade = fade;
291     updateKindHandle();
292     //updateAngleHandles();
293     updatePath(size());
294 }
295 
fade() const296 qreal SpiralShape::fade() const
297 {
298     return m_fade;
299 }
300 
clockWise() const301 bool SpiralShape::clockWise() const
302 {
303     return m_clockwise;
304 }
305 
setClockWise(bool clockWise)306 void SpiralShape::setClockWise(bool clockWise)
307 {
308     m_clockwise = clockWise;
309     updateKindHandle();
310     //updateAngleHandles();
311     updatePath(size());
312 }
313 
pathShapeId() const314 QString SpiralShape::pathShapeId() const
315 {
316     return SpiralShapeId;
317 }
318