Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_transform_utils.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <cmath>
10#include <QPainterPath>
11#include <QTransform>
12#include <QVector3D>
13#include <KoUnit.h>
14#include "tool_transform_args.h"
15#include "kis_paint_device.h"
16#include "kis_algebra_2d.h"
18#include "kis_painter.h"
19
25
27#include "kis_transform_mask.h"
30#include "kis_selection.h"
31#include "kis_image.h"
33
38
39
41{
42 return converter->imageToDocumentTransform() * converter->documentToFlakeTransform();
43}
44
46{
47 QPointF handleRadiusPt = flakeToImage(converter, QPointF(handleRadius, handleRadius));
48 return (handleRadiusPt.x() > handleRadiusPt.y()) ? handleRadiusPt.x() : handleRadiusPt.y();
49}
50
52{
53 QPointF handleRadiusPt = flakeToImage(converter, QPointF(rotationHandleRadius, rotationHandleRadius));
54 return (handleRadiusPt.x() > handleRadiusPt.y()) ? handleRadiusPt.x() : handleRadiusPt.y();
55}
56
57qreal KisTransformUtils::scaleFromAffineMatrix(const QTransform &t) {
59}
60
61qreal KisTransformUtils::scaleFromPerspectiveMatrixX(const QTransform &t, const QPointF &basePt) {
62 const QPointF pt = basePt + QPointF(1.0, 0);
63 return kisDistance(t.map(pt), t.map(basePt));
64}
65
66qreal KisTransformUtils::scaleFromPerspectiveMatrixY(const QTransform &t, const QPointF &basePt) {
67 const QPointF pt = basePt + QPointF(0, 1.0);
68 return kisDistance(t.map(pt), t.map(basePt));
69}
70
71qreal KisTransformUtils::effectiveSize(const QRectF &rc) {
72 return 0.5 * (rc.width() + rc.height());
73}
74
75bool KisTransformUtils::thumbnailTooSmall(const QTransform &resultThumbTransform, const QRect &originalImageRect)
76{
77 return KisAlgebra2D::minDimension(resultThumbTransform.mapRect(originalImageRect)) < 32;
78}
79
80QRectF handleRectImpl(qreal radius, const QTransform &t, const QRectF &limitingRect, const QPointF &basePoint, qreal *dOutX, qreal *dOutY) {
81 const qreal handlesExtraScaleX =
83 const qreal handlesExtraScaleY =
85
86 const qreal maxD = 0.2 * KisTransformUtils::effectiveSize(limitingRect);
87 const qreal dX = qMin(maxD, radius / handlesExtraScaleX);
88 const qreal dY = qMin(maxD, radius / handlesExtraScaleY);
89
90 QRectF handleRect(-0.5 * dX, -0.5 * dY, dX, dY);
91
92 if (dOutX) {
93 *dOutX = dX;
94 }
95
96 if (dOutY) {
97 *dOutY = dY;
98 }
99
100 return handleRect;
101
102}
103
104QRectF KisTransformUtils::handleRect(qreal radius, const QTransform &t, const QRectF &limitingRect, qreal *dOutX, qreal *dOutY) {
105 return handleRectImpl(radius, t, limitingRect, limitingRect.center(), dOutX, dOutY);
106}
107
108QRectF KisTransformUtils::handleRect(qreal radius, const QTransform &t, const QRectF &limitingRect, const QPointF &basePoint) {
109 return handleRectImpl(radius, t, limitingRect, basePoint, 0, 0);
110}
111
112QPointF KisTransformUtils::clipInRect(QPointF p, QRectF r)
113{
114 QPointF center = r.center();
115 QPointF t = p - center;
116 r.translate(- center);
117
118 if (t.y() != 0) {
119 if (t.x() != 0) {
120 double slope = t.y() / t.x();
121
122 if (t.x() < r.left()) {
123 t.setY(r.left() * slope);
124 t.setX(r.left());
125 }
126 else if (t.x() > r.right()) {
127 t.setY(r.right() * slope);
128 t.setX(r.right());
129 }
130
131 if (t.y() < r.top()) {
132 t.setX(r.top() / slope);
133 t.setY(r.top());
134 }
135 else if (t.y() > r.bottom()) {
136 t.setX(r.bottom() / slope);
137 t.setY(r.bottom());
138 }
139 }
140 else {
141 if (t.y() < r.top())
142 t.setY(r.top());
143 else if (t.y() > r.bottom())
144 t.setY(r.bottom());
145 }
146 }
147 else {
148 if (t.x() < r.left())
149 t.setX(r.left());
150 else if (t.x() > r.right())
151 t.setX(r.right());
152 }
153
154 t += center;
155
156 return t;
157}
158
160{
161 TS = QTransform::fromTranslate(-args.originalCenter().x(), -args.originalCenter().y());
162 SC = QTransform::fromScale(args.scaleX(), args.scaleY());
163 S.shear(0, args.shearY()); S.shear(args.shearX(), 0);
164
166 BRI.rotate(180. * -args.boundsRotation() / M_PI);
167 P.rotate(180. * normalizeAngle(args.aX()) / M_PI, QVector3D(1, 0, 0));
168 P.rotate(180. * normalizeAngle(args.aY()) / M_PI, QVector3D(0, 1, 0));
169 P.rotate(180. * normalizeAngle(args.aZ()) / M_PI, QVector3D(0, 0, 1));
170 projectedP = P.toTransform(args.cameraPos().z());
171 } else if (args.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
172 // see a comment in KisPerspectiveTransformStrategy::Private::transformIntoArgs()
173#if 0
174 projectedP.rotate(kisRadiansToDegrees(args.aZ()));
176#else
178#endif
179 P = QMatrix4x4(projectedP);
180 }
181
182 QPointF translation = args.transformedCenter();
183 T = QTransform::fromTranslate(translation.x(), translation.y());
184}
185
187{
188 return TS * BRI * SC * S * projectedP * T;
189}
190
191bool KisTransformUtils::checkImageTooBig(const QRectF &bounds, const MatricesPack &m, qreal cameraHeight)
192{
193 bool imageTooBig = false;
194
195 QMatrix4x4 unprojectedMatrix = QMatrix4x4(m.T) * m.P * QMatrix4x4(m.TS * m.SC * m.S);
196 QVector<QPointF> points;
197 points << bounds.topLeft();
198 points << bounds.topRight();
199 points << bounds.bottomRight();
200 points << bounds.bottomLeft();
201
202 Q_FOREACH (const QPointF &pt, points) {
203 QVector4D v(pt.x(), pt.y(), 0, 1);
204
205 v = unprojectedMatrix * v;
206 qreal z = v.z() / v.w();
207
208 imageTooBig = z > 1.5 * cameraHeight;
209
210 if (imageTooBig) {
211 break;
212 }
213 }
214
215 return imageTooBig;
216}
217
219 KisPaintDeviceSP device,
220 KoUpdaterPtr updater)
221{
222 double scaleX = config.scaleX();
223 double scaleY = config.scaleY();
224 double shearX = config.shearX();
225 double shearY = config.shearY();
226 double aZ = config.aZ();
227
228 if (config.boundsRotation() != 0.0) {
229 const KisTransformUtils::MatricesPack m(config);
230 QTransform Z; Z.rotateRadians(aZ);
231 QTransform desired = m.BRI * m.SC * m.S * Z;
233 if (dm.isValid()) {
234 scaleX = dm.scaleX;
235 scaleY = dm.scaleY;
236 shearX = dm.shearXY;
237 shearY = 0;
238 aZ = kisDegreesToRadians(dm.angle);
239 }
240 }
241
242 QPointF transformedCenter;
243 {
245 scaleX, scaleY,
246 shearX, shearY,
247 aZ,
248 0, // set X and Y translation
249 0, // to null for calculation
250 0,
251 config.filter());
252
253 transformedCenter = t.transform().map(config.originalCenter());
254 }
255
256 QPointF translation = config.transformedCenter() - transformedCenter;
257
258 KisTransformWorker transformWorker(device,
259 scaleX, scaleY,
260 shearX, shearY,
261 normalizeAngle(aZ),
262 translation.x(),
263 translation.y(),
264 updater,
265 config.filter());
266
267 // MatricesPack m(config);
268 // ENTER_FUNCTION() << ppVar(m.finalTransform() - transformWorker.transform());
269
270 return transformWorker;
271}
272
274 KisPaintDeviceSP device,
276{
277 KisPaintDeviceSP tmp = new KisPaintDevice(*device);
278 transformDevice(config, tmp, device, helper);
279}
280
281
282namespace {
283
284void transformDeviceImpl(const ToolTransformArgs &config,
285 KisPaintDeviceSP srcDevice,
286 KisPaintDeviceSP dstDevice,
288 bool cropDst,
289 bool forceSubPixelTranslation)
290{
291 if (config.mode() == ToolTransformArgs::WARP) {
292 KoUpdaterPtr updater = helper->updater();
293
294 KisWarpTransformWorker worker(config.warpType(),
295 config.origPoints(),
296 config.transfPoints(),
297 config.alpha(),
298 updater);
299 worker.run(srcDevice, dstDevice);
300 } else if (config.mode() == ToolTransformArgs::CAGE) {
301 KoUpdaterPtr updater = helper->updater();
302
303 dstDevice->makeCloneFromRough(srcDevice, srcDevice->extent());
304
305 KisCageTransformWorker worker(srcDevice->region().boundingRect(),
306 config.origPoints(),
307 updater,
308 config.pixelPrecision());
309
310 worker.prepareTransform();
311 worker.setTransformedCage(config.transfPoints());
312 worker.run(srcDevice, dstDevice);
313 } else if (config.mode() == ToolTransformArgs::LIQUIFY && config.liquifyWorker()) {
314 KoUpdaterPtr updater = helper->updater();
315 //FIXME:
316 Q_UNUSED(updater);
317
318 config.liquifyWorker()->run(srcDevice, dstDevice);
319 } else if (config.mode() == ToolTransformArgs::MESH) {
320 KoUpdaterPtr updater = helper->updater();
321 //FIXME:
322 Q_UNUSED(updater);
323
324 dstDevice->clear();
325 config.meshTransform()->transformMesh(srcDevice, dstDevice);
326
327 } else {
328 KoUpdaterPtr updater1 = helper->updater();
329 KoUpdaterPtr updater2 = helper->updater();
330
331 dstDevice->makeCloneFromRough(srcDevice, srcDevice->extent());
332
333 KisTransformWorker transformWorker =
334 KisTransformUtils::createTransformWorker(config, dstDevice, updater1);
335
336 transformWorker.setForceSubPixelTranslation(forceSubPixelTranslation);
337 transformWorker.run();
338
340 config.filterId() == "NearestNeighbor" ?
343
344 if (config.mode() == ToolTransformArgs::FREE_TRANSFORM) {
345 KisPerspectiveTransformWorker perspectiveWorker(dstDevice,
346 config.transformedCenter(),
347 config.aX(),
348 config.aY(),
349 config.cameraPos().z(),
350 cropDst,
351 updater2);
352 perspectiveWorker.setForceSubPixelTranslation(forceSubPixelTranslation);
353 perspectiveWorker.run(sampleType);
354 } else if (config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
355 QTransform T =
356 QTransform::fromTranslate(config.transformedCenter().x(),
357 config.transformedCenter().y());
358
359 KisPerspectiveTransformWorker perspectiveWorker(dstDevice,
360 T.inverted() * config.flattenedPerspectiveTransform() * T,
361 cropDst,
362 updater2);
363 perspectiveWorker.setForceSubPixelTranslation(forceSubPixelTranslation);
364 perspectiveWorker.run(sampleType);
365 }
366 }
367}
368
369}
370
372 KisPaintDeviceSP srcDevice,
373 KisPaintDeviceSP dstDevice,
375{
376 transformDeviceImpl(config, srcDevice, dstDevice, helper, false, false);
377}
378
380{
381 transformDeviceImpl(config, srcDevice, dstDevice, helper, true, forceSubPixelTranslation);
382}
383
385 const QRect &rc,
386 const QRect &srcBounds)
387{
388 QRect result = rc;
389
390 if (config.mode() == ToolTransformArgs::WARP) {
391 KisWarpTransformWorker worker(config.warpType(),
392 config.origPoints(),
393 config.transfPoints(),
394 config.alpha(),
395 0);
396
397 result = worker.approxNeedRect(rc, srcBounds);
398
399 } else if (config.mode() == ToolTransformArgs::CAGE) {
400 KisCageTransformWorker worker(srcBounds,
401 config.origPoints(),
402 0,
403 config.pixelPrecision());
404 worker.setTransformedCage(config.transfPoints());
405 result = worker.approxNeedRect(rc, srcBounds);
406 } else if (config.mode() == ToolTransformArgs::LIQUIFY) {
407 result = config.liquifyWorker() ?
408 config.liquifyWorker()->approxNeedRect(rc, srcBounds) : rc;
409 } else if (config.mode() == ToolTransformArgs::MESH) {
410 result = config.meshTransform()->approxNeedRect(rc);
411 } else {
412 KIS_ASSERT_RECOVER_NOOP(0 && "this works for non-affine transformations only!");
413 }
414
415 return result;
416}
417
419 const QRect &rc)
420{
421 QRect result = rc;
422
423 if (config.mode() == ToolTransformArgs::WARP) {
424 KisWarpTransformWorker worker(config.warpType(),
425 config.origPoints(),
426 config.transfPoints(),
427 config.alpha(),
428 0);
429
430 result = worker.approxChangeRect(rc);
431
432 } else if (config.mode() == ToolTransformArgs::CAGE) {
433 KisCageTransformWorker worker(rc,
434 config.origPoints(),
435 0,
436 config.pixelPrecision());
437
438 worker.setTransformedCage(config.transfPoints());
439 result = worker.approxChangeRect(rc);
440 } else if (config.mode() == ToolTransformArgs::LIQUIFY) {
441 result = config.liquifyWorker() ?
442 config.liquifyWorker()->approxChangeRect(rc) : rc;
443 } else if (config.mode() == ToolTransformArgs::MESH) {
444 result = config.meshTransform()->approxChangeRect(rc);
445
446 } else {
447 KIS_ASSERT_RECOVER_NOOP(0 && "this works for non-affine transformations only!");
448 }
449
450 return result;
451}
452
454 : m_enabled(enabled),
455 m_config(config)
456{
457 if (!m_enabled) return;
458
460
463}
464
466 if (!m_enabled) return;
467
468 const KisTransformUtils::MatricesPack m(*m_config);
469 const QPointF newStaticPointInView = m.finalTransform().map(m_staticPoint);
470
471 const QPointF diff = m_oldStaticPointInView - newStaticPointInView;
472
473 m_config->setTransformedCenter(m_config->transformedCenter() + diff);
474}
475
477 const TransformTransactionProperties *transaction,
478 ToolTransformArgs *config)
479{
480 static const int DEFAULT_POINTS_PER_LINE = 3;
481
482 if (pointsPerLine < 0) {
483 pointsPerLine = DEFAULT_POINTS_PER_LINE;
484 }
485
486 int nbPoints = pointsPerLine * pointsPerLine;
487 QVector<QPointF> origPoints(nbPoints);
488 QVector<QPointF> transfPoints(nbPoints);
489 qreal gridSpaceX, gridSpaceY;
490
491 if (nbPoints == 1) {
492 //there is actually no grid
493 origPoints[0] = transaction->originalCenterGeometric();
494 transfPoints[0] = transaction->originalCenterGeometric();
495 }
496 else if (nbPoints > 1) {
497 gridSpaceX = transaction->originalRect().width() / (pointsPerLine - 1);
498 gridSpaceY = transaction->originalRect().height() / (pointsPerLine - 1);
499 double y = transaction->originalRect().top();
500 for (int i = 0; i < pointsPerLine; ++i) {
501 double x = transaction->originalRect().left();
502 for (int j = 0 ; j < pointsPerLine; ++j) {
503 origPoints[i * pointsPerLine + j] = QPointF(x, y);
504 transfPoints[i * pointsPerLine + j] = QPointF(x, y);
505 x += gridSpaceX;
506 }
507 y += gridSpaceY;
508 }
509 }
510
511 config->setDefaultPoints(nbPoints > 0);
512 config->setPoints(origPoints, transfPoints);
513}
514
516 const QString &filterId,
517 const TransformTransactionProperties &transaction,
518 KisPaintDeviceSP externalSource)
519{
521
522 args.setOriginalCenter(transaction.originalCenterGeometric());
524 args.setFilterId(filterId);
525 args.setExternalSource(externalSource);
526
529 } else if (mode == ToolTransformArgs::WARP) {
531 KisTransformUtils::setDefaultWarpPoints(-1, &transaction, &args);
532 args.setEditingTransformPoints(false);
533 } else if (mode == ToolTransformArgs::CAGE) {
535 args.setEditingTransformPoints(true);
536 } else if (mode == ToolTransformArgs::LIQUIFY) {
538 const QRect srcRect = transaction.originalRect().toAlignedRect();
539 if (!srcRect.isEmpty()) {
540 args.initLiquifyTransformMode(srcRect);
541 }
542 } else if (mode == ToolTransformArgs::MESH) {
544 const QRect srcRect = transaction.originalRect().toAlignedRect();
545 if (!srcRect.isEmpty()) {
546 *args.meshTransform() = KisBezierTransformMesh(QRectF(srcRect));
547 }
548 } else if (mode == ToolTransformArgs::PERSPECTIVE_4POINT) {
550 }
551
552 return args;
553}
554
556{
557 bool hasExternalLayers = false;
558 Q_FOREACH (KisNodeSP node, processedNodes) {
559 if (node->inherits("KisShapeLayer")) {
560 hasExternalLayers = true;
561 break;
562 }
563 }
564
565 bool result = false;
566
567 if (hasExternalLayers) {
568 result =
571 }
572
573 return result;
574}
575
580{
581 KoUpdaterPtr mergeUpdater = helper->updater();
582
583 KisPaintDeviceSP tmp = new KisPaintDevice(src->colorSpace());
584 tmp->prepareClone(src);
585
586 KisTransformUtils::transformDevice(config, src, tmp, helper);
587
588 QRect mergeRect = tmp->extent();
589 KisPainter painter(dst);
590 painter.setProgress(mergeUpdater);
591 painter.bitBlt(mergeRect.topLeft(), tmp, mergeRect);
592 painter.end();
593}
594
606
607void KisTransformUtils::postProcessToplevelCommand(KUndo2Command *command, const ToolTransformArgs &args, KisNodeList rootNodes, KisNodeList processedNodes, int currentTime, const KisSavedMacroCommand *overriddenCommand)
608{
610 data->savedTransformArgs = args;
611 data->rootNodes = rootNodes;
612 data->transformedNodes = processedNodes;
613 data->transformedTime = currentTime;
614
615 command->setExtraData(data);
616
617 KisSavedMacroCommand *macroCommand = dynamic_cast<KisSavedMacroCommand*>(command);
618 KIS_SAFE_ASSERT_RECOVER_NOOP(macroCommand);
619
620 if (overriddenCommand && macroCommand) {
621 macroCommand->setOverrideInfo(overriddenCommand, {});
622 }
623}
624
625bool KisTransformUtils::fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeList *rootNodes, KisNodeList *transformedNodes, int *oldTime)
626{
627 const TransformExtraData *data = dynamic_cast<const TransformExtraData*>(command->extraData());
628
629 if (data) {
630 *args = data->savedTransformArgs;
631 *rootNodes = data->rootNodes;
632 *transformedNodes = data->transformedNodes;
633 *oldTime = data->transformedTime;
634 }
635
636 return bool(data);
637}
638
640{
641 // we search for masks only at the first level of hierarchy,
642 // all other masks are just ignored.
643
644 KisNodeSP node = root->firstChild();
645
646 while (node) {
647 if (node->inherits("KisTransformMask") && node->isEditable()) {
648 root = node;
649 break;
650 }
651
652 node = node->nextSibling();
653 }
654
655 return root;
656}
657
659{
660 Q_FOREACH(KisNodeSP node, rootNodes) {
665 if (node && node->image()) {
666 return node->image()->animationInterface()->currentTime();
667 }
668 }
669 return -1;
670}
671
673{
674 QList<KisNodeSP> result;
675
676 Q_FOREACH (KisNodeSP root, rootNodes) {
677 bool hasTransformMaskDescendant =
678 KisLayerUtils::recursiveFindNode(root, [root] (KisNodeSP node) {
679 return node != root && node->visible() && node->inherits("KisTransformMask");
680 });
681
686 KIS_SAFE_ASSERT_RECOVER_NOOP(!hasTransformMaskDescendant);
687
688 KisNodeSP selectionNode = selection ? selection->parentNode() : 0;
689
690 auto fetchFunc =
691 [&result, mode, root, selectionNode] (KisNodeSP node) {
692 if (node->isEditable(node == root) &&
693 (!node->inherits("KisShapeLayer") || mode == ToolTransformArgs::FREE_TRANSFORM) &&
694 !node->inherits("KisFileLayer") &&
695 !node->inherits("KisColorizeMask") &&
696 (!node->inherits("KisTransformMask") || node == root) &&
697 (!selectionNode || node != selectionNode)) {
698
699 result << node;
700 }
701 };
702
703 if (isExternalSourcePresent) {
704 fetchFunc(root);
705 } else {
706 KisLayerUtils::recursiveApplyNodes(root, fetchFunc);
707 }
708 }
709
711 return result;
712}
713
715{
716 bool result = false;
717
718 Q_FOREACH(KisNodeSP node, rootNodes) {
719 if (KisTransformMaskSP mask =
720 dynamic_cast<KisTransformMask*>(node.data())) {
721
723 mask->transformParams();
724
725 KisTransformMaskAdapter *adapter =
726 dynamic_cast<KisTransformMaskAdapter*>(savedParams.data());
727
728 if (adapter && adapter->isInitialized()) {
729 *args = *adapter->transformArgs();
730 result = true;
731 }
732 }
733 }
734
735 return result;
736}
737
740 KisNodeList currentNodes,
741 KisNodeList selectedNodes,
742 KisStrokeUndoFacade *undoFacade,
743 int currentTime,
745 const KisSavedMacroCommand **overriddenCommand)
746{
747 bool result = false;
748
749 const KUndo2Command *lastCommand = undoFacade->lastExecutedCommand();
750 KisNodeList oldRootNodes;
751 KisNodeList oldTransformedNodes;
752 int oldTime = -1;
753
755
756 if (lastCommand &&
757 KisTransformUtils::fetchArgsFromCommand(lastCommand, &args, &oldRootNodes, &oldTransformedNodes, &oldTime) &&
758 args.mode() == mode &&
759 oldRootNodes == currentNodes &&
760 oldTime == currentTime) {
761
762 if (KritaUtils::compareListsUnordered(oldTransformedNodes, selectedNodes)) {
763 args.saveContinuedState();
764
765 *outArgs = args;
766
767 const KisSavedMacroCommand *command = dynamic_cast<const KisSavedMacroCommand*>(lastCommand);
769
770 // the jobs are fetched as !shouldGoToHistory,
771 // so there is no need to put them into
772 // m_s->skippedWhileMergeCommands
773 command->getCommandExecutionJobs(undoJobs, true, false);
774 *overriddenCommand = command;
775
776 result = true;
777 }
778 }
779
780 return result;
781}
KisBezierTransformMeshDetail::KisBezierTransformMesh KisBezierTransformMesh
Eigen::Matrix< double, 4, 2 > S
const Params2D p
qreal v
KUndo2CommandExtraData * extraData() const
void setExtraData(KUndo2CommandExtraData *data)
void transformMesh(const QPoint &srcQImageOffset, const QImage &srcImage, const QPoint &dstQImageOffset, QImage *dstImage) const
KisImageAnimationInterface * animationInterface() const
virtual void clear()
KisRegion region() const
void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
QRect extent() const
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void setProgress(KoUpdater *progressUpdater)
QRect boundingRect() const
void getCommandExecutionJobs(QVector< KisStrokeJobData * > *jobs, bool undo, bool shouldGoToHistory=true) const
void setOverrideInfo(const KisSavedMacroCommand *overriddenCommand, const QVector< const KUndo2Command * > &skipWhileOverride)
virtual const KUndo2Command * lastExecutedCommand() const =0
virtual const QSharedPointer< ToolTransformArgs > transformArgs() const
static qreal effectiveHandleGrabRadius(const KisCoordinatesConverter *converter)
static void transformDevice(const ToolTransformArgs &config, KisPaintDeviceSP device, KisProcessingVisitor::ProgressHelper *helper)
static T flakeToImage(const KisCoordinatesConverter *converter, T object)
static qreal scaleFromPerspectiveMatrixY(const QTransform &t, const QPointF &basePt)
static QRect needRect(const ToolTransformArgs &config, const QRect &rc, const QRect &srcBounds)
static KisTransformWorker createTransformWorker(const ToolTransformArgs &config, KisPaintDeviceSP device, KoUpdaterPtr updater)
static qreal scaleFromPerspectiveMatrixX(const QTransform &t, const QPointF &basePt)
static qreal effectiveSize(const QRectF &rc)
static ToolTransformArgs resetArgsForMode(ToolTransformArgs::TransformMode mode, const QString &filterId, const TransformTransactionProperties &transaction, KisPaintDeviceSP externalSource)
static qreal effectiveRotationHandleGrabRadius(const KisCoordinatesConverter *converter)
static const int handleRadius
static const int rotationHandleRadius
static void setDefaultWarpPoints(int pointsPerLine, const TransformTransactionProperties *transaction, ToolTransformArgs *config)
static QRect changeRect(const ToolTransformArgs &config, const QRect &rc)
static qreal scaleFromAffineMatrix(const QTransform &t)
static QPointF clipInRect(QPointF p, QRectF r)
static int fetchCurrentImageTime(KisNodeList rootNodes)
static void postProcessToplevelCommand(KUndo2Command *command, const ToolTransformArgs &args, KisNodeList rootNodes, KisNodeList processedNodes, int currentTime, const KisSavedMacroCommand *overriddenCommand)
static QTransform imageToFlakeTransform(const KisCoordinatesConverter *converter)
static QList< KisNodeSP > fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeList rootNodes, bool isExternalSourcePresent, KisSelectionSP selection)
static KisNodeSP tryOverrideRootToTransformMask(KisNodeSP root)
static void transformAndMergeDevice(const ToolTransformArgs &config, KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper)
static const int handleVisualRadius
static bool shouldRestartStrokeOnModeChange(ToolTransformArgs::TransformMode oldMode, ToolTransformArgs::TransformMode newMode, KisNodeList processedNodes)
static bool fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeList *rootNodes, KisNodeList *transformedNodes, int *oldTime)
static QRectF handleRect(qreal radius, const QTransform &t, const QRectF &limitingRect, qreal *dOutX, qreal *dOutY)
static bool thumbnailTooSmall(const QTransform &resultThumbTransform, const QRect &originalImageRect)
static bool tryInitArgsFromNode(KisNodeList rootNodes, ToolTransformArgs *args)
static bool checkImageTooBig(const QRectF &bounds, const MatricesPack &m, qreal cameraHeight)
static const int rotationHandleVisualRadius
static void transformDeviceWithCroppedDst(const ToolTransformArgs &config, KisPaintDeviceSP srcDevice, KisPaintDeviceSP dstDevice, KisProcessingVisitor::ProgressHelper *helper, bool forceSubPixelTranslation)
static bool tryFetchArgsFromCommandAndUndo(ToolTransformArgs *outArgs, ToolTransformArgs::TransformMode mode, KisNodeList currentNodes, KisNodeList selectedNodes, KisStrokeUndoFacade *undoFacade, int currentTime, QVector< KisStrokeJobData * > *undoJobs, const KisSavedMacroCommand **overriddenCommand)
void setForceSubPixelTranslation(bool value)
QTransform transform() const
QRect approxChangeRect(const QRect &rc)
QRect approxNeedRect(const QRect &rc, const QRect &fullBounds)
static qreal approxTransformScale(const QTransform &t)
Definition KoUnit.cpp:387
QPointF transformedCenter() const
void setFilterId(const QString &id)
void initLiquifyTransformMode(const QRect &srcRect)
const QVector< QPointF > & origPoints() const
QTransform flattenedPerspectiveTransform() const
KisFilterStrategy * filter() const
KisLiquifyTransformWorker * liquifyWorker() const
KisWarpTransformWorker::WarpType warpType() const
void setPoints(QVector< QPointF > origPoints, QVector< QPointF > transfPoints)
void setTransformedCenter(QPointF transformedCenter)
void setOriginalCenter(QPointF originalCenter)
QPointF originalCenter() const
QPointF rotationCenterOffset() const
void setDefaultPoints(bool defaultPoints)
const QVector< QPointF > & transfPoints() const
const KisBezierTransformMesh * meshTransform() const
void setEditingTransformPoints(bool value)
void setMode(TransformMode mode)
double boundsRotation() const
QString filterId() const
void setExternalSource(KisPaintDeviceSP externalSource)
TransformMode mode() const
QVector3D cameraPos() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define bounds(x, a, b)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
T kisRadiansToDegrees(T radians)
Definition kis_global.h:181
T kisDegreesToRadians(T degrees)
Definition kis_global.h:176
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngle(T a)
Definition kis_global.h:121
#define M_PI
Definition kis_global.h:111
QRectF handleRectImpl(qreal radius, const QTransform &t, const QRectF &limitingRect, const QPointF &basePoint, qreal *dOutX, qreal *dOutY)
auto minDimension(Size size) -> decltype(size.width())
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
void recursiveApplyNodes(NodePointer node, Functor func)
KisNodeList sortMergeableInternalNodes(KisNodeList nodes)
bool compareListsUnordered(const QList< T > &a, const QList< T > &b)
bool isEditable(bool checkVisibility=true) const
KisImageWSP image
virtual bool visible(bool recursive=false) const
void setTransformedCage(const QVector< QPointF > &transformedCage)
QRect approxChangeRect(const QRect &rc)
QRect approxNeedRect(const QRect &rc, const QRect &fullBounds)
QRect approxNeedRect(const QRect &rc, const QRect &fullBounds)
void run(KisPaintDeviceSP srcDevice, KisPaintDeviceSP dstDevice)
KisNodeSP firstChild() const
Definition kis_node.cpp:361
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
KisNodeWSP parentNode
AnchorHolder(bool enabled, ToolTransformArgs *config)
MatricesPack(const ToolTransformArgs &args)
ToolTransformArgs savedTransformArgs
KUndo2CommandExtraData * clone() const override