Krita Source Code Documentation
Loading...
Searching...
No Matches
ShapeResizeStrategy.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2006-2007 Thomas Zander <zander@kde.org>
3 * SPDX-FileCopyrightText: 2007, 2011 Jan Hambrecht <jaham@gmx.net>
4 * SPDX-FileCopyrightText: 2017 Boudewijn Rempt <boud@valdyas.org>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
10#include "SelectionDecorator.h"
11
12#include <KoShapeManager.h>
13#include <KoPointerEvent.h>
14#include <KoCanvasBase.h>
16#include <kis_command_utils.h>
17#include <KoSnapGuide.h>
18#include <KoToolBase.h>
19#include <KoSelection.h>
20
21#include <klocalizedstring.h>
22#include <limits>
23#include <math.h>
24
25#include <kis_debug.h>
26#include "kis_algebra_2d.h"
27
28ShapeResizeStrategy::ShapeResizeStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, KoFlake::SelectionHandle direction, bool forceUniformScalingMode)
30 m_forceUniformScalingMode(forceUniformScalingMode)
31{
32 KIS_SAFE_ASSERT_RECOVER_RETURN(selection && selection->count() > 0);
33
35 m_start = clicked;
36
37 KoShape *shape = selection;
38
39 const qreal w = shape->size().width();
40 const qreal h = shape->size().height();
41
42 switch (direction) {
45 m_top = true; m_bottom = false; m_left = false; m_right = false;
46 m_globalStillPoint = QPointF(0.5 * w, h);
47 break;
50 m_top = true; m_bottom = false; m_left = false; m_right = true;
51 m_globalStillPoint = QPointF(0, h);
52 break;
55 m_top = false; m_bottom = false; m_left = false; m_right = true;
56 m_globalStillPoint = QPointF(0, 0.5 * h);
57 break;
60 m_top = false; m_bottom = true; m_left = false; m_right = true;
61 m_globalStillPoint = QPointF(0, 0);
62 break;
65 m_top = false; m_bottom = true; m_left = false; m_right = false;
66 m_globalStillPoint = QPointF(0.5 * w, 0);
67 break;
70 m_top = false; m_bottom = true; m_left = true; m_right = false;
71 m_globalStillPoint = QPointF(w, 0);
72 break;
75 m_top = false; m_bottom = false; m_left = true; m_right = false;
76 m_globalStillPoint = QPointF(w, 0.5 * h);
77 break;
80 m_top = true; m_bottom = false; m_left = true; m_right = false;
81 m_globalStillPoint = QPointF(w, h);
82 break;
83 default:
84 Q_ASSERT(0); // illegal 'corner'
85 }
86
87 const QPointF p0 = shape->outlineRect().topLeft();
90
91 m_unwindMatrix = shape->absoluteTransformation().inverted();
94
95
96 tool->setStatusText(i18n("Press CTRL to resize from center."));
98}
99
104
105void ShapeResizeStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers)
106{
107 QPointF newPos = tool()->canvas()->snapGuide()->snap(point, modifiers);
108
109 bool keepAspect = modifiers & Qt::ShiftModifier;
110 Q_FOREACH (KoShape *shape, m_selectedShapes) {
111 keepAspect = keepAspect || shape->keepAspectRatio();
112 }
113
114 qreal startWidth = m_initialSelectionSize.width();
115 if (startWidth < std::numeric_limits<qreal>::epsilon()) {
116 startWidth = std::numeric_limits<qreal>::epsilon();
117 }
118 qreal startHeight = m_initialSelectionSize.height();
119 if (startHeight < std::numeric_limits<qreal>::epsilon()) {
120 startHeight = std::numeric_limits<qreal>::epsilon();
121 }
122
123 QPointF distance = m_unwindMatrix.map(newPos) - m_unwindMatrix.map(m_start);
124
125 // guard against resizing zero width shapes, which would result in huge zoom factors
126 if (m_initialSelectionSize.width() < std::numeric_limits<qreal>::epsilon()) {
127 distance.rx() = 0.0;
128 }
129 // guard against resizing zero height shapes, which would result in huge zoom factors
130 if (m_initialSelectionSize.height() < std::numeric_limits<qreal>::epsilon()) {
131 distance.ry() = 0.0;
132 }
133
134 const bool scaleFromCenter = modifiers & Qt::ControlModifier;
135 if (scaleFromCenter) {
136 distance *= 2.0;
137 }
138
139 qreal newWidth = startWidth;
140 qreal newHeight = startHeight;
141
142 if (m_left) {
143 newWidth = startWidth - distance.x();
144 } else if (m_right) {
145 newWidth = startWidth + distance.x();
146 }
147
148 if (m_top) {
149 newHeight = startHeight - distance.y();
150 } else if (m_bottom) {
151 newHeight = startHeight + distance.y();
152 }
153
159 QSizeF minViewSize(1.0, 1.0);
160 QSizeF minDocSize = tool()->canvas()->viewConverter()->viewToDocument(minViewSize);
161
162 if (qAbs(newWidth) < minDocSize.width()) {
163 newWidth = KisAlgebra2D::signPZ(newWidth) * minDocSize.width();
164 }
165
166 if (qAbs(newHeight) < minDocSize.height()) {
167 newHeight = KisAlgebra2D::signPZ(newHeight) * minDocSize.height();
168 }
169
170 qreal zoomX = qAbs(startWidth) >= minDocSize.width() ? newWidth / startWidth : 1.0;
171 qreal zoomY = qAbs(startHeight) >= minDocSize.height() ? newHeight / startHeight : 1.0;
172
173 if (keepAspect) {
174 const bool cornerUsed = m_bottom + m_top + m_left + m_right == 2;
175 if (cornerUsed) {
176 if (startWidth < startHeight) {
177 zoomY = zoomX;
178 } else {
179 zoomX = zoomY;
180 }
181 } else {
182 if (m_left || m_right) {
183 zoomY = qAbs(zoomX);
184 } else {
185 zoomX = qAbs(zoomY);
186 }
187 }
188 }
189
190 resizeBy(scaleFromCenter ? m_globalCenterPoint : m_globalStillPoint, zoomX, zoomY);
191}
192
193void ShapeResizeStrategy::resizeBy(const QPointF &stillPoint, qreal zoomX, qreal zoomY)
194{
195 if (!m_executedCommand) {
196 const bool usePostScaling = m_selectedShapes.size() > 1 || m_forceUniformScalingMode;
197
198 m_executedCommand.reset(
201 zoomX, zoomY,
202 stillPoint,
203 false, usePostScaling, m_postScalingCoveringTransform));
204 m_executedCommand->redo();
205 } else {
206 m_executedCommand->replaceResizeAction(zoomX, zoomY, stillPoint);
207 }
208}
209
211{
212 tool()->canvas()->snapGuide()->reset();
213
214 if (m_executedCommand) {
215 m_executedCommand->setSkipOneRedo(true);
216 }
217
218 return m_executedCommand.take();
219}
220
221void ShapeResizeStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
222{
223 Q_UNUSED(modifiers);
224}
225
226void ShapeResizeStrategy::paint(QPainter &painter, const KoViewConverter &converter)
227{
228 Q_UNUSED(painter);
229 Q_UNUSED(converter);
230}
QPointF p0
qreal distance(const QPointF &p1, const QPointF &p2)
KoSnapGuide * snapGuide
virtual const KoViewConverter * viewConverter() const =0
int count() const
return the selection count, i.e. the number of all selected shapes
const QList< KoShape * > selectedEditableShapes() const
virtual QSizeF size() const
Get the size of the shape in pt.
Definition KoShape.cpp:820
virtual QRectF outlineRect() const
Definition KoShape.cpp:637
QPointF absolutePosition(KoFlake::AnchorPosition anchor=KoFlake::Center) const
Definition KoShape.cpp:653
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
static QList< KoShape * > linearizeSubtree(const QList< KoShape * > &shapes)
Definition KoShape.cpp:1381
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:424
bool keepAspectRatio() const
Definition KoShape.cpp:1052
void reset()
Resets the snap guide.
QPointF snap(const QPointF &mousePosition, Qt::KeyboardModifiers modifiers)
snaps the mouse position, returns if mouse was snapped
void setIgnoredShapes(const QList< KoShape * > &ignoredShapes)
Sets list of ignored shapes.
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
void setStatusText(const QString &statusText)
virtual QPointF viewToDocument(const QPointF &viewPoint) const
KUndo2Command * createCommand() override
QList< KoShape * > m_selectedShapes
void finishInteraction(Qt::KeyboardModifiers modifiers) override
void resizeBy(const QPointF &stillPoint, qreal zoomX, qreal zoomY)
void paint(QPainter &painter, const KoViewConverter &converter) override
QTransform m_postScalingCoveringTransform
QScopedPointer< KoShapeResizeCommand > m_executedCommand
void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override
ShapeResizeStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, KoFlake::SelectionHandle direction, bool forceUniformScalingMode)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
@ BottomRight
Definition KoFlake.h:94
@ TopRight
Definition KoFlake.h:88
@ TopLeft
Definition KoFlake.h:86
@ BottomLeft
Definition KoFlake.h:92
@ Center
Definition KoFlake.h:90
SelectionHandle
Enum determining which handle is meant, used in KoInteractionTool.
Definition KoFlake.h:55
@ BottomRightHandle
The handle that is at the bottom right of a selection.
Definition KoFlake.h:59
@ BottomLeftHandle
The handle that is at the bottom left of a selection.
Definition KoFlake.h:61
@ RightMiddleHandle
The handle that is at the right - center of a selection.
Definition KoFlake.h:58
@ TopRightHandle
The handle that is at the top - right of a selection.
Definition KoFlake.h:57
@ TopLeftHandle
The handle that is at the top left of a selection.
Definition KoFlake.h:63
@ LeftMiddleHandle
The handle that is at the left center of a selection.
Definition KoFlake.h:62
@ TopMiddleHandle
The handle that is at the top - center of a selection.
Definition KoFlake.h:56
@ BottomMiddleHandle
The handle that is at the bottom center of a selection.
Definition KoFlake.h:60