Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_constrained_rect.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 "kis_debug.h"
11#include "kis_algebra_2d.h"
12
13
17
21
23{
24 m_rect = rect;
25 if (!ratioLocked()) {
26 storeRatioSafe(m_rect.size());
27 }
28 Q_EMIT sigValuesChanged();
29}
30
31void KisConstrainedRect::setCropRect(const QRect &cropRect)
32{
33 m_cropRect = cropRect;
34}
35
37 return m_centered;
38}
42
44 return m_canGrow;
45}
49
51 return m_rect.normalized();
52}
53
55 return qAbs(m_ratio);
56}
57
58void KisConstrainedRect::moveHandle(HandleType handle, const QPoint &offset, const QRect &oldRect)
59{
60 const QSize oldSize = oldRect.size();
61 QSize newSize = oldSize;
62 QPoint newOffset = oldRect.topLeft();
63
64 int xSizeCoeff = 1;
65 int ySizeCoeff = 1;
66
67 qreal xOffsetFromSizeChange = 1.0;
68 qreal yOffsetFromSizeChange = 1.0;
69
70 int baseSizeCoeff = 1;
71
72 bool useMoveOnly = false;
73
74 switch (handle) {
75 case UpperLeft:
76 xSizeCoeff = -1;
77 ySizeCoeff = -1;
78 xOffsetFromSizeChange = -1.0;
79 yOffsetFromSizeChange = -1.0;
80 break;
81 case UpperRight:
82 xSizeCoeff = 1;
83 ySizeCoeff = -1;
84 xOffsetFromSizeChange = 0.0;
85 yOffsetFromSizeChange = -1.0;
86 break;
87 case Creation:
88 baseSizeCoeff = 0;
89 Q_FALLTHROUGH();
90 case LowerRight:
91 xSizeCoeff = 1;
92 ySizeCoeff = 1;
93 xOffsetFromSizeChange = 0.0;
94 yOffsetFromSizeChange = 0.0;
95 break;
96 case LowerLeft:
97 xSizeCoeff = -1;
98 ySizeCoeff = 1;
99 xOffsetFromSizeChange = -1.0;
100 yOffsetFromSizeChange = 0.0;
101 break;
102 case Upper:
103 xSizeCoeff = 0;
104 ySizeCoeff = -1;
105 xOffsetFromSizeChange = -0.5;
106 yOffsetFromSizeChange = -1.0;
107 break;
108 case Right:
109 xSizeCoeff = 1;
110 ySizeCoeff = 0;
111 xOffsetFromSizeChange = 0.0;
112 yOffsetFromSizeChange = -0.5;
113 break;
114 case Lower:
115 xSizeCoeff = 0;
116 ySizeCoeff = 1;
117 xOffsetFromSizeChange = -0.5;
118 yOffsetFromSizeChange = 0.0;
119 break;
120 case Left:
121 xSizeCoeff = -1;
122 ySizeCoeff = 0;
123 xOffsetFromSizeChange = -1.0;
124 yOffsetFromSizeChange = -0.5;
125 break;
126 case Inside:
127 useMoveOnly = true;
128 break;
129 case None: // should never happen
130 break;
131 }
132
133 if (!useMoveOnly) {
134 const int centeringSizeCoeff = m_centered ? 2 : 1;
135 if (m_centered) {
136 xOffsetFromSizeChange = -0.5;
137 yOffsetFromSizeChange = -0.5;
138 }
139
140
141 QSize sizeDiff(offset.x() * xSizeCoeff * centeringSizeCoeff,
142 offset.y() * ySizeCoeff * centeringSizeCoeff);
143
144 QSize tempSize = baseSizeCoeff * oldSize + sizeDiff;
145 bool widthPreferable = qAbs(tempSize.width()) > qAbs(tempSize.height() * m_ratio);
146
147 if (ratioLocked()) {
148 if ((widthPreferable && xSizeCoeff != 0) || ySizeCoeff == 0) {
149 newSize.setWidth(tempSize.width());
150 newSize.setHeight(heightFromWidthUnsignedRatio(newSize.width(), m_ratio, tempSize.height()));
151 } else if ((!widthPreferable && ySizeCoeff != 0) || xSizeCoeff == 0) {
152 newSize.setHeight(tempSize.height());
153 newSize.setWidth(widthFromHeightUnsignedRatio(newSize.height(), m_ratio, tempSize.width()));
154 }
155
156 // see https://bugs.kde.org/show_bug.cgi?id=432036
157 if (!m_canGrow && qAbs(newSize.width()) > m_cropRect.width()) {
158 newSize.setWidth(m_cropRect.width());
159 newSize.setHeight(heightFromWidthUnsignedRatio(newSize.width(), m_ratio, newSize.height()));
160 }
161 if (!m_canGrow && qAbs(newSize.height()) > m_cropRect.height()) {
162 newSize.setHeight(m_cropRect.height());
163 newSize.setWidth(widthFromHeightUnsignedRatio(newSize.height(), m_ratio, newSize.width()));
164 }
165 } else if (widthLocked() && heightLocked()) {
166 newSize.setWidth(KisAlgebra2D::copysign(newSize.width(), tempSize.width()));
167 newSize.setHeight(KisAlgebra2D::copysign(newSize.height(), tempSize.height()));
168 } else if (widthLocked()) {
169 newSize.setWidth(KisAlgebra2D::copysign(newSize.width(), tempSize.width()));
170 newSize.setHeight(tempSize.height());
171 storeRatioSafe(newSize);
172 } else if (heightLocked()) {
173 newSize.setHeight(KisAlgebra2D::copysign(newSize.height(), tempSize.height()));
174 newSize.setWidth(tempSize.width());
175 storeRatioSafe(newSize);
176 } else {
177 newSize = baseSizeCoeff * oldSize + sizeDiff;
178 storeRatioSafe(newSize);
179 }
180
181 QSize realSizeDiff = newSize - baseSizeCoeff * oldSize;
182 QPoint offsetDiff(realSizeDiff.width() * xOffsetFromSizeChange,
183 realSizeDiff.height() * yOffsetFromSizeChange);
184
185 newOffset = oldRect.topLeft() + offsetDiff;
186 } else {
187 newOffset = oldRect.topLeft() + offset;
188 }
189
190 QPoint prevOffset = newOffset;
191
192 if (!m_canGrow) {
193 if (newOffset.x() + newSize.width() > m_cropRect.width()) {
194 newOffset.setX(m_cropRect.width() - newSize.width());
195 }
196
197 if (newOffset.y() + newSize.height() > m_cropRect.height()) {
198 newOffset.setY(m_cropRect.height() - newSize.height());
199 }
200 if (newOffset.x() < m_cropRect.x()) {
201 newOffset.setX(m_cropRect.x());
202 }
203 if (newOffset.y() < m_cropRect.y()) {
204 newOffset.setY(m_cropRect.y());
205 }
206 }
207
208 if (!m_ratioLocked && !useMoveOnly) {
209 newOffset = prevOffset;
210 }
211
212 m_rect = QRect(newOffset, newSize);
213
214 if (!m_canGrow) {
216
217 if (ratioLocked() && m_rect.height()) {
218 qreal newRatio = m_rect.width() / (qreal)(m_rect.height());
219
220 if (newRatio > m_ratio) {
221 m_rect.setWidth(widthFromHeightUnsignedRatio(m_rect.height(), m_ratio, m_rect.width()));
222 } else {
223 m_rect.setHeight(heightFromWidthUnsignedRatio(m_rect.width(), m_ratio, m_rect.height()));
224 }
225 }
226 }
227
228 Q_EMIT sigValuesChanged();
229}
230
231QPointF KisConstrainedRect::handleSnapPoint(HandleType handle, const QPointF &cursorPos)
232{
233 QPointF snapPoint = cursorPos;
234
235 switch (handle) {
236 case UpperLeft:
237 snapPoint = m_rect.topLeft();
238 break;
239 case UpperRight:
240 snapPoint = m_rect.topRight() + QPointF(1, 0);
241 break;
242 case Creation:
243 break;
244 case LowerRight:
245 snapPoint = m_rect.bottomRight() + QPointF(1, 1);
246 break;
247 case LowerLeft:
248 snapPoint = m_rect.bottomLeft() + QPointF(0, 1);
249 break;
250 case Upper:
251 snapPoint.ry() = m_rect.y();
252 break;
253 case Right:
254 snapPoint.rx() = m_rect.right() + 1;
255 break;
256 case Lower:
257 snapPoint.ry() = m_rect.bottom() + 1;
258 break;
259 case Left:
260 snapPoint.rx() = m_rect.x();
261 break;
262 case Inside:
263 break;
264 case None: // should never happen
265 break;
266 }
267
268 return snapPoint;
269}
270
272{
273 setRectInitial(m_rect.normalized());
274}
275
276void KisConstrainedRect::setOffset(const QPoint &offset)
277{
278 QRect newRect = m_rect;
279 newRect.moveTo(offset);
280
281 if (!m_canGrow) {
282 newRect &= m_cropRect;
283 }
284
285 if (!newRect.isEmpty()) {
286 m_rect = newRect;
287 }
288
289 Q_EMIT sigValuesChanged();
290}
291
294
295 const qreal eps = 1e-7;
296 const qreal invEps = 1.0 / eps;
297
298 if (value < eps || value > invEps) {
299 Q_EMIT sigValuesChanged();
300 return;
301 }
302
303 const QSize oldSize = m_rect.size();
304 QSize newSize = oldSize;
305
306 if (widthLocked() && heightLocked()) {
307 setHeightLocked(false);
308 }
309
310 m_ratio = value;
311
312 if (!widthLocked() && !heightLocked()) {
313 int area = oldSize.width() * oldSize.height();
314 newSize.setWidth(qRound(std::sqrt(area * m_ratio)));
315 newSize.setHeight(qRound(newSize.width() / m_ratio));
316 } else if (widthLocked()) {
317 newSize.setHeight(newSize.width() / m_ratio);
318 } else if (heightLocked()) {
319 newSize.setWidth(newSize.height() * m_ratio);
320 }
321
322 assignNewSize(newSize);
323}
324
326{
328
329 const QSize oldSize = m_rect.size();
330 QSize newSize = oldSize;
331
332 if (ratioLocked()) {
333 newSize.setWidth(value);
334 newSize.setHeight(newSize.width() / m_ratio);
335 } else {
336 newSize.setWidth(value);
337 storeRatioSafe(newSize);
338 }
339
340 assignNewSize(newSize);
341}
342
344{
346
347 const QSize oldSize = m_rect.size();
348 QSize newSize = oldSize;
349
350 if (ratioLocked()) {
351 newSize.setHeight(value);
352 newSize.setWidth(newSize.height() * m_ratio);
353 } else {
354 newSize.setHeight(value);
355 storeRatioSafe(newSize);
356 }
357
358 assignNewSize(newSize);
359}
360
361void KisConstrainedRect::assignNewSize(const QSize &newSize)
362{
363 if (!m_centered) {
364 m_rect.setSize(newSize);
365 } else {
366 QSize sizeDiff = newSize - m_rect.size();
367 m_rect.translate(-qRound(sizeDiff.width() / 2.0), -qRound(sizeDiff.height() / 2.0));
368 m_rect.setSize(newSize);
369 }
370
371 if (!m_canGrow) {
373 }
374
375 Q_EMIT sigValuesChanged();
376}
377
378void KisConstrainedRect::storeRatioSafe(const QSize &newSize)
379{
380 m_ratio = qAbs(qreal(newSize.width()) / newSize.height());
381}
382
383int KisConstrainedRect::widthFromHeightUnsignedRatio(int height, qreal ratio, int oldWidth) const
384{
385 int newWidth = qRound(height * ratio);
386 return KisAlgebra2D::copysign(newWidth, oldWidth);
387}
388
389int KisConstrainedRect::heightFromWidthUnsignedRatio(int width, qreal ratio, int oldHeight) const
390{
391 int newHeight = qRound(width / ratio);
392 return KisAlgebra2D::copysign(newHeight, oldHeight);
393}
394
396 return m_widthLocked;
397}
399 return m_heightLocked;
400}
402 return m_ratioLocked;
403}
404
411
418
427
float value(const T *src, size_t ch)
void storeRatioSafe(const QSize &newSize)
void moveHandle(HandleType handle, const QPoint &offset, const QRect &oldRect)
void sigLockValuesChanged()
int widthFromHeightUnsignedRatio(int height, qreal ratio, int oldWidth) const
void setRectInitial(const QRect &rect)
QPointF handleSnapPoint(HandleType handle, const QPointF &cursorPos)
void setCropRect(const QRect &cropRect)
void setCentered(bool value)
void setCanGrow(bool value)
int heightFromWidthUnsignedRatio(int width, qreal ratio, int oldHeight) const
void assignNewSize(const QSize &newSize)
void setOffset(const QPoint &offset)
void setHeightLocked(bool value)
void setRatioLocked(bool value)
void setWidthLocked(bool value)
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
const qreal eps
T copysign(T x, T y)