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 */