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 }