libs/flake

KoPathPoint.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
00003    Copyright (C) 2006-2008 Jan Hambrecht <jaham@gmx.net>
00004    Copyright (C) 2007 Thomas Zander <zander@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "KoPathPoint.h"
00023 #include "KoPathShape.h"
00024 #include "KoPointGroup.h"
00025 
00026 #include <KDebug>
00027 #include <QtGui/QPainter>
00028 #include <QPointF>
00029 
00030 #include <math.h>
00031 
00032 class KoPathPoint::Private
00033 {
00034 public:
00035     Private()
00036             : shape(0), properties(Normal), pointGroup(0)
00037             , activeControlPoint1(false), activeControlPoint2(false) {}
00038     KoPathShape * shape;
00039     QPointF point;
00040     QPointF controlPoint1;
00041     QPointF controlPoint2;
00042     KoPointProperties properties;
00043     KoPointGroup * pointGroup;
00044     bool activeControlPoint1;
00045     bool activeControlPoint2;
00046 };
00047 
00048 KoPathPoint::KoPathPoint(const KoPathPoint & pathPoint)
00049         : d(new Private())
00050 {
00051     d->shape = pathPoint.d->shape;
00052     d->point = pathPoint.d->point;
00053     d->controlPoint1 = pathPoint.d->controlPoint1;
00054     d->controlPoint2 = pathPoint.d->controlPoint2;
00055     d->properties = pathPoint.d->properties;
00056     d->activeControlPoint1 = pathPoint.d->activeControlPoint1;
00057     d->activeControlPoint2 = pathPoint.d->activeControlPoint2;
00058 }
00059 
00060 KoPathPoint::KoPathPoint()
00061         : d(new Private())
00062 {
00063 }
00064 
00065 KoPathPoint::KoPathPoint(KoPathShape * path, const QPointF & point, KoPointProperties properties)
00066         : d(new Private())
00067 {
00068     d->shape = path;
00069     d->point = point;
00070     d->properties = properties;
00071 }
00072 
00073 KoPathPoint::~KoPathPoint()
00074 {
00075     delete d;
00076 }
00077 
00078 KoPathPoint& KoPathPoint::operator=(const KoPathPoint & rhs)
00079 {
00080     if (this == &rhs)
00081         return (*this);
00082 
00083     d->shape = rhs.d->shape;
00084     d->point = rhs.d->point;
00085     d->controlPoint1 = rhs.d->controlPoint1;
00086     d->controlPoint2 = rhs.d->controlPoint2;
00087     d->properties = rhs.d->properties;
00088     //d->pointGroup = rhs.d->pointGroup;
00089     d->activeControlPoint1 = rhs.d->activeControlPoint1;
00090     d->activeControlPoint2 = rhs.d->activeControlPoint2;
00091 
00092     return (*this);
00093 }
00094 
00095 bool KoPathPoint::operator == (const KoPathPoint &rhs) const
00096 {
00097     if (d->point != rhs.d->point)
00098         return false;
00099     if (d->controlPoint1 != rhs.d->controlPoint1)
00100         return false;
00101     if (d->controlPoint2 != rhs.d->controlPoint2)
00102         return false;
00103     if (d->properties != rhs.d->properties)
00104         return false;
00105     if (d->activeControlPoint1 != rhs.d->activeControlPoint1)
00106         return false;
00107     if (d->activeControlPoint2 != rhs.d->activeControlPoint2)
00108         return false;
00109     return true;
00110 }
00111 
00112 void KoPathPoint::setPoint(const QPointF & point)
00113 {
00114     d->point = point;
00115     if (d->shape)
00116         d->shape->notifyChanged();
00117 }
00118 
00119 void KoPathPoint::setControlPoint1(const QPointF & point)
00120 {
00121     d->controlPoint1 = point;
00122     d->activeControlPoint1 = true;
00123     if (d->shape)
00124         d->shape->notifyChanged();
00125 }
00126 
00127 void KoPathPoint::setControlPoint2(const QPointF & point)
00128 {
00129     d->controlPoint2 = point;
00130     d->activeControlPoint2 = true;
00131     if (d->shape)
00132         d->shape->notifyChanged();
00133 }
00134 
00135 void KoPathPoint::removeControlPoint1()
00136 {
00137     d->activeControlPoint1 = false;
00138     d->properties &= ~IsSmooth;
00139     d->properties &= ~IsSymmetric;
00140     if (d->shape)
00141         d->shape->notifyChanged();
00142 }
00143 
00144 void KoPathPoint::removeControlPoint2()
00145 {
00146     d->activeControlPoint2 = false;
00147     d->properties &= ~IsSmooth;
00148     d->properties &= ~IsSymmetric;
00149     if (d->shape)
00150         d->shape->notifyChanged();
00151 }
00152 
00153 void KoPathPoint::setProperties(KoPointProperties properties)
00154 {
00155     d->properties = properties;
00156     // CloseSubpath only allowed with StartSubpath or StopSubpath
00157     if ((d->properties & StartSubpath) == 0 && (d->properties & StopSubpath) == 0)
00158         d->properties &= ~CloseSubpath;
00159 
00160     if (! activeControlPoint1() || ! activeControlPoint2()) {
00161         // strip smooth and symmetric flags if point has not two control points
00162         d->properties &= ~IsSmooth;
00163         d->properties &= ~IsSymmetric;
00164     }
00165 
00166     if (d->shape)
00167         d->shape->notifyChanged();
00168 }
00169 
00170 void KoPathPoint::setProperty(KoPointProperty property)
00171 {
00172     switch (property) {
00173     case StartSubpath:
00174     case StopSubpath:
00175     case CloseSubpath:
00176         // nothing special to do here
00177         break;
00178     case IsSmooth:
00179         d->properties &= ~IsSymmetric;
00180         break;
00181     case IsSymmetric:
00182         d->properties &= ~IsSmooth;
00183         break;
00184     default: return;
00185     }
00186 
00187     d->properties |= property;
00188 
00189     if (! activeControlPoint1() || ! activeControlPoint2()) {
00190         // strip smooth and symmetric flags if point has not two control points
00191         d->properties &= ~IsSymmetric;
00192         d->properties &= ~IsSmooth;
00193     }
00194 }
00195 
00196 void KoPathPoint::unsetProperty(KoPointProperty property)
00197 {
00198     switch (property) {
00199     case StartSubpath:
00200         if (d->properties & StartSubpath && (d->properties & StopSubpath) == 0)
00201             d->properties &= ~CloseSubpath;
00202         break;
00203     case StopSubpath:
00204         if (d->properties & StopSubpath && (d->properties & StartSubpath) == 0)
00205             d->properties &= ~CloseSubpath;
00206         break;
00207     case CloseSubpath:
00208         if (d->properties & StartSubpath || d->properties & StopSubpath) {
00209             d->properties &= ~IsSmooth;
00210             d->properties &= ~IsSymmetric;
00211         }
00212         break;
00213     case IsSmooth:
00214     case IsSymmetric:
00215         // no others depend on these
00216         break;
00217     default: return;
00218     }
00219     d->properties &= ~property;
00220 }
00221 
00222 bool KoPathPoint::activeControlPoint1() const
00223 {
00224     // only start point on closed subpaths can have a controlPoint1
00225     if ((d->properties & StartSubpath) && (d->properties & CloseSubpath) == 0)
00226         return false;
00227 
00228     return d->activeControlPoint1;
00229 }
00230 
00231 bool KoPathPoint::activeControlPoint2() const
00232 {
00233     // only end point on closed subpaths can have a controlPoint2
00234     if ((d->properties & StopSubpath) && (d->properties & CloseSubpath) == 0)
00235         return false;
00236 
00237     return d->activeControlPoint2;
00238 }
00239 
00240 void KoPathPoint::map(const QMatrix &matrix, bool mapGroup)
00241 {
00242     if (d->pointGroup && mapGroup) {
00243         d->pointGroup->map(matrix);
00244     } else {
00245         d->point = matrix.map(d->point);
00246         d->controlPoint1 = matrix.map(d->controlPoint1);
00247         d->controlPoint2 = matrix.map(d->controlPoint2);
00248     }
00249     if (d->shape)
00250         d->shape->notifyChanged();
00251 }
00252 
00253 void KoPathPoint::paint(QPainter &painter, int handleRadius, KoPointTypes types, bool active)
00254 {
00255     QRectF handle(-handleRadius, -handleRadius, 2*handleRadius, 2*handleRadius);
00256 
00257     bool drawControlPoint1 = types & ControlPoint1 && (!active || activeControlPoint1());
00258     bool drawControlPoint2 = types & ControlPoint2 && (!active || activeControlPoint2());
00259 
00260     // draw lines at the bottom
00261     if (drawControlPoint2)
00262         painter.drawLine(point(), controlPoint2());
00263 
00264     if (drawControlPoint1)
00265         painter.drawLine(point(), controlPoint1());
00266 
00267 
00268     QMatrix worldMatrix = painter.worldMatrix();
00269 
00270     painter.setWorldMatrix(QMatrix());
00271 
00272     // the point is lowest
00273     if (types & Node) {
00274         if (properties() & IsSmooth)
00275             painter.drawRect(handle.translated(worldMatrix.map(point())));
00276         else if (properties() & IsSymmetric) {
00277             QMatrix matrix;
00278             matrix.rotate(45.0);
00279             QPolygonF poly(handle);
00280             poly = matrix.map(poly);
00281             poly.translate(worldMatrix.map(point()));
00282             painter.drawPolygon(poly);
00283         } else
00284             painter.drawEllipse(handle.translated(worldMatrix.map(point())));
00285     }
00286 
00287     // then comes control point 2
00288     if (drawControlPoint2)
00289         painter.drawEllipse(handle.translated(worldMatrix.map(controlPoint2())));
00290 
00291     // then comes control point 1
00292     if (drawControlPoint1)
00293         painter.drawEllipse(handle.translated(worldMatrix.map(controlPoint1())));
00294 
00295     painter.setWorldMatrix(worldMatrix);
00296 }
00297 
00298 void KoPathPoint::setParent(KoPathShape* parent)
00299 {
00300     // don't set to zero
00301     //Q_ASSERT( parent );
00302     d->shape = parent;
00303 }
00304 
00305 QRectF KoPathPoint::boundingRect(bool active) const
00306 {
00307     QRectF rect(d->point, QSize(1, 1));
00308     if (!active && activeControlPoint1()) {
00309         QRectF r1(d->point, QSize(1, 1));
00310         r1.setBottomRight(d->controlPoint1);
00311         rect = rect.unite(r1);
00312     }
00313     if (!active && activeControlPoint2()) {
00314         QRectF r2(d->point, QSize(1, 1));
00315         r2.setBottomRight(d->controlPoint2);
00316         rect = rect.unite(r2);
00317     }
00318     if (d->shape)
00319         return d->shape->shapeToDocument(rect);
00320     else
00321         return rect;
00322 }
00323 
00324 void KoPathPoint::reverse()
00325 {
00326     qSwap(d->controlPoint1, d->controlPoint2);
00327     qSwap(d->activeControlPoint1, d->activeControlPoint2);
00328     KoPointProperties newProps = Normal;
00329     newProps |= d->properties & IsSmooth;
00330     newProps |= d->properties & IsSymmetric;
00331     newProps |= d->properties & StartSubpath;
00332     newProps |= d->properties & StopSubpath;
00333     newProps |= d->properties & CloseSubpath;
00334     d->properties = newProps;
00335 }
00336 
00337 bool KoPathPoint::isSmooth(KoPathPoint * prev, KoPathPoint * next) const
00338 {
00339     QPointF t1, t2;
00340 
00341     if (activeControlPoint1()) {
00342         t1 = point() - controlPoint1();
00343     } else {
00344         // we need the previous path point but there is none provided
00345         if (! prev)
00346             return false;
00347         if (prev->activeControlPoint2())
00348             t1 = point() - prev->controlPoint2();
00349         else
00350             t1 = point() - prev->point();
00351     }
00352 
00353     if (activeControlPoint2()) {
00354         t2 = controlPoint2() - point();
00355     } else {
00356         // we need the next path point but there is none provided
00357         if (! next)
00358             return false;
00359         if (next->activeControlPoint1())
00360             t2 = next->controlPoint1() - point();
00361         else
00362             t2 = next->point() - point();
00363     }
00364 
00365     // normalize tangent vectors
00366     qreal l1 = sqrt(t1.x() * t1.x() + t1.y() * t1.y());
00367     qreal l2 = sqrt(t2.x() * t2.x() + t2.y() * t2.y());
00368     if (qFuzzyCompare(l1 + 1, qreal(1.0)) || qFuzzyCompare(l2 + 1, qreal(1.0)))
00369         return true;
00370 
00371     t1 /= l1;
00372     t2 /= l2;
00373 
00374     qreal scalar = t1.x() * t2.x() + t1.y() * t2.y();
00375     // tangents are parallel if t1*t2 = |t1|*|t2|
00376     return qFuzzyCompare(scalar, qreal(1.0));
00377 }
00378 
00379 void KoPathPoint::removeFromGroup()
00380 {
00381     if (d->pointGroup)
00382         d->pointGroup->remove(this);
00383     d->pointGroup = 0;
00384 }
00385 
00386 void KoPathPoint::addToGroup(KoPointGroup *pointGroup)
00387 {
00388     if (d->pointGroup && d->pointGroup != pointGroup) {
00389         //TODO error message as this should not happen
00390         removeFromGroup();
00391     }
00392     d->pointGroup = pointGroup;
00393 }
00394 
00395 KoPathPoint::KoPointProperties KoPathPoint::properties() const
00396 {
00397     return d->properties;
00398 }
00399 
00400 QPointF KoPathPoint::point() const
00401 {
00402     return d->point;
00403 }
00404 
00405 QPointF KoPathPoint::controlPoint1() const
00406 {
00407     return d->controlPoint1;
00408 }
00409 
00410 QPointF KoPathPoint::controlPoint2() const
00411 {
00412     return d->controlPoint2;
00413 }
00414 
00415 KoPathShape * KoPathPoint::parent() const
00416 {
00417     return d->shape;
00418 }
00419 
00420 KoPointGroup * KoPathPoint::group()
00421 {
00422     return d->pointGroup;
00423 }