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