Krita Source Code Documentation
Loading...
Searching...
No Matches
CutThroughShapeStrategy Class Reference

#include <CutThroughShapeStrategy.h>

+ Inheritance diagram for CutThroughShapeStrategy:

Public Member Functions

KUndo2CommandcreateCommand () override
 
 CutThroughShapeStrategy (KoToolBase *tool, KoSelection *selection, const QList< KoShape * > &allShapes, QPointF startPoint, const GutterWidthsConfig &width)
 
void finishInteraction (Qt::KeyboardModifiers modifiers) override
 
void handleMouseMove (const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override
 
void paint (QPainter &painter, const KoViewConverter &converter) override
 
 ~CutThroughShapeStrategy () override
 
- Public Member Functions inherited from KoInteractionStrategy
virtual void cancelInteraction ()
 
 KoInteractionStrategy (KoToolBase *parent)
 constructor
 
KoToolBasetool () const
 
virtual ~KoInteractionStrategy ()
 Destructor.
 

Private Member Functions

qreal calculateLineAngle (QPointF start, QPointF end)
 
qreal gutterWidthInDocumentCoordinates (qreal lineAngle)
 

Private Attributes

QList< KoShape * > m_allShapes
 
QPointF m_endPoint = QPointF()
 
QRectF m_previousLineDirtyRect = QRectF()
 
QList< KoShape * > m_selectedShapes
 
QPointF m_startPoint = QPointF()
 
GutterWidthsConfig m_width
 

Additional Inherited Members

- Protected Member Functions inherited from KoInteractionStrategy
uint decorationThickness () const
 
uint grabSensitivity () const
 Convenience function to get the global grab sensitivity.
 
uint handleRadius () const
 Convenience function to get the global handle radius.
 
 KoInteractionStrategy (KoInteractionStrategyPrivate &)
 constructor
 
- Protected Attributes inherited from KoInteractionStrategy
KoInteractionStrategyPrivated_ptr
 

Detailed Description

Definition at line 23 of file CutThroughShapeStrategy.h.

Constructor & Destructor Documentation

◆ CutThroughShapeStrategy()

CutThroughShapeStrategy::CutThroughShapeStrategy ( KoToolBase * tool,
KoSelection * selection,
const QList< KoShape * > & allShapes,
QPointF startPoint,
const GutterWidthsConfig & width )

Definition at line 29 of file CutThroughShapeStrategy.cpp.

31 , m_startPoint(startPoint)
32 , m_endPoint(startPoint)
33 , m_width(width)
34{
36 m_allShapes = shapes;
37}
QList< KoShape * > m_selectedShapes
KoInteractionStrategy(KoToolBase *parent)
constructor
const QList< KoShape * > selectedEditableShapes() const

References m_allShapes, m_selectedShapes, and KoSelection::selectedEditableShapes().

◆ ~CutThroughShapeStrategy()

CutThroughShapeStrategy::~CutThroughShapeStrategy ( )
override

Definition at line 39 of file CutThroughShapeStrategy.cpp.

40{
41
42}

Member Function Documentation

◆ calculateLineAngle()

qreal CutThroughShapeStrategy::calculateLineAngle ( QPointF start,
QPointF end )
private

Definition at line 356 of file CutThroughShapeStrategy.cpp.

357{
358 QPointF vec = end - start;
359 qreal angleDegrees = KisAlgebra2D::wrapValue(kisRadiansToDegrees(std::atan2(vec.y(), vec.x())), 0.0, 360.0);
360 return angleDegrees;
361}
T kisRadiansToDegrees(T radians)
Definition kis_global.h:181
T wrapValue(T value, T wrapBounds)

References kisRadiansToDegrees(), and KisAlgebra2D::wrapValue().

◆ createCommand()

KUndo2Command * CutThroughShapeStrategy::createCommand ( )
overridevirtual

For interactions that are undo-able this method should be implemented to return such a command. Implementations should return 0 otherwise.

Returns
a command, or 0.

Implements KoInteractionStrategy.

Definition at line 44 of file CutThroughShapeStrategy.cpp.

45{
46 // TODO: undoing
47 return 0;
48}

◆ finishInteraction()

void CutThroughShapeStrategy::finishInteraction ( Qt::KeyboardModifiers modifiers)
overridevirtual

Override to make final changes to the data on the end of an interaction.

Implements KoInteractionStrategy.

Definition at line 87 of file CutThroughShapeStrategy.cpp.

88{
90
91
92 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(tool()->canvas());
94 const QTransform booleanWorkaroundTransform = KritaUtils::pathShapeBooleanSpaceWorkaround(kisCanvas->image());
95
96 QList<QPainterPath> srcOutlines;
97 QRectF outlineRect;
98
99 if (m_allShapes.length() == 0) {
100 qCritical() << "No shapes are available";
101 return;
102 }
103
104 Q_FOREACH (KoShape *shape, m_allShapes) {
105
106 QPainterPath outlineHere =
107 booleanWorkaroundTransform.map(
108 shape->absoluteTransformation().map(
109 shape->outline()));
110
111 srcOutlines << outlineHere;
112 outlineRect |= outlineHere.boundingRect();//booleanWorkaroundTransform.map(shape->absoluteOutlineRect()).boundingRect();
113 }
114
115 if (outlineRect.isEmpty()) {
116 //qCritical() << "The outline rect is empty";
117 return;
118 }
119
120 QRectF outlineRectBigger = kisGrowRect(outlineRect, 10);
121 QRect outlineRectBiggerInt = outlineRectBigger.toRect();
122
123 QLineF gapLine = QLineF(m_startPoint, m_endPoint);
124 qreal eps = 0.0000001;
125 if (gapLine.length() < eps) {
126 return;
127 }
128
130
131 QList<QLineF> gapLines = KisAlgebra2D::getParallelLines(gapLine, gutterWidth/2);
132
133 gapLine = booleanWorkaroundTransform.map(gapLine);
134 gapLines[0] = booleanWorkaroundTransform.map(gapLines[0]);
135 gapLines[1] = booleanWorkaroundTransform.map(gapLines[1]);
136
137 QLineF leftLine = gapLines[0];
138 QLineF rightLine = gapLines[1];
139
140 QLineF leftLineLong = leftLine;
141 QLineF rightLineLong = rightLine;
142
143
144
145 KisAlgebra2D::cropLineToRect(leftLineLong, outlineRectBiggerInt, true, true);
146 KisAlgebra2D::cropLineToRect(rightLineLong, outlineRectBiggerInt, true, true);
147
148 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Knife tool: cut through shapes"));
149
150
151 new KoKeepShapesSelectedCommand(m_selectedShapes, {}, kisCanvas->selectedShapesProxy(), false, cmd);
152
153
154 if (leftLine.length() == 0 || rightLine.length() == 0) {
155 KIS_SAFE_ASSERT_RECOVER_RETURN(gapLine.length() != 0 && gapLines[0].length() != 0 && gapLines[1].length() != 0 && "Original gap lines shouldn't be empty at this point");
156 // looks like *all* shapes need to be cut out
157
158 tool()->canvas()->shapeController()->removeShapes(m_allShapes, cmd);
159 tool()->canvas()->addCommand(cmd);
160 return;
161 }
162
163
164 QList<QPainterPath> paths = KisAlgebra2D::getPathsFromRectangleCutThrough(QRectF(outlineRectBiggerInt), leftLineLong, rightLineLong);
165 QPainterPath left = paths[0];
166 QPainterPath right = paths[1];
167
168 QList<QPainterPath> pathsOpposite = KisAlgebra2D::getPathsFromRectangleCutThrough(QRectF(outlineRectBiggerInt), rightLineLong, leftLineLong);
169 QPainterPath leftOpposite = pathsOpposite[0];
170 QPainterPath rightOpposite = pathsOpposite[1];
171
172 QList<KoShape*> newSelectedShapes;
173
174 QList<KoShape*> shapesToRemove;
175
176 QTransform booleanWorkaroundTransformInverted = booleanWorkaroundTransform.inverted();
177
178 QRectF gapLineLeftRect = KisAlgebra2D::createRectFromCorners(leftLine);
179 QRectF gapLineRightRect = KisAlgebra2D::createRectFromCorners(rightLine);
180
181
182 for (int i = 0; i < srcOutlines.size(); i++) {
183
184 KoShape* referenceShape = m_allShapes[i];
185 bool wasSelected = m_selectedShapes.contains(referenceShape);
186
187 if ((srcOutlines[i].boundingRect() & leftOpposite.boundingRect()).isEmpty()
188 || (srcOutlines[i].boundingRect() & rightOpposite.boundingRect()).isEmpty()) {
189 // there is nothing on one side
190 // everything is on the other, far away from the gap line
191 // it just makes it a bit faster when there is a whole lot of shapes
192
193 if (wasSelected) {
194 newSelectedShapes << referenceShape;
195 }
196 continue;
197 }
198
199 if ((srcOutlines[i].boundingRect() & gapLineLeftRect).isEmpty()
200 || (srcOutlines[i].boundingRect() & gapLineRightRect).isEmpty()) {
201 // the gap lines can't cross the shape since their bounding rects don't cross
202 if (wasSelected) {
203 newSelectedShapes << referenceShape;
204 }
205 continue;
206 }
207
208 // either one of the points is inside the path, or the line crosses one of the segments
209 bool containsGapLinePoint = srcOutlines[i].contains(leftLine.p1()) || srcOutlines[i].contains(leftLine.p2())
210 || srcOutlines[i].contains(rightLine.p1()) || srcOutlines[i].contains(rightLine.p2());
211 bool crossesGapLine = KisAlgebra2D::getLineSegmentCrossingLineIndexes(leftLine, srcOutlines[i]).count() > 0
212 || KisAlgebra2D::getLineSegmentCrossingLineIndexes(rightLine, srcOutlines[i]).count() > 0;
213 if (!containsGapLinePoint && !crossesGapLine) {
214
215 //qCritical() << "it doesn't cross the line!";
216 if (wasSelected) {
217 newSelectedShapes << referenceShape;
218 }
219 continue;
220 }
221
222
223
224
225 QPainterPath leftPath = srcOutlines[i] & left;
226 QPainterPath rightPath = srcOutlines[i] & right;
227
228 QList<QPainterPath> bothSides;
229 bothSides << leftPath << rightPath;
230
231
232 Q_FOREACH(QPainterPath path, bothSides) {
233 if (path.isEmpty()) {
234 continue;
235 }
236
237 // comment copied from another place:
238 // there is a bug in Qt, sometimes it leaves the resulting
239 // outline open, so just close it explicitly.
240 path.closeSubpath();
241 // this is needed because Qt linearize curves; this allows for a
242 // "sane" linearization instead of a very blocky appearance
243 path = booleanWorkaroundTransformInverted.map(path);
245
246 if (shape->boundingRect().isEmpty()) {
247 continue;
248 }
249
250 shape->setBackground(referenceShape->background());
251 shape->setStroke(referenceShape->stroke());
252 shape->setZIndex(referenceShape->zIndex());
253
254 KoShapeContainer *parent = referenceShape->parent();
255 tool()->canvas()->shapeController()->addShapeDirect(shape, parent, cmd);
256
257 if (wasSelected) {
258 newSelectedShapes << shape;
259 }
260
261
262 }
263
264 // that happens no matter if there was any non-empty shape
265 // because if there is none, maybe they just were underneath the gap
266 shapesToRemove << m_allShapes[i];
267
268 }
269
270 tool()->canvas()->shapeController()->removeShapes(shapesToRemove, cmd);
271 new KoKeepShapesSelectedCommand({}, newSelectedShapes, tool()->canvas()->selectedShapesProxy(), true, cmd);
272
273
274 tool()->canvas()->addCommand(cmd);
275
276
277
278}
qreal calculateLineAngle(QPointF start, QPointF end)
qreal gutterWidthInDocumentCoordinates(qreal lineAngle)
KisSelectedShapesProxy selectedShapesProxy
KisImageWSP image() const
QPointer< KoShapeController > shapeController
virtual void updateCanvas(const QRectF &rc)=0
virtual void addCommand(KUndo2Command *command)=0
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
The position of a path point within a path shape.
Definition KoPathShape.h:63
QRectF boundingRect() const override
reimplemented
static KoPathShape * createShapeFromPainterPath(const QPainterPath &path)
Creates path shape from given QPainterPath.
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:787
virtual QPainterPath outline() const
Definition KoShape.cpp:559
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:890
KoShapeContainer * parent() const
Definition KoShape.cpp:862
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:904
QTransform absoluteTransformation() const
Definition KoShape.cpp:335
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:751
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:759
qint16 zIndex() const
Definition KoShape.cpp:529
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
const qreal eps
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
KUndo2MagicString kundo2_i18n(const char *text)
QList< QPainterPath > getPathsFromRectangleCutThrough(const QRectF &rect, const QLineF &leftLine, const QLineF &rightLine)
getPathsFromRectangleCutThrough get paths defining both sides of a rectangle cut through using two (s...
void cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)
Crop line to rect; if it doesn't intersect, just return an empty line (QLineF()).
QList< QLineF > getParallelLines(const QLineF &line, const qreal distance)
QList< int > getLineSegmentCrossingLineIndexes(const QLineF &line, const QPainterPath &shape)
PointTypeTraits< Point >::rect_type createRectFromCorners(Point corner1, Point corner2)
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
Definition KisForest.h:327
QTransform pathShapeBooleanSpaceWorkaround(KisImageSP image)

References KoShape::absoluteTransformation(), KoCanvasBase::addCommand(), KoShape::background(), KoPathShape::boundingRect(), calculateLineAngle(), KoToolBase::canvas(), KisAlgebra2D::createRectFromCorners(), KoPathShape::createShapeFromPainterPath(), KisAlgebra2D::cropLineToRect(), eps, KisAlgebra2D::getLineSegmentCrossingLineIndexes(), KisAlgebra2D::getParallelLines(), KisAlgebra2D::getPathsFromRectangleCutThrough(), gutterWidthInDocumentCoordinates(), KisCanvas2::image(), KIS_SAFE_ASSERT_RECOVER_RETURN, kisGrowRect(), kundo2_i18n(), m_allShapes, m_endPoint, m_previousLineDirtyRect, m_selectedShapes, m_startPoint, KoShape::outline(), KoShape::parent(), KritaUtils::pathShapeBooleanSpaceWorkaround(), KoCanvasBase::selectedShapesProxy(), KisCanvas2::selectedShapesProxy, KoShape::setBackground(), KoShape::setStroke(), KoShape::setZIndex(), KoCanvasBase::shapeController, KoShape::stroke(), KoInteractionStrategy::tool(), KoCanvasBase::updateCanvas(), and KoShape::zIndex().

◆ gutterWidthInDocumentCoordinates()

qreal CutThroughShapeStrategy::gutterWidthInDocumentCoordinates ( qreal lineAngle)
private

Definition at line 347 of file CutThroughShapeStrategy.cpp.

348{
349 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(tool()->canvas());
351 QLineF helperGapWidthLine = QLineF(QPointF(0, 0), QPointF(0, m_width.widthForAngleInPixels(lineAngle)));
352 QLineF helperGapWidthLineTransformed = kisCanvas->coordinatesConverter()->imageToDocument(helperGapWidthLine);
353 return helperGapWidthLineTransformed.length();
354}
qreal widthForAngleInPixels(qreal lineAngleDegrees)
KisCoordinatesConverter * coordinatesConverter
_Private::Traits< T >::Result imageToDocument(const T &obj) const
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129

References KoToolBase::canvas(), KisCanvas2::coordinatesConverter, KisCoordinatesConverter::imageToDocument(), KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE, m_width, KoInteractionStrategy::tool(), and GutterWidthsConfig::widthForAngleInPixels().

◆ handleMouseMove()

void CutThroughShapeStrategy::handleMouseMove ( const QPointF & mouseLocation,
Qt::KeyboardModifiers modifiers )
overridevirtual

Extending classes should implement this method to update the selectedShapes based on the new mouse position.

Parameters
mouseLocationthe new location in pt
modifiersOR-ed set of keys pressed.

Implements KoInteractionStrategy.

Definition at line 72 of file CutThroughShapeStrategy.cpp.

73{
74 m_endPoint = snapEndPoint(m_startPoint, mouseLocation, modifiers);
75 QRectF dirtyRect;
78 dirtyRect = kisGrowRect(dirtyRect, gutterWidthInDocumentCoordinates(calculateLineAngle(m_startPoint, m_endPoint))); // twice as much as it should need to account for lines showing the effect
79
80 QRectF accumulatedWithPrevious = m_previousLineDirtyRect | dirtyRect;
81
82 tool()->canvas()->updateCanvas(accumulatedWithPrevious);
83 m_previousLineDirtyRect = dirtyRect;
84
85}
QPointF snapEndPoint(const QPointF &startPoint, const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
void accumulateBounds(const Point &pt, Rect *bounds)

References KisAlgebra2D::accumulateBounds(), calculateLineAngle(), KoToolBase::canvas(), gutterWidthInDocumentCoordinates(), kisGrowRect(), m_endPoint, m_previousLineDirtyRect, m_startPoint, snapEndPoint(), KoInteractionStrategy::tool(), and KoCanvasBase::updateCanvas().

◆ paint()

void CutThroughShapeStrategy::paint ( QPainter & painter,
const KoViewConverter & converter )
overridevirtual

Reimplement this if the action needs to draw a "blob" on the canvas; that is, a transient decoration like a rubber band.

Reimplemented from KoInteractionStrategy.

Definition at line 280 of file CutThroughShapeStrategy.cpp.

281{
282 painter.save();
283
284 QColor semitransparentGray = QColor(Qt::darkGray);
285 semitransparentGray.setAlphaF(0.6);
286 QPen pen = QPen(QBrush(semitransparentGray), 2);
287 painter.setPen(pen);
288
289 painter.setRenderHint(QPainter::RenderHint::Antialiasing, true);
290
292
293 QLineF gutterCenterLine = QLineF(m_startPoint, m_endPoint);
294 gutterCenterLine = converter.documentToView().map(gutterCenterLine);
295 QLineF gutterWidthHelperLine = QLineF(QPointF(0, 0), QPointF(gutterWidth, 0));
296 gutterWidthHelperLine = converter.documentToView().map(gutterWidthHelperLine);
297
298 gutterWidth = gutterWidthHelperLine.length();
299
300 QList<QLineF> gutterLines = KisAlgebra2D::getParallelLines(gutterCenterLine, gutterWidth/2);
301
302 QLineF gutterLine1 = gutterLines.length() > 0 ? gutterLines[0] : gutterCenterLine;
303 QLineF gutterLine2 = gutterLines.length() > 1 ? gutterLines[1] : gutterCenterLine;
304
305
306 painter.drawLine(gutterLine1);
307 painter.drawLine(gutterLine2);
308
309 QRectF arcRect1 = QRectF(gutterCenterLine.p1() - QPointF(gutterWidth/2, gutterWidth/2), gutterCenterLine.p1() + QPointF(gutterWidth/2, gutterWidth/2));
310 QRectF arcRect2 = QRectF(gutterCenterLine.p2() - QPointF(gutterWidth/2, gutterWidth/2), gutterCenterLine.p2() + QPointF(gutterWidth/2, gutterWidth/2));
311
312 int qtAngleFactor = 16;
313 int qtHalfCircle = qtAngleFactor*180;
314
315 painter.drawArc(arcRect1, -qtAngleFactor*kisRadiansToDegrees(KisAlgebra2D::directionBetweenPoints(gutterCenterLine.p1(), gutterLine1.p1(), 0)), qtHalfCircle);
316 painter.drawArc(arcRect2, -qtAngleFactor*kisRadiansToDegrees(KisAlgebra2D::directionBetweenPoints(gutterCenterLine.p2(), gutterLine1.p2(), 0)), -qtHalfCircle);
317
318
319 int xLength = 3;
320 qreal xLengthEllipse = 2*qSqrt(2);
321
322 if (false) { // drawing X
323 painter.drawLine({QLineF(gutterCenterLine.p1() - QPointF(xLength, xLength), gutterCenterLine.p1() + QPointF(xLength, xLength))});
324 painter.drawLine({QLineF(gutterCenterLine.p2() - QPointF(xLength, xLength), gutterCenterLine.p2() + QPointF(xLength, xLength))});
325
326 painter.drawLine({QLineF(gutterCenterLine.p1() - QPointF(xLength, -xLength), gutterCenterLine.p1() + QPointF(xLength, -xLength))});
327 painter.drawLine({QLineF(gutterCenterLine.p2() - QPointF(xLength, -xLength), gutterCenterLine.p2() + QPointF(xLength, -xLength))});
328 }
329
330 // ellipse at the both ends of the gutter center line
331 painter.drawEllipse(gutterCenterLine.p1(), xLengthEllipse, xLengthEllipse);
332 painter.drawEllipse(gutterCenterLine.p2(), xLengthEllipse, xLengthEllipse);
333
334
335
336 pen.setWidth(1);
337 semitransparentGray.setAlphaF(0.2);
338 pen.setColor(semitransparentGray);
339
340 painter.setPen(pen);
341
342 painter.drawLine(gutterCenterLine);
343
344 painter.restore();
345}
virtual QPointF documentToView(const QPointF &documentPoint) const
qreal directionBetweenPoints(const QPointF &p1, const QPointF &p2, qreal defaultAngle)

References calculateLineAngle(), KisAlgebra2D::directionBetweenPoints(), KoViewConverter::documentToView(), KisAlgebra2D::getParallelLines(), gutterWidthInDocumentCoordinates(), kisRadiansToDegrees(), m_endPoint, and m_startPoint.

Member Data Documentation

◆ m_allShapes

QList<KoShape *> CutThroughShapeStrategy::m_allShapes
private

Definition at line 49 of file CutThroughShapeStrategy.h.

◆ m_endPoint

QPointF CutThroughShapeStrategy::m_endPoint = QPointF()
private

Definition at line 46 of file CutThroughShapeStrategy.h.

◆ m_previousLineDirtyRect

QRectF CutThroughShapeStrategy::m_previousLineDirtyRect = QRectF()
private

Definition at line 47 of file CutThroughShapeStrategy.h.

◆ m_selectedShapes

QList<KoShape *> CutThroughShapeStrategy::m_selectedShapes
private

Definition at line 48 of file CutThroughShapeStrategy.h.

◆ m_startPoint

QPointF CutThroughShapeStrategy::m_startPoint = QPointF()
private

Definition at line 45 of file CutThroughShapeStrategy.h.

◆ m_width

GutterWidthsConfig CutThroughShapeStrategy::m_width
private

Definition at line 50 of file CutThroughShapeStrategy.h.


The documentation for this class was generated from the following files: