13#include <QPainterPath>
91 inline QPointF
imageToThumb(
const QPointF &pt,
bool useFlakeOptimization);
102 m_d(new
Private(this, converter, currentArgs, transaction))
104 connect(&
m_d->recalculateSignalCompressor, SIGNAL(timeout()),
105 SLOT(recalculateTransformations()));
114 Q_UNUSED(shiftModifierActive);
115 Q_UNUSED(altModifierActive);
119 bool cursorOverPoint =
false;
120 m_d->pointIndexUnderCursor = -1;
126 for (
int i = 0; i < points.size(); ++i) {
130 cursorOverPoint =
true;
131 m_d->pointIndexUnderCursor = i;
135 if (cursorOverPoint) {
136 m_d->mode = perspectiveModifierActive &&
137 !
m_d->currentArgs.isEditingTransformPoints() ?
140 }
else if (!
m_d->currentArgs.isEditingTransformPoints()) {
141 QPolygonF polygon(
m_d->currentArgs.transfPoints());
142 bool insidePolygon = polygon.boundingRect().contains(mousePos);
181 bool drawTransfPoints)
184 m_d->drawOrigPoints = drawOrigPoints;
185 m_d->drawTransfPoints = drawTransfPoints;
190 m_d->closeOnStartPointClick =
value;
195 m_d->clipOriginalPointsPosition =
value;
199 m_d->transformType = type;
205 bool isEditingPoints)
207 Q_UNUSED(isEditingPoints);
216 const int numPoints = origPoints.size();
218 for (
int i = 0; i < numPoints; ++i) {
219 gc.setPen(outlinePen);
220 gc.drawLine(transfPoints[i], origPoints[i]);
222 gc.drawLine(transfPoints[i], origPoints[i]);
232 gc.setOpacity(
m_d->transaction.basePreviewOpacity());
233 gc.setTransform(
m_d->paintingTransform,
true);
234 gc.drawImage(
m_d->paintingOffset,
m_d->transformedImage);
240 gc.setTransform(
m_d->handlesTransform,
true);
242 if (
m_d->drawConnectionLines) {
246 m_d->currentArgs.origPoints(),
247 m_d->currentArgs.transfPoints(),
248 m_d->currentArgs.isEditingTransformPoints());
252 QPen mainPen(Qt::black);
253 mainPen.setCosmetic(
true);
255 QPen outlinePen(Qt::white);
256 outlinePen.setCosmetic(
true);
261 const int numPoints =
m_d->currentArgs.origPoints().size();
267 qreal dstIn = 8 / handlesExtraScale;
268 qreal dstOut = 10 / handlesExtraScale;
269 qreal srcIn = 6 / handlesExtraScale;
270 qreal srcOut = 6 / handlesExtraScale;
272 QRectF handleRect1(-0.5 * dstIn, -0.5 * dstIn, dstIn, dstIn);
273 QRectF handleRect2(-0.5 * dstOut, -0.5 * dstOut, dstOut, dstOut);
275 if (
m_d->drawTransfPoints) {
278 for (
int i = 0; i < numPoints; ++i) {
279 gc.setPen(outlinePen);
280 gc.drawEllipse(handleRect2.translated(
m_d->currentArgs.transfPoints()[i]));
282 gc.drawEllipse(handleRect1.translated(
m_d->currentArgs.transfPoints()[i]));
288 QBrush selectionBrush = selectedPoints.size() > 1 ? Qt::red : Qt::black;
290 QBrush oldBrush = gc.brush();
291 gc.setBrush(selectionBrush);
292 Q_FOREACH (
const QPointF *pt, selectedPoints) {
293 gc.drawEllipse(handleRect1.translated(*pt));
295 gc.setBrush(oldBrush);
299 if (
m_d->drawOrigPoints) {
301 inLine.moveTo(-0.5 * srcIn, 0);
302 inLine.lineTo( 0.5 * srcIn, 0);
303 inLine.moveTo( 0, -0.5 * srcIn);
304 inLine.lineTo( 0, 0.5 * srcIn);
306 QPainterPath outLine;
307 outLine.moveTo(-0.5 * srcOut, -0.5 * srcOut);
308 outLine.lineTo( 0.5 * srcOut, -0.5 * srcOut);
309 outLine.lineTo( 0.5 * srcOut, 0.5 * srcOut);
310 outLine.lineTo(-0.5 * srcOut, 0.5 * srcOut);
311 outLine.lineTo(-0.5 * srcOut, -0.5 * srcOut);
315 for (
int i = 0; i < numPoints; ++i) {
316 gc.setPen(outlinePen);
317 gc.drawPath(outLine.translated(
m_d->currentArgs.origPoints()[i]));
319 gc.drawPath(inLine.translated(
m_d->currentArgs.origPoints()[i]));
326 if (
m_d->currentArgs.warpCalculation() == KisWarpTransformWorker::WarpCalculation::GRID &&
331 const int numPoints =
m_d->currentArgs.origPoints().size();
334 int rowsInWarp = sqrt(
m_d->currentArgs.origPoints().size());
341 for (
int i = 0; i < numPoints; i++) {
342 if (i != 0 && i % rowsInWarp == rowsInWarp -1) {
350 for (
int i = 0; i < numPoints; i++) {
352 if ( (numPoints - i - 1) < rowsInWarp ) {
355 handlePainter.
drawConnectionLine(
m_d->currentArgs.transfPoints()[i],
m_d->currentArgs.transfPoints()[i+rowsInWarp] );
366 if (
m_d->lastNumPoints !=
m_d->currentArgs.transfPoints().size()) {
367 m_d->pointsInAction.clear();
370 m_d->recalculateTransformations();
375 const bool isEditingPoints =
m_d->currentArgs.isEditingTransformPoints();
386 }
else if (isEditingPoints) {
387 QPointF newPos =
m_d->clipOriginalPointsPosition ?
391 m_d->currentArgs.refOriginalPoints().append(newPos);
392 m_d->currentArgs.refTransformedPoints().append(newPos);
395 m_d->pointIndexUnderCursor =
m_d->currentArgs.origPoints().size() - 1;
397 m_d->recalculateSignalCompressor.start();
403 m_d->pointPosOnClick =
404 m_d->currentArgs.transfPoints()[
m_d->pointIndexUnderCursor];
405 m_d->pointWasDragged =
false;
407 m_d->pointsInAction.clear();
408 m_d->pointsInAction <<
m_d->pointIndexUnderCursor;
409 m_d->lastNumPoints =
m_d->currentArgs.transfPoints().size();
412 std::find(
m_d->pointsInAction.begin(),
413 m_d->pointsInAction.end(),
414 m_d->pointIndexUnderCursor);
416 if (it !=
m_d->pointsInAction.end()) {
417 m_d->pointsInAction.erase(it);
419 m_d->pointsInAction <<
m_d->pointIndexUnderCursor;
422 m_d->lastNumPoints =
m_d->currentArgs.transfPoints().size();
425 m_d->lastMousePos = pt;
437 selectedPoints << &points[index];
443 for (; it != end; ++it) {
444 selectedPoints << &(*it);
449 *center = boundingRect.center();
450 return selectedPoints;
455 Q_UNUSED(shiftModifierActive);
456 Q_UNUSED(altModifierActive);
463 m_d->pointIndexUnderCursor >= 0 &&
464 m_d->pointsInAction.size() == 1) ||
466 m_d->pointIndexUnderCursor >= 0));
469 if (
m_d->currentArgs.isEditingTransformPoints()) {
470 QPointF newPos =
m_d->clipOriginalPointsPosition ?
473 m_d->currentArgs.origPoint(
m_d->pointIndexUnderCursor) = newPos;
474 m_d->currentArgs.transfPoint(
m_d->pointIndexUnderCursor) = newPos;
476 m_d->currentArgs.transfPoint(
m_d->pointIndexUnderCursor) = pt;
483 m_d->currentArgs.transfPoint(
m_d->pointIndexUnderCursor),
484 m_d->pointPosOnClick);
486 if (dist > handleRadiusSq) {
487 m_d->pointWasDragged =
true;
493 QPointF diff = pt -
m_d->lastMousePos;
497 for (; it != end; ++it) {
504 QPointF oldDirection =
m_d->lastMousePos - center;
505 QPointF newDirection = pt - center;
509 R.rotateRadians(rotateAngle);
512 QTransform::fromTranslate(-center.x(), -center.y()) *
514 QTransform::fromTranslate(center.x(), center.y());
518 for (; it != end; ++it) {
525 QPolygonF polygon(
m_d->currentArgs.origPoints());
526 QSizeF maxSize = polygon.boundingRect().size();
527 qreal maxDimension = qMax(maxSize.width(), maxSize.height());
529 qreal scale = 1.0 - (pt -
m_d->lastMousePos).y() / maxDimension;
532 QTransform::fromTranslate(-center.x(), -center.y()) *
533 QTransform::fromScale(scale, scale) *
534 QTransform::fromTranslate(center.x(), center.y());
538 for (; it != end; ++it) {
543 m_d->lastMousePos = pt;
544 m_d->recalculateSignalCompressor.start();
550 return currentArgs.isEditingTransformPoints() &&
551 closeOnStartPointClick &&
552 pointIndexUnderCursor == 0 &&
553 currentArgs.origPoints().size() > 2 &&
559 return m_d->shouldCloseTheCage() ||
560 m_d->currentArgs.isEditingTransformPoints();
565 if (
m_d->shouldCloseTheCage()) {
566 m_d->currentArgs.setEditingTransformPoints(
false);
574 return useFlakeOptimization ? converter->imageToDocument(converter->documentToFlake((pt))) : q->thumbToImageTransform().inverted().map(pt);
581 QTransform resultThumbTransform = q->thumbToImageTransform() * scaleTransform;
583 bool useFlakeOptimization = scale < 1.0 &&
589 for (
int i = 0; i < currentArgs.numPoints(); ++i) {
590 thumbOrigPoints[i] = imageToThumb(currentArgs.origPoints()[i], useFlakeOptimization);
591 thumbTransfPoints[i] = imageToThumb(currentArgs.transfPoints()[i], useFlakeOptimization);
594 paintingOffset = transaction.originalTopLeft();
596 if (!q->originalImage().isNull() && !currentArgs.isEditingTransformPoints()) {
597 QPointF origTLInFlake = imageToThumb(transaction.originalTopLeft(), useFlakeOptimization);
599 if (useFlakeOptimization) {
600 transformedImage = q->originalImage().transformed(resultThumbTransform);
601 paintingTransform = QTransform();
603 transformedImage = q->originalImage();
604 paintingTransform = resultThumbTransform;
608 transformedImage = q->calculateTransformedImage(currentArgs,
615 transformedImage = q->originalImage();
616 paintingOffset = imageToThumb(transaction.originalTopLeft(),
false);
617 paintingTransform = resultThumbTransform;
620 handlesTransform = scaleTransform;
621 Q_EMIT q->requestCanvasUpdate();
622 Q_EMIT q->requestImageRecalculation();
626 const QImage &srcImage,
629 const QPointF &srcOffset,
634 origPoints, transfPoints,
637 srcOffset, dstOffset);
640#include "moc_kis_warp_transform_strategy.cpp"
float value(const T *src, size_t ch)
Eigen::Matrix< double, 4, 2 > R
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
static QCursor crossCursor()
static QCursor rotateCursor()
static QCursor moveCursor()
static QCursor arrowCursor()
static QCursor pointingHandCursor()
static QCursor sizeVerCursor()
The KisHandlePainterHelper class is a special helper for painting handles around objects....
void drawConnectionLine(const QLineF &line)
void setHandleStyle(const KisHandleStyle &style)
static KisHandleStyle & primarySelection()
#define KIS_ASSERT_RECOVER_RETURN(cond)
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
void accumulateBounds(const Point &pt, Rect *bounds)
qreal angleBetweenVectors(const QPointF &v1, const QPointF &v2)
void initAntsPen(QPen *antsPen, QPen *outlinePen, int antLength, int antSpace)