libs/flake

KoPathPointMergeCommand.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002  * Copyright (C) 2009 Jan Hambrecht <jaham@gmx.net>
00003  * Copyright (C) 2006,2007 Thorsten Zachmann <zachmann@kde.org>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public License
00016  * along with this library; see the file COPYING.LIB.  If not, write to
00017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "KoPathPointMergeCommand.h"
00022 #include "KoPathPoint.h"
00023 #include "KoPathPointData.h"
00024 #include "KoPathShape.h"
00025 #include <KLocale>
00026 #include <QPointF>
00027 
00028 class KoPathPointMergeCommand::Private
00029 {
00030 public:
00031     Private(const KoPathPointData &pointData1, const KoPathPointData &pointData2)
00032     : pathShape(pointData1.pathShape)
00033     , endPoint(pointData1.pointIndex)
00034     , startPoint(pointData2.pointIndex)
00035     , splitIndex(KoPathPointIndex(-1, -1))
00036     , removedPoint(0)
00037     , reverse(ReverseNone)
00038     {
00039     }
00040 
00041     ~Private()
00042     {
00043         delete removedPoint;
00044     }
00045 
00046     KoPathPoint * mergePoints( KoPathPoint * p1, KoPathPoint * p2)
00047     {
00048         QPointF mergePosition = 0.5 * (p1->point() + p2->point());
00049         QPointF mergeControlPoint1 = mergePosition + (p1->controlPoint1() - p1->point());
00050         QPointF mergeControlPoint2 = mergePosition + (p2->controlPoint2() - p2->point());
00051 
00052         // change position and control points of first merged point
00053         p1->setPoint( mergePosition );
00054         if (p1->activeControlPoint1()) {
00055             p1->setControlPoint1(mergeControlPoint1);
00056         }
00057         if (p2->activeControlPoint2()) {
00058             p1->setControlPoint2(mergeControlPoint2);
00059         }
00060 
00061         // remove the second merged point
00062         KoPathPointIndex removeIndex = pathShape->pathPointIndex(p2);
00063         return pathShape->removePoint(removeIndex);
00064     }
00065 
00066     void resetPoints( KoPathPointIndex index1, KoPathPointIndex index2 )
00067     {
00068         KoPathPoint * p1 = pathShape->pointByIndex(index1);
00069         KoPathPoint * p2 = pathShape->pointByIndex(index2);
00070         p1->setPoint(pathShape->documentToShape(oldNodePoint1));
00071         p2->setPoint(pathShape->documentToShape(oldNodePoint2));
00072         if (p1->activeControlPoint1()) {
00073             p1->setControlPoint1(pathShape->documentToShape(oldControlPoint1));
00074         }
00075         if (p2->activeControlPoint2()) {
00076             p2->setControlPoint2(pathShape->documentToShape(oldControlPoint2));
00077         }
00078     }
00079 
00080     KoPathShape * pathShape;
00081     KoPathPointIndex endPoint;
00082     KoPathPointIndex startPoint;
00083     KoPathPointIndex splitIndex;
00084 
00085     // the control points have to be stored in document positions
00086     QPointF oldNodePoint1;
00087     QPointF oldControlPoint1;
00088     QPointF oldNodePoint2;
00089     QPointF oldControlPoint2;
00090 
00091     KoPathPoint * removedPoint;
00092 
00093     enum Reverse {
00094         ReverseNone = 0,
00095         ReverseFirst = 1,
00096         ReverseSecond = 2
00097     };
00098     int reverse;
00099 };
00100 
00107 KoPathPointMergeCommand::KoPathPointMergeCommand(const KoPathPointData &pointData1, const KoPathPointData &pointData2, QUndoCommand *parent)
00108     : QUndoCommand(parent), d(new Private(pointData1, pointData2))
00109 {
00110     Q_ASSERT(pointData1.pathShape == pointData2.pathShape);
00111     Q_ASSERT(d->pathShape);
00112     Q_ASSERT(!d->pathShape->isClosedSubpath(d->endPoint.first));
00113     Q_ASSERT(d->endPoint.second == 0 ||
00114              d->endPoint.second == d->pathShape->pointCountSubpath(d->endPoint.first) - 1);
00115     Q_ASSERT(!d->pathShape->isClosedSubpath(d->startPoint.first));
00116     Q_ASSERT(d->startPoint.second == 0 ||
00117              d->startPoint.second == d->pathShape->pointCountSubpath(d->startPoint.first) - 1);
00118 
00119     // if we have two different subpaths we might need to reverse them
00120     if (d->endPoint.first != d->startPoint.first) {
00121         // sort by point index
00122         if (d->startPoint < d->endPoint)
00123             qSwap(d->endPoint, d->startPoint);
00124         // mark first subpath to be reversed if first point starts a subpath with more than one point
00125         if (d->endPoint.second == 0 && d->pathShape->pointCountSubpath(d->endPoint.first) > 1)
00126             d->reverse |= Private::ReverseFirst;
00127         // mark second subpath to be reversed if second point does not start a subpath with more than one point
00128         if (d->startPoint.second != 0 && d->pathShape->pointCountSubpath(d->startPoint.first) > 1)
00129             d->reverse |= Private::ReverseSecond;
00130     } else {
00131         Q_ASSERT(d->endPoint.second != d->startPoint.second);
00132         if (d->endPoint < d->startPoint)
00133             qSwap(d->endPoint, d->startPoint);
00134     }
00135 
00136     KoPathPoint * p1 = d->pathShape->pointByIndex(d->endPoint);
00137     KoPathPoint * p2 = d->pathShape->pointByIndex(d->startPoint);
00138 
00139     d->oldNodePoint1 = d->pathShape->shapeToDocument(p1->point());
00140     if (d->reverse & Private::ReverseFirst) {
00141         d->oldControlPoint1 = d->pathShape->shapeToDocument(p1->controlPoint2());
00142     } else {
00143         d->oldControlPoint1 = d->pathShape->shapeToDocument(p1->controlPoint1());
00144     }
00145     d->oldNodePoint2 = d->pathShape->shapeToDocument(p2->point());
00146     if (d->reverse & Private::ReverseSecond) {
00147         d->oldControlPoint2 = d->pathShape->shapeToDocument(p2->controlPoint1());
00148     } else {
00149         d->oldControlPoint2 = d->pathShape->shapeToDocument(p2->controlPoint2());
00150     }
00151 
00152     setText(i18n("Merge points"));
00153 }
00154 
00155 KoPathPointMergeCommand::~KoPathPointMergeCommand()
00156 {
00157     delete d;
00158 }
00159 
00160 void KoPathPointMergeCommand::redo()
00161 {
00162     QUndoCommand::redo();
00163 
00164     if (d->removedPoint)
00165         return;
00166 
00167     d->pathShape->update();
00168 
00169     KoPathPoint * endPoint = d->pathShape->pointByIndex(d->endPoint);
00170     KoPathPoint * startPoint = d->pathShape->pointByIndex(d->startPoint);
00171 
00172     // are we just closing a single subpath ?
00173     if (d->endPoint.first == d->startPoint.first) {
00174         // change the endpoint of the subpath
00175         d->removedPoint = d->mergePoints(endPoint, startPoint);
00176         // set endpoint of subpath to close the subpath
00177         endPoint->setProperty(KoPathPoint::CloseSubpath);
00178         // set new startpoint of subpath to close the subpath
00179         KoPathPointIndex newStartIndex(d->startPoint.first,0);
00180         d->pathShape->pointByIndex(newStartIndex)->setProperty(KoPathPoint::CloseSubpath);
00181     } else {
00182         // first revert subpathes if needed
00183         if (d->reverse & Private::ReverseFirst) {
00184             d->pathShape->reverseSubpath(d->endPoint.first);
00185         }
00186         if (d->reverse & Private::ReverseSecond) {
00187             d->pathShape->reverseSubpath(d->startPoint.first);
00188         }
00189         // move the subpaths so the second is directly after the first
00190         d->pathShape->moveSubpath(d->startPoint.first, d->endPoint.first + 1);
00191         d->splitIndex = d->pathShape->pathPointIndex(endPoint);
00192         // join both subpathes
00193         d->pathShape->join(d->endPoint.first);
00194         // change the first point of the points to merge
00195         d->removedPoint = d->mergePoints(endPoint, startPoint);
00196     }
00197 
00198     d->pathShape->normalize();
00199     d->pathShape->update();
00200 }
00201 
00202 void KoPathPointMergeCommand::undo()
00203 {
00204     QUndoCommand::undo();
00205 
00206     if (!d->removedPoint)
00207         return;
00208 
00209     d->pathShape->update();
00210 
00211     // check if we just have closed a single subpath
00212     if (d->endPoint.first == d->startPoint.first) {
00213         // open the subpath at the old/new first point
00214         d->pathShape->openSubpath(d->startPoint);
00215         // reinsert the old first point
00216         d->pathShape->insertPoint(d->removedPoint, d->startPoint);
00217         // reposition the points
00218         d->resetPoints(d->endPoint, d->startPoint);
00219     } else {
00220         // break merged subpathes apart
00221         d->pathShape->breakAfter(d->splitIndex);
00222         // reinsert the old second point
00223         d->pathShape->insertPoint(d->removedPoint, KoPathPointIndex(d->splitIndex.first+1,0));
00224         // reposition the first point
00225         d->resetPoints(d->splitIndex, KoPathPointIndex(d->splitIndex.first+1,0));
00226         // move second subpath to its old position
00227         d->pathShape->moveSubpath(d->splitIndex.first+1, d->startPoint.first);
00228         // undo the reversion of the subpaths
00229         if (d->reverse & Private::ReverseFirst) {
00230             d->pathShape->reverseSubpath(d->endPoint.first);
00231         }
00232         if (d->reverse & Private::ReverseSecond) {
00233             d->pathShape->reverseSubpath(d->startPoint.first);
00234         }
00235     }
00236 
00237     d->pathShape->normalize();
00238     d->pathShape->update();
00239 
00240     // reset the removed point
00241     d->removedPoint = 0;
00242 }