Warning, file /office/calligra/libs/flake/KoPathPoint.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
0003    Copyright (C) 2006-2008 Jan Hambrecht <jaham@gmx.net>
0004    Copyright (C) 2007 Thomas Zander <zander@kde.org>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "KoPathPoint.h"
0023 #include "KoPathShape.h"
0024 
0025 #include <FlakeDebug.h>
0026 #include <QPainter>
0027 #include <QPointF>
0028 
0029 #include <math.h>
0030 
0031 #include <qnumeric.h> // for qIsNaN
0032 static bool qIsNaNPoint(const QPointF &p) {
0033     return qIsNaN(p.x()) || qIsNaN(p.y());
0034 }
0035 
0036 class Q_DECL_HIDDEN KoPathPoint::Private
0037 {
0038 public:
0039     Private()
0040             : shape(0), properties(Normal)
0041             , activeControlPoint1(false), activeControlPoint2(false) {}
0042     KoPathShape * shape;
0043     QPointF point;
0044     QPointF controlPoint1;
0045     QPointF controlPoint2;
0046     PointProperties properties;
0047     bool activeControlPoint1;
0048     bool activeControlPoint2;
0049 };
0050 
0051 KoPathPoint::KoPathPoint(const KoPathPoint &pathPoint)
0052         : d(new Private())
0053 {
0054     d->shape = pathPoint.d->shape;
0055     d->point = pathPoint.d->point;
0056     d->controlPoint1 = pathPoint.d->controlPoint1;
0057     d->controlPoint2 = pathPoint.d->controlPoint2;
0058     d->properties = pathPoint.d->properties;
0059     d->activeControlPoint1 = pathPoint.d->activeControlPoint1;
0060     d->activeControlPoint2 = pathPoint.d->activeControlPoint2;
0061 }
0062 
0063 KoPathPoint::KoPathPoint()
0064         : d(new Private())
0065 {
0066 }
0067 
0068 KoPathPoint::KoPathPoint(KoPathShape * path, const QPointF &point, PointProperties properties)
0069         : d(new Private())
0070 {
0071     d->shape = path;
0072     d->point = point;
0073     d->controlPoint1 = point;
0074     d->controlPoint2 = point;
0075     d->properties = properties;
0076 }
0077 
0078 KoPathPoint::~KoPathPoint()
0079 {
0080     delete d;
0081 }
0082 
0083 KoPathPoint &KoPathPoint::operator=(const KoPathPoint &rhs)
0084 {
0085     if (this == &rhs)
0086         return (*this);
0087 
0088     d->shape = rhs.d->shape;
0089     d->point = rhs.d->point;
0090     d->controlPoint1 = rhs.d->controlPoint1;
0091     d->controlPoint2 = rhs.d->controlPoint2;
0092     d->properties = rhs.d->properties;
0093     d->activeControlPoint1 = rhs.d->activeControlPoint1;
0094     d->activeControlPoint2 = rhs.d->activeControlPoint2;
0095 
0096     return (*this);
0097 }
0098 
0099 bool KoPathPoint::operator == (const KoPathPoint &rhs) const
0100 {
0101     if (d->point != rhs.d->point)
0102         return false;
0103     if (d->controlPoint1 != rhs.d->controlPoint1)
0104         return false;
0105     if (d->controlPoint2 != rhs.d->controlPoint2)
0106         return false;
0107     if (d->properties != rhs.d->properties)
0108         return false;
0109     if (d->activeControlPoint1 != rhs.d->activeControlPoint1)
0110         return false;
0111     if (d->activeControlPoint2 != rhs.d->activeControlPoint2)
0112         return false;
0113     return true;
0114 }
0115 
0116 void KoPathPoint::setPoint(const QPointF &point)
0117 {
0118     d->point = point;
0119     if (d->shape)
0120         d->shape->notifyChanged();
0121 }
0122 
0123 void KoPathPoint::setControlPoint1(const QPointF &point)
0124 {
0125     if (qIsNaNPoint(point)) return;
0126 
0127     d->controlPoint1 = point;
0128     d->activeControlPoint1 = true;
0129     if (d->shape)
0130         d->shape->notifyChanged();
0131 }
0132 
0133 void KoPathPoint::setControlPoint2(const QPointF &point)
0134 {
0135     if (qIsNaNPoint(point)) return;
0136 
0137     d->controlPoint2 = point;
0138     d->activeControlPoint2 = true;
0139     if (d->shape)
0140         d->shape->notifyChanged();
0141 }
0142 
0143 void KoPathPoint::removeControlPoint1()
0144 {
0145     d->activeControlPoint1 = false;
0146     d->properties &= ~IsSmooth;
0147     d->properties &= ~IsSymmetric;
0148     if (d->shape)
0149         d->shape->notifyChanged();
0150 }
0151 
0152 void KoPathPoint::removeControlPoint2()
0153 {
0154     d->activeControlPoint2 = false;
0155     d->properties &= ~IsSmooth;
0156     d->properties &= ~IsSymmetric;
0157     if (d->shape)
0158         d->shape->notifyChanged();
0159 }
0160 
0161 void KoPathPoint::setProperties(PointProperties properties)
0162 {
0163     d->properties = properties;
0164     // CloseSubpath only allowed with StartSubpath or StopSubpath
0165     if ((d->properties & StartSubpath) == 0 && (d->properties & StopSubpath) == 0)
0166         d->properties &= ~CloseSubpath;
0167 
0168     if (! activeControlPoint1() || ! activeControlPoint2()) {
0169         // strip smooth and symmetric flags if point has not two control points
0170         d->properties &= ~IsSmooth;
0171         d->properties &= ~IsSymmetric;
0172     }
0173 
0174     if (d->shape)
0175         d->shape->notifyChanged();
0176 }
0177 
0178 void KoPathPoint::setProperty(PointProperty property)
0179 {
0180     switch (property) {
0181     case StartSubpath:
0182     case StopSubpath:
0183     case CloseSubpath:
0184         // nothing special to do here
0185         break;
0186     case IsSmooth:
0187         d->properties &= ~IsSymmetric;
0188         break;
0189     case IsSymmetric:
0190         d->properties &= ~IsSmooth;
0191         break;
0192     default: return;
0193     }
0194 
0195     d->properties |= property;
0196 
0197     if (! activeControlPoint1() || ! activeControlPoint2()) {
0198         // strip smooth and symmetric flags if point has not two control points
0199         d->properties &= ~IsSymmetric;
0200         d->properties &= ~IsSmooth;
0201     }
0202 }
0203 
0204 void KoPathPoint::unsetProperty(PointProperty property)
0205 {
0206     switch (property) {
0207     case StartSubpath:
0208         if (d->properties & StartSubpath && (d->properties & StopSubpath) == 0)
0209             d->properties &= ~CloseSubpath;
0210         break;
0211     case StopSubpath:
0212         if (d->properties & StopSubpath && (d->properties & StartSubpath) == 0)
0213             d->properties &= ~CloseSubpath;
0214         break;
0215     case CloseSubpath:
0216         if (d->properties & StartSubpath || d->properties & StopSubpath) {
0217             d->properties &= ~IsSmooth;
0218             d->properties &= ~IsSymmetric;
0219         }
0220         break;
0221     case IsSmooth:
0222     case IsSymmetric:
0223         // no others depend on these
0224         break;
0225     default: return;
0226     }
0227     d->properties &= ~property;
0228 }
0229 
0230 bool KoPathPoint::activeControlPoint1() const
0231 {
0232     // only start point on closed subpaths can have a controlPoint1
0233     if ((d->properties & StartSubpath) && (d->properties & CloseSubpath) == 0)
0234         return false;
0235 
0236     return d->activeControlPoint1;
0237 }
0238 
0239 bool KoPathPoint::activeControlPoint2() const
0240 {
0241     // only end point on closed subpaths can have a controlPoint2
0242     if ((d->properties & StopSubpath) && (d->properties & CloseSubpath) == 0)
0243         return false;
0244 
0245     return d->activeControlPoint2;
0246 }
0247 
0248 void KoPathPoint::map(const QTransform &matrix)
0249 {
0250     d->point = matrix.map(d->point);
0251     d->controlPoint1 = matrix.map(d->controlPoint1);
0252     d->controlPoint2 = matrix.map(d->controlPoint2);
0253 
0254     if (d->shape)
0255         d->shape->notifyChanged();
0256 }
0257 
0258 void KoPathPoint::paint(QPainter &painter, int handleRadius, PointTypes types, bool active)
0259 {
0260     QRectF handle(-handleRadius, -handleRadius, 2*handleRadius, 2*handleRadius);
0261 
0262     bool drawControlPoint1 = types & ControlPoint1 && (!active || activeControlPoint1());
0263     bool drawControlPoint2 = types & ControlPoint2 && (!active || activeControlPoint2());
0264 
0265     // draw lines at the bottom
0266     if (drawControlPoint2)
0267         painter.drawLine(point(), controlPoint2());
0268 
0269     if (drawControlPoint1)
0270         painter.drawLine(point(), controlPoint1());
0271 
0272 
0273     QTransform worldMatrix = painter.worldTransform();
0274 
0275     painter.setWorldTransform(QTransform());
0276 
0277     // the point is lowest
0278     if (types & Node) {
0279         if (properties() & IsSmooth)
0280             painter.drawRect(handle.translated(worldMatrix.map(point())));
0281         else if (properties() & IsSymmetric) {
0282             QTransform matrix;
0283             matrix.rotate(45.0);
0284             QPolygonF poly(handle);
0285             poly = matrix.map(poly);
0286             poly.translate(worldMatrix.map(point()));
0287             painter.drawPolygon(poly);
0288         } else
0289             painter.drawEllipse(handle.translated(worldMatrix.map(point())));
0290     }
0291 
0292     // then comes control point 2
0293     if (drawControlPoint2)
0294         painter.drawEllipse(handle.translated(worldMatrix.map(controlPoint2())));
0295 
0296     // then comes control point 1
0297     if (drawControlPoint1)
0298         painter.drawEllipse(handle.translated(worldMatrix.map(controlPoint1())));
0299 
0300     painter.setWorldTransform(worldMatrix);
0301 }
0302 
0303 void KoPathPoint::setParent(KoPathShape* parent)
0304 {
0305     // don't set to zero
0306     //Q_ASSERT(parent);
0307     d->shape = parent;
0308 }
0309 
0310 QRectF KoPathPoint::boundingRect(bool active) const
0311 {
0312     QRectF rect(d->point, QSize(1, 1));
0313     if (!active && activeControlPoint1()) {
0314         QRectF r1(d->point, QSize(1, 1));
0315         r1.setBottomRight(d->controlPoint1);
0316         rect = rect.united(r1);
0317     }
0318     if (!active && activeControlPoint2()) {
0319         QRectF r2(d->point, QSize(1, 1));
0320         r2.setBottomRight(d->controlPoint2);
0321         rect = rect.united(r2);
0322     }
0323     if (d->shape)
0324         return d->shape->shapeToDocument(rect);
0325     else
0326         return rect;
0327 }
0328 
0329 void KoPathPoint::reverse()
0330 {
0331     qSwap(d->controlPoint1, d->controlPoint2);
0332     qSwap(d->activeControlPoint1, d->activeControlPoint2);
0333     PointProperties newProps = Normal;
0334     newProps |= d->properties & IsSmooth;
0335     newProps |= d->properties & IsSymmetric;
0336     newProps |= d->properties & StartSubpath;
0337     newProps |= d->properties & StopSubpath;
0338     newProps |= d->properties & CloseSubpath;
0339     d->properties = newProps;
0340 }
0341 
0342 bool KoPathPoint::isSmooth(KoPathPoint * prev, KoPathPoint * next) const
0343 {
0344     QPointF t1, t2;
0345 
0346     if (activeControlPoint1()) {
0347         t1 = point() - controlPoint1();
0348     } else {
0349         // we need the previous path point but there is none provided
0350         if (! prev)
0351             return false;
0352         if (prev->activeControlPoint2())
0353             t1 = point() - prev->controlPoint2();
0354         else
0355             t1 = point() - prev->point();
0356     }
0357 
0358     if (activeControlPoint2()) {
0359         t2 = controlPoint2() - point();
0360     } else {
0361         // we need the next path point but there is none provided
0362         if (! next)
0363             return false;
0364         if (next->activeControlPoint1())
0365             t2 = next->controlPoint1() - point();
0366         else
0367             t2 = next->point() - point();
0368     }
0369 
0370     // normalize tangent vectors
0371     qreal l1 = sqrt(t1.x() * t1.x() + t1.y() * t1.y());
0372     qreal l2 = sqrt(t2.x() * t2.x() + t2.y() * t2.y());
0373     if (qFuzzyCompare(l1 + 1, qreal(1.0)) || qFuzzyCompare(l2 + 1, qreal(1.0)))
0374         return true;
0375 
0376     t1 /= l1;
0377     t2 /= l2;
0378 
0379     qreal scalar = t1.x() * t2.x() + t1.y() * t2.y();
0380     // tangents are parallel if t1*t2 = |t1|*|t2|
0381     return qFuzzyCompare(scalar, qreal(1.0));
0382 }
0383 
0384 KoPathPoint::PointProperties KoPathPoint::properties() const
0385 {
0386     return d->properties;
0387 }
0388 
0389 QPointF KoPathPoint::point() const
0390 {
0391     return d->point;
0392 }
0393 
0394 QPointF KoPathPoint::controlPoint1() const
0395 {
0396     return d->controlPoint1;
0397 }
0398 
0399 QPointF KoPathPoint::controlPoint2() const
0400 {
0401     return d->controlPoint2;
0402 }
0403 
0404 KoPathShape * KoPathPoint::parent() const
0405 {
0406     return d->shape;
0407 }