1 /* This file is part of the KDE project
2  * Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
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 "KarbonWhirlPinchCommand.h"
21 
22 #include <KoPathShape.h>
23 #include <KoPathPoint.h>
24 
25 #include <klocalizedstring.h>
26 #include <math.h>
27 
28 // the original data of a path point
29 struct PointData
30 {
PointDataPointData31     PointData(KoPathPoint * p)
32     {
33         KoPathShape * shape = p->parent();
34         // save points in document coordinates
35         oldNode = shape->shapeToDocument(p->point());
36         oldControlPoint1 = shape->shapeToDocument(p->controlPoint1());
37         oldControlPoint2 = shape->shapeToDocument(p->controlPoint2());
38     }
39 
restorePointPointData40     void restorePoint(KoPathPoint * p)
41     {
42         KoPathShape * shape = p->parent();
43         p->setPoint(shape->documentToShape(oldNode));
44         if (p->activeControlPoint1())
45             p->setControlPoint1(shape->documentToShape(oldControlPoint1));
46         if (p->activeControlPoint2())
47             p->setControlPoint2(shape->documentToShape(oldControlPoint2));
48     }
49 
50     QPointF oldNode;
51     QPointF oldControlPoint1;
52     QPointF oldControlPoint2;
53 };
54 
55 class KarbonWhirlPinchCommand::Private
56 {
57 public:
Private(KoPathShape * path,qreal angle,qreal pinch,qreal radius)58     Private(KoPathShape * path, qreal angle, qreal pinch, qreal radius)
59             : pathShape(path), whirlAngle(angle), pinchAmount(pinch), effectRadius(radius)
60     {
61         effectCenter = pathShape->boundingRect().center();
62         if (pinchAmount < -1.0)
63             pinchAmount = -1.0;
64         else if (pinchAmount > 1.0)
65             pinchAmount = 1.0;
66     }
67 
whirlPinch(const QPointF & point)68     QPointF whirlPinch(const QPointF &point)
69     {
70         // transform to document coordinates first
71         QPointF docPoint = pathShape->shapeToDocument(point);
72 
73         QPointF delta = docPoint - effectCenter;
74         qreal distToCenter = sqrt(delta.x() * delta.x() + delta.y() * delta.y());
75 
76         if (distToCenter < effectRadius) {
77             QTransform m;
78 
79             distToCenter /= effectRadius;
80 
81             qreal scale = pow(sin(M_PI_2 * distToCenter), -pinchAmount);
82 
83             // pinch:
84             m.translate(effectCenter.x(), effectCenter.y());
85             m.scale(scale, scale);
86 
87             // whirl:
88             m.rotate(whirlAngle *(1.0 - distToCenter) *(1.0 - distToCenter));
89             m.translate(-effectCenter.x(), -effectCenter.y());
90 
91             // transform back to shape coordinates
92             return pathShape->documentToShape(m.map(docPoint));
93         }
94         return point;
95     }
96 
97     KoPathShape * pathShape;
98     qreal whirlAngle;
99     qreal pinchAmount;
100     qreal effectRadius;
101     QPointF effectCenter;
102     QList< QList<PointData> > pathData;
103 };
104 
KarbonWhirlPinchCommand(KoPathShape * path,qreal angle,qreal pinch,qreal radius,KUndo2Command * parent)105 KarbonWhirlPinchCommand::KarbonWhirlPinchCommand(KoPathShape * path, qreal angle, qreal pinch, qreal radius, KUndo2Command *parent)
106         : KUndo2Command(parent), d(new Private(path, angle, pinch, radius))
107 {
108     setText(kundo2_i18n("Whirl & pinch"));
109 
110     // save the path point data used for undo
111     uint subpathCount = d->pathShape->subpathCount();
112     for (uint subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
113         QList<PointData> subpathData;
114         int pointCount = d->pathShape->subpathPointCount(subpathIndex);
115         for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
116             KoPathPoint * p = d->pathShape->pointByIndex(KoPathPointIndex(subpathIndex, pointIndex));
117             subpathData.append(PointData(p));
118         }
119         d->pathData.append(subpathData);
120     }
121 }
122 
~KarbonWhirlPinchCommand()123 KarbonWhirlPinchCommand::~KarbonWhirlPinchCommand()
124 {
125     delete d;
126 }
127 
redo()128 void KarbonWhirlPinchCommand::redo()
129 {
130     d->pathShape->update();
131     uint subpathCount = d->pathData.count();
132     for (uint subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
133         uint pointCount = d->pathData[subpathIndex].count();
134         for (uint pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
135             KoPathPoint * p = d->pathShape->pointByIndex(KoPathPointIndex(subpathIndex, pointIndex));
136             p->setPoint(d->whirlPinch(p->point()));
137             if (p->activeControlPoint1())
138                 p->setControlPoint1(d->whirlPinch(p->controlPoint1()));
139             if (p->activeControlPoint2())
140                 p->setControlPoint2(d->whirlPinch(p->controlPoint2()));
141         }
142     }
143     d->pathShape->normalize();
144     d->pathShape->update();
145 
146     KUndo2Command::redo();
147 }
148 
undo()149 void KarbonWhirlPinchCommand::undo()
150 {
151     d->pathShape->update();
152     uint subpathCount = d->pathData.count();
153     for (uint subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
154         uint pointCount = d->pathData[subpathIndex].count();
155         for (uint pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
156             KoPathPoint * p = d->pathShape->pointByIndex(KoPathPointIndex(subpathIndex, pointIndex));
157             d->pathData[subpathIndex][pointIndex].restorePoint(p);
158         }
159     }
160     d->pathShape->normalize();
161     d->pathShape->update();
162 
163     KUndo2Command::undo();
164 }
165