File indexing completed on 2024-05-12 15:56:44
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2006, 2011 Thorsten Zachmann <zachmann@kde.org> 0003 SPDX-FileCopyrightText: 2007, 2009 Thomas Zander <zander@kde.org> 0004 SPDX-FileCopyrightText: 2006-2008 Jan Hambrecht <jaham@gmx.net> 0005 SPDX-FileCopyrightText: 2011 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #ifndef KOPATHSHAPE_H 0011 #define KOPATHSHAPE_H 0012 0013 #include "kritaflake_export.h" 0014 0015 #include <QMetaType> 0016 #include <QTransform> 0017 0018 #include "KoTosContainer.h" 0019 0020 #define KoPathShapeId "KoPathShape" 0021 0022 class KoPathSegment; 0023 class KoPathPoint; 0024 class KoPathShapePrivate; 0025 class KoMarker; 0026 class KisHandlePainterHelper; 0027 0028 typedef QPair<int, int> KoPathPointIndex; 0029 0030 /// a KoSubpath contains a path from a moveTo until a close or a new moveTo 0031 typedef QList<KoPathPoint *> KoSubpath; 0032 typedef QList<KoSubpath *> KoSubpathList; 0033 /// The position of a path point within a path shape 0034 /** 0035 * @brief This is the base for all graphical objects. 0036 * 0037 * All graphical objects are based on this object e.g. lines, rectangulars, pies 0038 * and so on. 0039 * 0040 * The KoPathShape uses KoPathPoint's to describe the path of the shape. 0041 * 0042 * Here a short example: 0043 * 3 points connected by a curveTo's described by the following svg: 0044 * M 100,200 C 100,100 250,100 250,200 C 250,200 400,300 400,200. 0045 * 0046 * This will be stored in 3 KoPathPoint's as 0047 * The first point contains in 0048 * point 100,200 0049 * controlPoint2 100,100 0050 * The second point contains in 0051 * point 250,200 0052 * controlPoint1 250,100 0053 * controlPoint2 250,300 0054 * The third point contains in 0055 * point 400,300 0056 * controlPoint1 400,200 0057 * 0058 * Not the segments are stored but the points. Out of the points the segments are 0059 * generated. See the outline method. The reason for storing it like that is that 0060 * it is the points that are modified by the user and not the segments. 0061 */ 0062 class KRITAFLAKE_EXPORT KoPathShape : public KoTosContainer 0063 { 0064 public: 0065 /** 0066 * @brief constructor 0067 */ 0068 KoPathShape(); 0069 0070 /** 0071 * @brief 0072 */ 0073 ~KoPathShape() override; 0074 0075 KoShape *cloneShape() const override; 0076 0077 /// reimplemented 0078 void paint(QPainter &painter) const override; 0079 virtual void paintPoints(KisHandlePainterHelper &handlesHelper); 0080 0081 /// reimplemented 0082 QRectF outlineRect() const override; 0083 /// reimplemented 0084 QPainterPath outline() const override; 0085 /// reimplemented 0086 QRectF boundingRect() const override; 0087 /// reimplemented 0088 QSizeF size() const override; 0089 0090 QPainterPath pathStroke(const QPen &pen) const; 0091 /** 0092 * Resize the shape 0093 * 0094 * This makes sure that the pathshape will not be resized to 0 if the new size 0095 * is null as that makes it impossible to undo the change. 0096 * 0097 * All functions that overwrite this function should also use the resizeMatrix 0098 * function to get and use the same data in resizing. 0099 * 0100 * @see resizeMatrix() 0101 */ 0102 void setSize(const QSizeF &size) override; 0103 0104 /// reimplemented 0105 bool hitTest(const QPointF &position) const override; 0106 0107 /// Removes all subpaths and their points from the path 0108 void clear(); 0109 /** 0110 * @brief Starts a new Subpath 0111 * 0112 * Moves the pen to p and starts a new subpath. 0113 * 0114 * @return the newly created point 0115 */ 0116 KoPathPoint *moveTo(const QPointF &p); 0117 0118 /** 0119 * @brief Adds a new line segment 0120 * 0121 * Adds a straight line between the last point and the given point p. 0122 * 0123 * @return the newly created point 0124 */ 0125 KoPathPoint *lineTo(const QPointF &p); 0126 0127 /** 0128 * @brief Adds a new cubic Bezier curve segment. 0129 * 0130 * Adds a cubic Bezier curve between the last point and the given point p, 0131 * using the control points specified by c1 and c2. 0132 * 0133 * @param c1 control point1 0134 * @param c2 control point2 0135 * @param p the endpoint of this curve segment 0136 * 0137 * @return The newly created point 0138 */ 0139 KoPathPoint *curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p); 0140 0141 /** 0142 * @brief Adds a new quadratic Bezier curve segment. 0143 * 0144 * Adds a quadratic Bezier curve between the last point and the given point p, 0145 * using the control point specified by c. 0146 * 0147 * @param c control point 0148 * @param p the endpoint of this curve segment 0149 * 0150 * @return The newly created point 0151 */ 0152 KoPathPoint *curveTo(const QPointF &c, const QPointF &p); 0153 0154 /** 0155 * @brief Add an arc. 0156 * 0157 * Adds an arc starting at the current point. The arc will be converted to bezier curves. 0158 * 0159 * @param rx x radius of the ellipse 0160 * @param ry y radius of the ellipse 0161 * @param startAngle the angle where the arc will be started 0162 * @param sweepAngle the length of the angle 0163 * TODO add param to have angle of the ellipse 0164 * 0165 * @return The newly created point 0166 */ 0167 KoPathPoint *arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle); 0168 0169 0170 /** 0171 * @brief Closes the current subpath 0172 */ 0173 void close(); 0174 0175 /** 0176 * @brief Closes the current subpath 0177 * 0178 * It tries to merge the last and first point of the subpath 0179 * to one point and then closes the subpath. If merging is not 0180 * possible as the two point are to far from each other a close 0181 * will be done. 0182 * TODO define a maximum distance between the two points until this is working 0183 */ 0184 void closeMerge(); 0185 0186 /** 0187 * @brief Normalizes the path data. 0188 * 0189 * The path points are transformed so that the top-left corner 0190 * of the bounding rect is at (0,0). 0191 * This should be called after adding points to the path or changing 0192 * positions of path points. 0193 * @return the offset by which the points are moved in shape coordinates. 0194 */ 0195 virtual QPointF normalize(); 0196 0197 /** 0198 * @brief Returns the path points within the given rectangle. 0199 * @param rect the rectangle the requested points are in 0200 * @param useControlPoints wether to add control points to result or not 0201 * @return list of points within the rectangle 0202 */ 0203 QList<KoPathPoint*> pointsAt(const QRectF &rect, const bool useControlPoints = false) const; 0204 0205 /** 0206 * @brief Returns the list of path segments within the given rectangle. 0207 * @param rect the rectangle the requested segments are in 0208 * @return list of segments within the rectangle 0209 */ 0210 QList<KoPathSegment> segmentsAt(const QRectF &rect) const; 0211 0212 /** 0213 * @brief Returns the path point index of a given path point 0214 * 0215 * @param point the point for which you want to get the index 0216 * @return path point index of the point if it exists 0217 * otherwise KoPathPointIndex( -1, -1 ) 0218 */ 0219 KoPathPointIndex pathPointIndex(const KoPathPoint *point) const; 0220 0221 /** 0222 * @brief Returns the path point specified by a path point index 0223 * 0224 * @param pointIndex index of the point to get 0225 * 0226 * @return KoPathPoint on success, 0 otherwise e.g. out of bounds 0227 */ 0228 KoPathPoint *pointByIndex(const KoPathPointIndex &pointIndex) const; 0229 0230 /** 0231 * @brief Returns the segment specified by a path point index 0232 * 0233 * A segment is defined by the point index of the first point in the segment. 0234 * A segment contains the defined point and its following point. If the subpath is 0235 * closed and the and the pointIndex point to the last point in the subpath, the 0236 * following point is the first point in the subpath. 0237 * 0238 * @param pointIndex index of the first point of the segment 0239 * 0240 * @return Segment containing both points of the segment or KoPathSegment( 0, 0 ) on error e.g. out of bounds 0241 */ 0242 KoPathSegment segmentByIndex(const KoPathPointIndex &pointIndex) const; 0243 0244 /** 0245 * @brief Returns the number of points in the path 0246 * 0247 * @return The number of points in the path 0248 */ 0249 int pointCount() const; 0250 0251 /** 0252 * @brief Returns the number of subpaths in the path 0253 * 0254 * @return The number of subpaths in the path 0255 */ 0256 int subpathCount() const; 0257 0258 /** 0259 * @brief Returns the number of points in a subpath 0260 * 0261 * @return The number of points in the subpath or -1 if subpath out of bounds 0262 */ 0263 int subpathPointCount(int subpathIndex) const; 0264 0265 /** 0266 * @brief Checks if a subpath is closed 0267 * 0268 * @param subpathIndex index of the subpath to check 0269 * 0270 * @return true when the subpath is closed, false otherwise 0271 */ 0272 bool isClosedSubpath(int subpathIndex) const; 0273 0274 /** 0275 * @brief Inserts a new point into the given subpath at the specified position 0276 * 0277 * This method keeps the subpath closed if it is closed, and open when it was 0278 * open. So it can change the properties of the point inserted. 0279 * You might need to update the point before/after to get the desired result 0280 * e.g. when you insert the point into a curve. 0281 * 0282 * @param point to insert 0283 * @param pointIndex index at which the point should be inserted 0284 * 0285 * @return true on success, 0286 * false when pointIndex is out of bounds 0287 */ 0288 bool insertPoint(KoPathPoint *point, const KoPathPointIndex &pointIndex); 0289 0290 /** 0291 * @brief Removes a point from the path. 0292 * 0293 * Note that the ownership of the point will pass to the caller. 0294 * 0295 * @param pointIndex index of the point which should be removed 0296 * 0297 * @return The removed point on success, 0298 * otherwise 0 0299 */ 0300 KoPathPoint *removePoint(const KoPathPointIndex &pointIndex); 0301 0302 /** 0303 * @brief Breaks the path after the point index 0304 * 0305 * The new subpath will be behind the one that was broken. The segment between 0306 * the given point and the one behind will be removed. If you want to split at 0307 * one point insert first a copy of the point behind it. 0308 * This does not work when the subpath is closed. Use openSubpath for this. 0309 * It does not break at the last position of a subpath or if there is only one 0310 * point in the subpath. 0311 * 0312 * @param pointIndex index of the point after which the path should be broken 0313 * 0314 * @return true if the subpath was broken, otherwise false 0315 */ 0316 bool breakAfter(const KoPathPointIndex &pointIndex); 0317 0318 /** 0319 * @brief Joins the given subpath with the following one 0320 * 0321 * Joins the given subpath with the following one by inserting a segment between 0322 * the two subpaths. 0323 * This does nothing if the specified subpath is the last subpath 0324 * or one of both subpaths is closed. 0325 * 0326 * @param subpathIndex index of the subpath being joined with the following subpath 0327 * 0328 * @return true if the subpath was joined, otherwise false 0329 */ 0330 bool join(int subpathIndex); 0331 0332 /** 0333 * @brief Moves the position of a subpath within a path 0334 * 0335 * @param oldSubpathIndex old index of the subpath 0336 * @param newSubpathIndex new index of the subpath 0337 * 0338 * @return true if the subpath was moved, otherwise false e.g. if an index is out of bounds 0339 */ 0340 bool moveSubpath(int oldSubpathIndex, int newSubpathIndex); 0341 0342 /** 0343 * @brief Opens a closed subpath 0344 * 0345 * The subpath is opened by removing the segment before the given point, making 0346 * the given point the new start point of the subpath. 0347 * 0348 * @param pointIndex the index of the point at which to open the closed subpath 0349 * @return the new position of the old first point in the subpath 0350 * otherwise KoPathPointIndex( -1, -1 ) 0351 */ 0352 KoPathPointIndex openSubpath(const KoPathPointIndex &pointIndex); 0353 0354 /** 0355 * @brief Close a open subpath 0356 * 0357 * The subpath is closed be inserting a segment between the start and end point, making 0358 * the given point the new start point of the subpath. 0359 * 0360 * @return the new position of the old first point in the subpath 0361 * otherwise KoPathPointIndex( -1, -1 ) 0362 */ 0363 KoPathPointIndex closeSubpath(const KoPathPointIndex &pointIndex); 0364 0365 /** 0366 * @brief Reverse subpath 0367 * 0368 * The last point becomes the first point and the first one becomes the last one. 0369 * 0370 * @param subpathIndex the index of the subpath to reverse 0371 */ 0372 bool reverseSubpath(int subpathIndex); 0373 0374 /** 0375 * @brief Removes subpath from the path 0376 * @param subpathIndex the index of the subpath to remove 0377 * @return the removed subpath on success, 0 otherwise. 0378 */ 0379 KoSubpath *removeSubpath(int subpathIndex); 0380 0381 /** 0382 * @brief Adds a subpath at the given index to the path 0383 * @param subpath the subpath to add 0384 * @param subpathIndex the index at which the new subpath should be inserted 0385 * @return true on success, false otherwise e.g. subpathIndex out of bounds 0386 */ 0387 bool addSubpath(KoSubpath *subpath, int subpathIndex); 0388 0389 /** 0390 * @brief Combines two path shapes by appending the data of the specified path. 0391 * @param path the path to combine with 0392 * @return index of the first segment inserted or -1 on failure 0393 */ 0394 int combine(KoPathShape *path); 0395 0396 /** 0397 * @brief Creates separate path shapes, one for each existing subpath. 0398 * @param separatedPaths the list which contains the separated path shapes 0399 * @return true if separating the path was successful, false otherwise 0400 */ 0401 bool separate(QList<KoPathShape*> &separatedPaths); 0402 0403 /** 0404 * Returns the specific path shape id. 0405 * 0406 * Path shape derived shapes have a different shape id which link them 0407 * to their respective shape factories. In most cases they do not have 0408 * a special tool for editing them. 0409 * This function returns the specific shape id for finding the shape 0410 * factory from KoShapeRegistry. The default KoPathShapeId is returned 0411 * from KoShape::shapeId() so that the generic path editing tool gets 0412 * activated when the shape is selected. 0413 * 0414 * @return the specific shape id 0415 */ 0416 virtual QString pathShapeId() const; 0417 0418 /// Returns a odf/svg string representation of the path data with the given matrix applied. 0419 QString toString(const QTransform &matrix = QTransform()) const; 0420 0421 /** 0422 * @brief Saves the node types 0423 * 0424 * This is inspired by inkscape and uses the same mechanism as they do. 0425 * This attribute contains of a string which has the node type of each point 0426 * in it. The following node types exist: 0427 * 0428 * c corner 0429 * s smooth 0430 * z symmetric 0431 * 0432 * The first point of a path is always of the type c. 0433 * If the path is closed the type of the first point is saved in the last element 0434 * E.g. you have a closed path with 2 points in it. The first one (start/end of path) 0435 * is symmetric and the second one is smooth that will result in the nodeType="czs" 0436 * So if there is a closed sub path the nodeTypes contain one more entry then there 0437 * are points. That is due to the first and the last point of a closed sub path get 0438 * merged into one when they are on the same position. 0439 * 0440 * @return The node types as string 0441 */ 0442 QString nodeTypes() const; 0443 0444 /** 0445 * @brief Loads node types 0446 */ 0447 void loadNodeTypes(const QString &nodeTypes); 0448 0449 /// Returns the fill rule for the path object 0450 Qt::FillRule fillRule() const; 0451 0452 /// Sets the fill rule to be used for painting the background 0453 void setFillRule(Qt::FillRule fillRule); 0454 0455 /// Creates path shape from given QPainterPath 0456 static KoPathShape *createShapeFromPainterPath(const QPainterPath &path); 0457 0458 void setMarker(KoMarker *marker, KoFlake::MarkerPosition pos); 0459 KoMarker* marker(KoFlake::MarkerPosition pos) const; 0460 bool hasMarkers() const; 0461 0462 bool autoFillMarkers() const; 0463 void setAutoFillMarkers(bool value); 0464 0465 public: 0466 struct KRITAFLAKE_EXPORT PointSelectionChangeListener : public ShapeChangeListener { 0467 void notifyShapeChanged(ChangeType type, KoShape *shape) override; 0468 virtual void recommendPointSelectionChange(KoPathShape *shape, const QList<KoPathPointIndex> &newSelection) = 0; 0469 virtual void notifyPathPointsChanged(KoPathShape *shape) = 0; 0470 }; 0471 0472 void recommendPointSelectionChange(const QList<KoPathPointIndex> &newSelection); 0473 0474 protected: 0475 void notifyPointsChanged(); 0476 0477 protected: 0478 /// constructor: to be used in cloneShape(), not in descendants! 0479 /// \internal 0480 /// XXX private? 0481 KoPathShape(const KoPathShape &rhs); 0482 0483 protected: 0484 0485 /** 0486 * @brief Add an arc. 0487 * 0488 * Adds an arc starting at the current point. The arc will be converted to bezier curves. 0489 * @param rx x radius of the ellipse 0490 * @param ry y radius of the ellipse 0491 * @param startAngle the angle where the arc will be started 0492 * @param sweepAngle the length of the angle 0493 * TODO add param to have angle of the ellipse 0494 * @param offset to the first point in the arc 0495 * @param curvePoints a array which take the curve points, pass a 'QPointF curvePoins[12]'; 0496 * 0497 * @return number of points created by the curve 0498 */ 0499 int arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF &offset, QPointF *curvePoints) const; 0500 0501 /** 0502 * Get the resize matrix 0503 * 0504 * This makes sure that also if the newSize isNull that there will be a 0505 * very small size of 0.000001 pixels 0506 */ 0507 QTransform resizeMatrix( const QSizeF &newSize ) const; 0508 0509 private: 0510 /// close-merges specified subpath 0511 void closeMergeSubpathPriv(KoSubpath *subpath); 0512 /// closes specified subpath 0513 void closeSubpathPriv(KoSubpath *subpath); 0514 void updateLastPriv(KoPathPoint **lastPoint); 0515 0516 protected: 0517 const KoSubpathList &subpaths() const; 0518 /// XXX: refactor this using setter? 0519 KoSubpathList &subpaths(); 0520 void map(const QTransform &matrix); 0521 0522 private: 0523 class Private; 0524 QScopedPointer<Private> d; 0525 }; 0526 0527 Q_DECLARE_METATYPE(KoPathShape*) 0528 0529 #endif /* KOPATHSHAPE_H */