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

#include <RemoveGutterStrategy.h>

+ Inheritance diagram for RemoveGutterStrategy:

Public Member Functions

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

Private Attributes

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

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 18 of file RemoveGutterStrategy.h.

Constructor & Destructor Documentation

◆ RemoveGutterStrategy()

RemoveGutterStrategy::RemoveGutterStrategy ( KoToolBase * tool,
KoSelection * selection,
const QList< KoShape * > & shapes,
QPointF startPoint )

Definition at line 30 of file RemoveGutterStrategy.cpp.

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

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

◆ ~RemoveGutterStrategy()

RemoveGutterStrategy::~RemoveGutterStrategy ( )
override

Definition at line 39 of file RemoveGutterStrategy.cpp.

40{
41
42}

Member Function Documentation

◆ createCommand()

KUndo2Command * RemoveGutterStrategy::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 RemoveGutterStrategy.cpp.

45{
46 return 0;
47}

◆ finishInteraction()

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

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

Implements KoInteractionStrategy.

Definition at line 97 of file RemoveGutterStrategy.cpp.

98{
100
101
102 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(tool()->canvas());
104 const QTransform booleanWorkaroundTransform = KritaUtils::pathShapeBooleanSpaceWorkaround(kisCanvas->image());
105
106 QList<QPainterPath> srcOutlines;
107 QList<QPainterPath> srcOutlinesOutside;
108
109 if (m_allShapes.length() == 0) {
110 qCritical() << "No shapes on the layer";
111 return;
112 }
113
114 QList<bool> isSelected = QList<bool>();
115 isSelected.reserve(m_allShapes.length());
116 for (int i = 0; i < m_allShapes.length(); i++) {
117 isSelected.append(m_selectedShapes.contains(m_allShapes[i]));
118 }
119
120 QLineF mouseLine = QLineF(m_startPoint, m_endPoint);
122
123 mouseLine = booleanWorkaroundTransform.map(mouseLine);
124 lineRect = KisAlgebra2D::createRectFromCorners(mouseLine);
125
126#ifdef KNIFE_DEBUG
127 convertShapeToDebugArray(mouseLine);
128 convertShapeToDebugArray(lineRect);
129#endif
130
131
132 QList<int> indexes;
133 QList<int> indexesOutside;
134
135 for (int i = 0; i < m_allShapes.length(); i++) {
136 KoShape* shape = m_allShapes[i];
137 QPainterPath outlineHere =
138 booleanWorkaroundTransform.map(
139 shape->absoluteTransformation().map(
140 shape->outline()));
141#ifdef KNIFE_DEBUG
142 convertShapeToDebugArray(outlineHere);
143#endif
144 if (outlineHere.boundingRect().intersects(lineRect)) {
145 srcOutlines << outlineHere;
146 indexes << i;
147 //outlineRect |= outlineHere.boundingRect();
148 } else {
149 srcOutlinesOutside << outlineHere;
150 indexesOutside << i;
151 }
152 }
153
154 if (srcOutlines.isEmpty()) {
155 return;
156 }
157
158
159 QList<int> shapeNewIndexes;
160 QList<int> shapeOrigIndexes;
161 QList<int> lineSegmentIndexes;
162
163
164 for (int i = 0; i < srcOutlines.length(); i++) {
165 QList<int> lineIndexes = KisAlgebra2D::getLineSegmentCrossingLineIndexes(mouseLine, srcOutlines[i]);
166 int shapeOrigIndex = indexes[i];
167
168 if (lineIndexes.length() > 0) {
169 Q_FOREACH(int lineIndex, lineIndexes) {
170 shapeNewIndexes << i;
171 lineSegmentIndexes << lineIndex;
172 shapeOrigIndexes << shapeOrigIndex;
173 }
174 } else {
175 srcOutlinesOutside << srcOutlines[i];
176 indexesOutside << shapeOrigIndex;
177 }
178 }
179
180 if (shapeNewIndexes.length() != 2) {
181#ifdef KNIFE_DEBUG
182 qCritical() << "Shape indexes count isn't correct: " << ppVar(shapeNewIndexes.length()) << ppVar(lineSegmentIndexes.length());
183 qCritical() << "Mouse line in used coordinates: " << mouseLine;
184 qCritical() << "Number of shapes even considered: " << srcOutlines.length();
185 Q_FOREACH(QPainterPath path, srcOutlines) {
186 qCritical() << "> A path: ";
187 convertShapeToDebugArray(path);
188 }
189 qCritical() << "That's the end of shapes considered.";
190 qCritical() << "Shapes not considered: " << srcOutlinesOutside.length();
191 Q_FOREACH(QPainterPath path, srcOutlinesOutside) {
192 qCritical() << "> A path: ";
193 convertShapeToDebugArray(path);
194 }
195 qCritical() << "That's the end of shapes not considered.";
196 for(int i = 0; i < shapeNewIndexes.length(); i++) {
197 int index = shapeNewIndexes[i];
198 qCritical() << "Shape: ";
199 convertShapeToDebugArray(srcOutlines[index]);
200 qCritical() << "Line index: " << lineSegmentIndexes[i] << " meaning it's: " << srcOutlines[index].elementAt(KisAlgebra2D::wrapValue(lineSegmentIndexes[i], 0, srcOutlines[index].elementCount()))
201 << srcOutlines[index].elementAt(KisAlgebra2D::wrapValue(lineSegmentIndexes[i] + 1, 0, srcOutlines[index].elementCount()));
202 }
203 // TODO: this if should end here; code below adds a debug line showing the mouse line
204
205 QPainterPath newLineShape = QPainterPath();
206 newLineShape.moveTo(mouseLine.p1());
207 newLineShape.lineTo(mouseLine.p2());
208
209 newLineShape = booleanWorkaroundTransform.inverted().map(newLineShape);
210 KoPathShape* newLineShapeToAdd = KoPathShape::createShapeFromPainterPath(newLineShape);
211
212 newLineShapeToAdd->setBackground(m_allShapes[0]->background());
213 newLineShapeToAdd->setStroke(m_allShapes[0]->stroke());
214 newLineShapeToAdd->setZIndex(m_allShapes[0]->zIndex() + 100);
215
216
217
218 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Knife tool: cut through shapes"));
219 tool()->canvas()->shapeController()->addShapeDirect(newLineShapeToAdd, m_allShapes[0]->parent(), cmd);
220 tool()->canvas()->addCommand(cmd);
221
222#endif
223
224 return;
225 }
226
227
228#ifdef KNIFE_DEBUG
229 qCritical() << "Found two shapes.";
230 qCritical() << "Shape 1:";
231 convertShapeToDebugArray(srcOutlines[shapeNewIndexes[0]]);
232 qCritical() << ppVar(srcOutlines[shapeNewIndexes[0]].toFillPolygon());
233
234 qCritical() << "Shape 2:";
235 convertShapeToDebugArray(srcOutlines[shapeNewIndexes[1]]);
236 qCritical() << ppVar(srcOutlines[shapeNewIndexes[1]].toFillPolygon());
237#endif
238
239 if (shapeNewIndexes[0] == shapeNewIndexes[1]) {
240 // the same shape
241 // gotta ensure the mouseline starts and ends inside
242 bool insideP1 = KisAlgebra2D::isInsideShape(srcOutlines[shapeNewIndexes[0]], mouseLine.p1());
243 bool insideP2 = KisAlgebra2D::isInsideShape(srcOutlines[shapeNewIndexes[0]], mouseLine.p2());
244 if (!insideP1 || !insideP2) {
245 // it's from outside, it doesn't go over a gutter, then
246 return;
247 }
248 }
249
250
251
252 QPainterPath result = KisAlgebra2D::removeGutterSmart(srcOutlines[shapeNewIndexes[0]], lineSegmentIndexes[0], srcOutlines[shapeNewIndexes[1]], lineSegmentIndexes[1], shapeNewIndexes[0]==shapeNewIndexes[1]);
253
254#ifdef KNIFE_DEBUG
255 qCritical() << "Finally got a result:";
256 convertShapeToDebugArray(result);
257 qCritical() << ppVar(result.toFillPolygon());
258#endif
259
260 QList<KoShape*> resultSelectedShapes;
261
262 Q_FOREACH(int index, indexesOutside) {
263 if (isSelected[index]) {
264 resultSelectedShapes << m_allShapes[index];
265 }
266 }
267
268 // since we can't really decide which style to use, we're gonna use the style of the first found shape.
269 // if the user doesn't like it, they can change it.
270
271
272 result = booleanWorkaroundTransform.inverted().map(result);
274
275 if (resultShape->boundingRect().isEmpty()) {
276 return;
277 }
278
279 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Knife tool: remove a gutter"));
280
281
282
284
285
286 KoShape* referenceShape = m_allShapes[shapeOrigIndexes[0]];
287 KoPathShape* koPathReferenceShape = dynamic_cast<KoPathShape*>(referenceShape);
288 resultShape->setBackground(referenceShape->background());
289 resultShape->setStroke(referenceShape->stroke());
290 resultShape->setZIndex(referenceShape->zIndex());
291 if (koPathReferenceShape) {
292 resultShape->setFillRule(koPathReferenceShape->fillRule());
293 }
294
295
296 KoShapeContainer *parent = referenceShape->parent();
297 tool()->canvas()->shapeController()->addShapeDirect(resultShape, parent, cmd);
298
299 if (isSelected[shapeOrigIndexes[0]] || isSelected[shapeOrigIndexes[1]]) { // if either is selected
300 resultSelectedShapes << resultShape;
301 }
302
303 QList<KoShape*> shapesToRemove;
304 shapesToRemove << m_allShapes[shapeOrigIndexes[0]];
305 if (shapeOrigIndexes[0] != shapeOrigIndexes[1]) { // there is a good reason in the workflow to allow doing this operation on the same shape
306 shapesToRemove << m_allShapes[shapeOrigIndexes[1]];
307 }
308
309
310 tool()->canvas()->shapeController()->removeShapes(shapesToRemove, cmd);
311 new KoKeepShapesSelectedCommand({}, resultSelectedShapes, tool()->canvas()->selectedShapesProxy(), true, cmd);
312 tool()->canvas()->addCommand(cmd);
313
314
315}
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
Qt::FillRule fillRule() const
Returns the fill rule for the path object.
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule to be used for painting the background.
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 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
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define ppVar(var)
Definition kis_debug.h:155
KUndo2MagicString kundo2_i18n(const char *text)
T wrapValue(T value, T wrapBounds)
QPainterPath removeGutterSmart(const QPainterPath &shape1, int index1, const QPainterPath &shape2, int index2, bool isSameShape)
removeGutterSmart
bool isInsideShape(const VectorPath &path, const QPointF &point)
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(), KoPathShape::boundingRect(), KoToolBase::canvas(), KisAlgebra2D::createRectFromCorners(), KoPathShape::createShapeFromPainterPath(), KoPathShape::fillRule(), KisAlgebra2D::getLineSegmentCrossingLineIndexes(), KisCanvas2::image(), KisAlgebra2D::isInsideShape(), KIS_SAFE_ASSERT_RECOVER_RETURN, kundo2_i18n(), m_allShapes, m_endPoint, m_previousLineDirtyRect, m_selectedShapes, m_startPoint, KoShape::outline(), KritaUtils::pathShapeBooleanSpaceWorkaround(), ppVar, KisAlgebra2D::removeGutterSmart(), KoCanvasBase::selectedShapesProxy(), KoShape::setBackground(), KoPathShape::setFillRule(), KoShape::setStroke(), KoShape::setZIndex(), KoCanvasBase::shapeController, KoInteractionStrategy::tool(), KoCanvasBase::updateCanvas(), and KisAlgebra2D::wrapValue().

◆ handleMouseMove()

void RemoveGutterStrategy::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 49 of file RemoveGutterStrategy.cpp.

50{
51 m_endPoint = mouseLocation;
52 QRectF dirtyRect;
55 QLineF l = QLine(QPoint(), QPoint(50, 50));
56
57 l = tool()->canvas()->viewConverter()->viewToDocument().map(l);
58 dirtyRect = kisGrowRect(dirtyRect, l.length()); // twice as much as it should need to account for lines showing the effect
59
60 QRectF accumulatedWithPrevious = m_previousLineDirtyRect | dirtyRect;
61
62 tool()->canvas()->updateCanvas(accumulatedWithPrevious);
63 m_previousLineDirtyRect = dirtyRect;
64}
virtual const KoViewConverter * viewConverter() const =0
virtual QPointF viewToDocument(const QPointF &viewPoint) const
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
void accumulateBounds(const Point &pt, Rect *bounds)

References KisAlgebra2D::accumulateBounds(), KoToolBase::canvas(), kisGrowRect(), m_endPoint, m_previousLineDirtyRect, m_startPoint, KoInteractionStrategy::tool(), KoCanvasBase::updateCanvas(), KoCanvasBase::viewConverter(), and KoViewConverter::viewToDocument().

◆ paint()

void RemoveGutterStrategy::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 317 of file RemoveGutterStrategy.cpp.

318{
319 painter.save();
320 painter.setPen(QPen(QBrush(Qt::darkGray), 2));
321
322 QLineF line = converter.documentToView().map(QLineF(m_startPoint, m_endPoint));
323 if (line.length() > 0) {
324 QPointF vector = line.p2() - line.p1();
325 vector = vector/line.length();
326 int arrowLength = 15;
327 int arrowThickness = 5;
328
329 QPointF before = line.p1() - vector*arrowLength;
330 QPointF after = line.p2() + vector*arrowLength;
331
332 QPointF perpendicular = QPointF(vector.y(), -vector.x());
333
334 painter.drawLine(QPointF(before + arrowThickness*perpendicular), line.p1());
335 painter.drawLine(QPointF(before - arrowThickness*perpendicular), line.p1());
336
337 painter.drawLine(QPointF(after + arrowThickness*perpendicular), line.p2());
338 painter.drawLine(QPointF(after - arrowThickness*perpendicular), line.p2());
339
340
341 }
342 painter.drawLine(line);
343
344 painter.restore();
345
346}
virtual QPointF documentToView(const QPointF &documentPoint) const

References KoViewConverter::documentToView(), m_endPoint, and m_startPoint.

Member Data Documentation

◆ m_allShapes

QList<KoShape *> RemoveGutterStrategy::m_allShapes
private

Definition at line 35 of file RemoveGutterStrategy.h.

◆ m_endPoint

QPointF RemoveGutterStrategy::m_endPoint = QPointF()
private

Definition at line 32 of file RemoveGutterStrategy.h.

◆ m_previousLineDirtyRect

QRectF RemoveGutterStrategy::m_previousLineDirtyRect = QRectF()
private

Definition at line 33 of file RemoveGutterStrategy.h.

◆ m_selectedShapes

QList<KoShape *> RemoveGutterStrategy::m_selectedShapes
private

Definition at line 36 of file RemoveGutterStrategy.h.

◆ m_startPoint

QPointF RemoveGutterStrategy::m_startPoint = QPointF()
private

Definition at line 31 of file RemoveGutterStrategy.h.


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