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 30 of file CutThroughShapeStrategy.cpp.

32 , m_startPoint(startPoint)
33 , m_endPoint(startPoint)
34 , m_width(width)
35{
37 m_allShapes = shapes;
38}
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 40 of file CutThroughShapeStrategy.cpp.

41{
42
43}

Member Function Documentation

◆ calculateLineAngle()

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

Definition at line 366 of file CutThroughShapeStrategy.cpp.

367{
368 QPointF vec = end - start;
369 qreal angleDegrees = KisAlgebra2D::wrapValue(kisRadiansToDegrees(std::atan2(vec.y(), vec.x())), 0.0, 360.0);
370 return angleDegrees;
371}
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 45 of file CutThroughShapeStrategy.cpp.

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

◆ 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 88 of file CutThroughShapeStrategy.cpp.

89{
91
92
93 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(tool()->canvas());
95 const QTransform booleanWorkaroundTransform = KritaUtils::pathShapeBooleanSpaceWorkaround(kisCanvas->image());
96
97 QList<QPainterPath> srcOutlines;
98 QRectF outlineRect;
99
100 if (m_allShapes.length() == 0) {
101 qCritical() << "No shapes are available";
102 return;
103 }
104
105 Q_FOREACH (KoShape *shape, m_allShapes) {
106
107 QPainterPath outlineHere =
108 booleanWorkaroundTransform.map(
109 shape->absoluteTransformation().map(
110 shape->outline()));
111
112 srcOutlines << outlineHere;
113 outlineRect |= outlineHere.boundingRect();//booleanWorkaroundTransform.map(shape->absoluteOutlineRect()).boundingRect();
114 }
115
116 if (outlineRect.isEmpty()) {
117 //qCritical() << "The outline rect is empty";
118 return;
119 }
120
121 QRectF outlineRectBigger = kisGrowRect(outlineRect, 10);
122 QRect outlineRectBiggerInt = outlineRectBigger.toRect();
123
124 QLineF gapLine = QLineF(m_startPoint, m_endPoint);
125 qreal eps = 0.0000001;
126 if (gapLine.length() < eps) {
127 return;
128 }
129
131
132 QList<QLineF> gapLines = KisAlgebra2D::getParallelLines(gapLine, gutterWidth/2);
133
134 gapLine = booleanWorkaroundTransform.map(gapLine);
135 gapLines[0] = booleanWorkaroundTransform.map(gapLines[0]);
136 gapLines[1] = booleanWorkaroundTransform.map(gapLines[1]);
137
138 QLineF leftLine = gapLines[0];
139 QLineF rightLine = gapLines[1];
140
141 QLineF leftLineLong = leftLine;
142 QLineF rightLineLong = rightLine;
143
144
145
146 KisAlgebra2D::cropLineToRect(leftLineLong, outlineRectBiggerInt, true, true);
147 KisAlgebra2D::cropLineToRect(rightLineLong, outlineRectBiggerInt, true, true);
148
149 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Knife tool: cut through shapes"));
150
151
152 new KoKeepShapesSelectedCommand(m_selectedShapes, {}, kisCanvas->selectedShapesProxy(), false, cmd);
153
154
155 if (leftLine.length() == 0 || rightLine.length() == 0) {
156 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");
157 // looks like *all* shapes need to be cut out
158
159 tool()->canvas()->shapeController()->removeShapes(m_allShapes, cmd);
160 tool()->canvas()->addCommand(cmd);
161 return;
162 }
163
164
165 QList<QPainterPath> paths = KisAlgebra2D::getPathsFromRectangleCutThrough(QRectF(outlineRectBiggerInt), leftLineLong, rightLineLong);
166 QPainterPath left = paths[0];
167 QPainterPath right = paths[1];
168
169 QList<QPainterPath> pathsOpposite = KisAlgebra2D::getPathsFromRectangleCutThrough(QRectF(outlineRectBiggerInt), rightLineLong, leftLineLong);
170 QPainterPath leftOpposite = pathsOpposite[0];
171 QPainterPath rightOpposite = pathsOpposite[1];
172
173 QList<KoShape*> newSelectedShapes;
174
175 QList<KoShape*> shapesToRemove;
176
177 QTransform booleanWorkaroundTransformInverted = booleanWorkaroundTransform.inverted();
178
179 QRectF gapLineLeftRect = KisAlgebra2D::createRectFromCorners(leftLine);
180 QRectF gapLineRightRect = KisAlgebra2D::createRectFromCorners(rightLine);
181
182
183 for (int i = 0; i < srcOutlines.size(); i++) {
184
185 KoShape* referenceShape = m_allShapes[i];
186 bool wasSelected = m_selectedShapes.contains(referenceShape);
187
188
189 if (dynamic_cast<KoSvgTextShape*>(referenceShape)) {
190 // skip all text
191 if (wasSelected) {
192 newSelectedShapes << referenceShape;
193 }
194 continue;
195 }
196
197 if ((srcOutlines[i].boundingRect() & leftOpposite.boundingRect()).isEmpty()
198 || (srcOutlines[i].boundingRect() & rightOpposite.boundingRect()).isEmpty()) {
199 // there is nothing on one side
200 // everything is on the other, far away from the gap line
201 // it just makes it a bit faster when there is a whole lot of shapes
202
203 if (wasSelected) {
204 newSelectedShapes << referenceShape;
205 }
206 continue;
207 }
208
209 if ((srcOutlines[i].boundingRect() & gapLineLeftRect).isEmpty()
210 || (srcOutlines[i].boundingRect() & gapLineRightRect).isEmpty()) {
211 // the gap lines can't cross the shape since their bounding rects don't cross
212 if (wasSelected) {
213 newSelectedShapes << referenceShape;
214 }
215 continue;
216 }
217
218 // either one of the points is inside the path, or the line crosses one of the segments
219 bool containsGapLinePoint = srcOutlines[i].contains(leftLine.p1()) || srcOutlines[i].contains(leftLine.p2())
220 || srcOutlines[i].contains(rightLine.p1()) || srcOutlines[i].contains(rightLine.p2());
221 bool crossesGapLine = KisAlgebra2D::getLineSegmentCrossingLineIndexes(leftLine, srcOutlines[i]).count() > 0
222 || KisAlgebra2D::getLineSegmentCrossingLineIndexes(rightLine, srcOutlines[i]).count() > 0;
223 if (!containsGapLinePoint && !crossesGapLine) {
224
225 //qCritical() << "it doesn't cross the line!";
226 if (wasSelected) {
227 newSelectedShapes << referenceShape;
228 }
229 continue;
230 }
231
232
233
234
235 QPainterPath leftPath = srcOutlines[i] & left;
236 QPainterPath rightPath = srcOutlines[i] & right;
237
238 QList<QPainterPath> bothSides;
239 bothSides << leftPath << rightPath;
240
241
242 Q_FOREACH(QPainterPath path, bothSides) {
243 if (path.isEmpty()) {
244 continue;
245 }
246
247 // comment copied from another place:
248 // there is a bug in Qt, sometimes it leaves the resulting
249 // outline open, so just close it explicitly.
250 path.closeSubpath();
251 // this is needed because Qt linearize curves; this allows for a
252 // "sane" linearization instead of a very blocky appearance
253 path = booleanWorkaroundTransformInverted.map(path);
255
256 if (shape->boundingRect().isEmpty()) {
257 continue;
258 }
259
260 shape->setBackground(referenceShape->background());
261 shape->setStroke(referenceShape->stroke());
262 shape->setZIndex(referenceShape->zIndex());
263
264 KoShapeContainer *parent = referenceShape->parent();
265 tool()->canvas()->shapeController()->addShapeDirect(shape, parent, cmd);
266
267 if (wasSelected) {
268 newSelectedShapes << shape;
269 }
270
271
272 }
273
274 // that happens no matter if there was any non-empty shape
275 // because if there is none, maybe they just were underneath the gap
276 shapesToRemove << m_allShapes[i];
277
278 }
279
280 tool()->canvas()->shapeController()->removeShapes(shapesToRemove, cmd);
281 new KoKeepShapesSelectedCommand({}, newSelectedShapes, tool()->canvas()->selectedShapesProxy(), true, cmd);
282
283
284 tool()->canvas()->addCommand(cmd);
285
286
287
288}
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 357 of file CutThroughShapeStrategy.cpp.

358{
359 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(tool()->canvas());
361 QLineF helperGapWidthLine = QLineF(QPointF(0, 0), QPointF(0, m_width.widthForAngleInPixels(lineAngle)));
362 QLineF helperGapWidthLineTransformed = kisCanvas->coordinatesConverter()->imageToDocument(helperGapWidthLine);
363 return helperGapWidthLineTransformed.length();
364}
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 73 of file CutThroughShapeStrategy.cpp.

74{
75 m_endPoint = snapEndPoint(m_startPoint, mouseLocation, modifiers);
76 QRectF dirtyRect;
79 dirtyRect = kisGrowRect(dirtyRect, gutterWidthInDocumentCoordinates(calculateLineAngle(m_startPoint, m_endPoint))); // twice as much as it should need to account for lines showing the effect
80
81 QRectF accumulatedWithPrevious = m_previousLineDirtyRect | dirtyRect;
82
83 tool()->canvas()->updateCanvas(accumulatedWithPrevious);
84 m_previousLineDirtyRect = dirtyRect;
85
86}
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 290 of file CutThroughShapeStrategy.cpp.

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