Krita Source Code Documentation
Loading...
Searching...
No Matches
KoFlake.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2009 Jos van den Oever <jos@vandenoever.info>
3 * SPDX-FileCopyrightText: 2009 Thomas Zander <zander@kde.org>
4 * SPDX-FileCopyrightText: 2008 Jan Hambrecht <jaham@gmx.net>
5 * SPDX-FileCopyrightText: 2010 Thorsten Zachmann <zachmann@kde.org>
6 *
7 * SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10#include "KoFlake.h"
11#include "KoShape.h"
12
13#include <QGradient>
14#include <math.h>
15#include "kis_global.h"
16
17QGradient *KoFlake::cloneGradient(const QGradient *gradient)
18{
19 if (! gradient)
20 return 0;
21
22 QGradient *clone = 0;
23
24 switch (gradient->type()) {
25 case QGradient::LinearGradient:
26 {
27 const QLinearGradient *lg = static_cast<const QLinearGradient*>(gradient);
28 clone = new QLinearGradient(lg->start(), lg->finalStop());
29 break;
30 }
31 case QGradient::RadialGradient:
32 {
33 const QRadialGradient *rg = static_cast<const QRadialGradient*>(gradient);
34 clone = new QRadialGradient(rg->center(), rg->radius(), rg->focalPoint());
35 break;
36 }
37 case QGradient::ConicalGradient:
38 {
39 const QConicalGradient *cg = static_cast<const QConicalGradient*>(gradient);
40 clone = new QConicalGradient(cg->center(), cg->angle());
41 break;
42 }
43 default:
44 return 0;
45 }
46
47 clone->setCoordinateMode(gradient->coordinateMode());
48 clone->setSpread(gradient->spread());
49 clone->setStops(gradient->stops());
50
51 return clone;
52}
53
54QGradient *KoFlake::mergeGradient(const QGradient *coordsSource, const QGradient *fillSource)
55{
56 QPointF start;
57 QPointF end;
58 QPointF focalPoint;
59
60 switch (coordsSource->type()) {
61 case QGradient::LinearGradient: {
62 const QLinearGradient *lg = static_cast<const QLinearGradient*>(coordsSource);
63 start = lg->start();
64 focalPoint = start;
65 end = lg->finalStop();
66 break;
67 }
68 case QGradient::RadialGradient: {
69 const QRadialGradient *rg = static_cast<const QRadialGradient*>(coordsSource);
70 start = rg->center();
71 end = start + QPointF(rg->radius(), 0);
72 focalPoint = rg->focalPoint();
73 break;
74 }
75 case QGradient::ConicalGradient: {
76 const QConicalGradient *cg = static_cast<const QConicalGradient*>(coordsSource);
77
78 start = cg->center();
79 focalPoint = start;
80
81 QLineF l (start, start + QPointF(1.0, 0));
82 l.setAngle(cg->angle());
83 end = l.p2();
84 break;
85 }
86 default:
87 return 0;
88 }
89
90 QGradient *clone = 0;
91
92 switch (fillSource->type()) {
93 case QGradient::LinearGradient:
94 clone = new QLinearGradient(start, end);
95 break;
96 case QGradient::RadialGradient:
97 clone = new QRadialGradient(start, kisDistance(start, end), focalPoint);
98 break;
99 case QGradient::ConicalGradient: {
100 QLineF l(start, end);
101 clone = new QConicalGradient(l.p1(), l.angle());
102 break;
103 }
104 default:
105 return 0;
106 }
107
108 clone->setCoordinateMode(fillSource->coordinateMode());
109 clone->setSpread(fillSource->spread());
110 clone->setStops(fillSource->stops());
111
112 return clone;
113}
114
115QPointF KoFlake::toRelative(const QPointF &absolute, const QSizeF &size)
116{
117 return QPointF(size.width() == 0 ? 0: absolute.x() / size.width(),
118 size.height() == 0 ? 0: absolute.y() / size.height());
119}
120
121QPointF KoFlake::toAbsolute(const QPointF &relative, const QSizeF &size)
122{
123 return QPointF(relative.x() * size.width(), relative.y() * size.height());
124}
125
126#include <QTransform>
127#include "kis_debug.h"
128#include "kis_algebra_2d.h"
129
130namespace {
131
132qreal getScaleByPointsPair(qreal x1, qreal x2, qreal expX1, qreal expX2)
133{
134 static const qreal eps = 1e-10;
135
136 const qreal diff = x2 - x1;
137 const qreal expDiff = expX2 - expX1;
138
139 return qAbs(diff) > eps ? expDiff / diff : 1.0;
140}
141
142void findMinMaxPoints(const QPolygonF &poly, int *minPoint, int *maxPoint, std::function<qreal(const QPointF&)> dimension)
143{
146
147 qreal minValue = dimension(poly[*minPoint]);
148 qreal maxValue = dimension(poly[*maxPoint]);
149
150 for (int i = 0; i < poly.size(); i++) {
151 const qreal value = dimension(poly[i]);
152
153 if (value < minValue) {
154 *minPoint = i;
155 minValue = value;
156 }
157
158 if (value > maxValue) {
159 *maxPoint = i;
160 maxValue = value;
161 }
162 }
163}
164
165}
166
167
168Qt::Orientation KoFlake::significantScaleOrientation(qreal scaleX, qreal scaleY)
169{
170 const qreal scaleXDeviation = qAbs(1.0 - scaleX);
171 const qreal scaleYDeviation = qAbs(1.0 - scaleY);
172
173 return scaleXDeviation > scaleYDeviation ? Qt::Horizontal : Qt::Vertical;
174}
175
176void KoFlake::scaleShape(KoShape *shape, qreal scaleX, qreal scaleY,
177 const QPointF &absoluteStillPoint,
178 const QTransform &postScalingCoveringTransform)
179{
180 const QTransform scale = QTransform::fromScale(scaleX, scaleY);
181 QPointF localStillPoint = postScalingCoveringTransform.inverted().map(absoluteStillPoint);
182 const QTransform localStillPointOffset = QTransform::fromTranslate(-localStillPoint.x(), -localStillPoint.y());
183
184 shape->setTransformation( shape->transformation() *
185 postScalingCoveringTransform.inverted() *
186 localStillPointOffset *
187 scale *
188 localStillPointOffset.inverted() *
189 postScalingCoveringTransform);
190}
191
192void KoFlake::scaleShapeGlobal(KoShape *shape, qreal scaleX, qreal scaleY,
193 const QPointF &absoluteStillPoint)
194{
195 const QTransform scale = QTransform::fromScale(scaleX, scaleY);
196 const QTransform absoluteStillPointOffset = QTransform::fromTranslate(-absoluteStillPoint.x(), -absoluteStillPoint.y());
197
198 const QTransform uniformGlobalTransform =
199 shape->absoluteTransformation() *
200 absoluteStillPointOffset *
201 scale *
202 absoluteStillPointOffset.inverted() *
203 shape->absoluteTransformation().inverted() *
204 shape->transformation();
205
206 shape->setTransformation(uniformGlobalTransform);
207}
208
209void KoFlake::resizeShape(KoShape *shape, qreal scaleX, qreal scaleY,
210 const QPointF &absoluteStillPoint,
211 bool useGlobalMode)
212{
213 using namespace KisAlgebra2D;
214
215 if (useGlobalMode) {
216 const QTransform scale = QTransform::fromScale(scaleX, scaleY);
217 const QTransform uniformGlobalTransform =
218 shape->absoluteTransformation() *
219 scale *
220 shape->absoluteTransformation().inverted();
221
222 const QRectF rect = shape->outlineRect();
223
245 // choose the most significant scale direction
246 Qt::Orientation significantOrientation = significantScaleOrientation(scaleX, scaleY);
247
248 std::function<qreal(const QPointF&)> dimension;
249
250 if (significantOrientation == Qt::Horizontal) {
251 dimension = [] (const QPointF &pt) {
252 return pt.x();
253 };
254
255 } else {
256 dimension = [] (const QPointF &pt) {
257 return pt.y();
258 };
259 }
260
261 // find min and max points (in absolute coordinates),
262 // by default use top-left and bottom-right
263 QPolygonF localPoints(rect);
264 QPolygonF globalPoints = shape->absoluteTransformation().map(localPoints);
265
266 int minPointIndex = 0;
267 int maxPointIndex = 2;
268
269 findMinMaxPoints(globalPoints, &minPointIndex, &maxPointIndex, dimension);
270
271 // calculate the scale using the extremum points
272 const QPointF minPoint = localPoints[minPointIndex];
273 const QPointF maxPoint = localPoints[maxPointIndex];
274
275 const QPointF minPointExpected = uniformGlobalTransform.map(minPoint);
276 const QPointF maxPointExpected = uniformGlobalTransform.map(maxPoint);
277
278 scaleX = getScaleByPointsPair(minPoint.x(), maxPoint.x(),
279 minPointExpected.x(), maxPointExpected.x());
280 scaleY = getScaleByPointsPair(minPoint.y(), maxPoint.y(),
281 minPointExpected.y(), maxPointExpected.y());
282 }
283
284 const QSizeF oldSize(shape->size());
285 const QSizeF newSize(oldSize.width() * qAbs(scaleX), oldSize.height() * qAbs(scaleY));
286
287 const QTransform mirrorTransform = QTransform::fromScale(signPZ(scaleX), signPZ(scaleY));
288
294 shape->setSize(newSize);
295
296 QPointF localStillPoint = shape->absoluteTransformation().inverted().map(absoluteStillPoint);
297 const QTransform localStillPointOffset = QTransform::fromTranslate(-localStillPoint.x(), -localStillPoint.y());
298 const QSizeF realNewSize = shape->size();
299
300 const QTransform realResizeTransform =
301 QTransform::fromScale(oldSize.width() > 0 ? realNewSize.width() / oldSize.width() : 1.0,
302 oldSize.height() > 0 ? realNewSize.height() / oldSize.height() : 1.0);
303
304 shape->setTransformation(realResizeTransform.inverted() *
305 localStillPointOffset *
306 realResizeTransform *
307 mirrorTransform *
308 localStillPointOffset.inverted() *
309 shape->transformation()
310 );
311}
312
313void KoFlake::resizeShapeCommon(KoShape *shape, qreal scaleX, qreal scaleY,
314 const QPointF &absoluteStillPoint,
315 bool useGlobalMode,
316 bool usePostScaling, const QTransform &postScalingCoveringTransform)
317{
318 if (usePostScaling) {
319 if (!useGlobalMode) {
320 scaleShape(shape, scaleX, scaleY, absoluteStillPoint, postScalingCoveringTransform);
321 } else {
322 scaleShapeGlobal(shape, scaleX, scaleY, absoluteStillPoint);
323 }
324 } else {
325 resizeShape(shape, scaleX, scaleY, absoluteStillPoint, useGlobalMode);
326 }
327}
328
329QPointF KoFlake::anchorToPoint(AnchorPosition anchor, const QRectF rect, bool *valid)
330{
331 static QVector<QPointF> anchorTable;
332
333 if (anchorTable.isEmpty()) {
334 anchorTable << QPointF(0.0,0.0);
335 anchorTable << QPointF(0.5,0.0);
336 anchorTable << QPointF(1.0,0.0);
337
338 anchorTable << QPointF(0.0,0.5);
339 anchorTable << QPointF(0.5,0.5);
340 anchorTable << QPointF(1.0,0.5);
341
342 anchorTable << QPointF(0.0,1.0);
343 anchorTable << QPointF(0.5,1.0);
344 anchorTable << QPointF(1.0,1.0);
345 }
346
347 if (valid)
348 *valid = false;
349
350 switch(anchor)
351 {
352 case AnchorPosition::TopLeft:
353 case AnchorPosition::Top:
354 case AnchorPosition::TopRight:
355 case AnchorPosition::Left:
356 case AnchorPosition::Center:
357 case AnchorPosition::Right:
358 case AnchorPosition::BottomLeft:
359 case AnchorPosition::Bottom:
360 case AnchorPosition::BottomRight:
361 if (valid)
362 *valid = true;
363 return KisAlgebra2D::relativeToAbsolute(anchorTable[int(anchor)], rect);
364 default:
365 KIS_SAFE_ASSERT_RECOVER_NOOP(anchor >= AnchorPosition::TopLeft && anchor < AnchorPosition::NumAnchorPositions);
366 return rect.topLeft();
367 }
368}
float value(const T *src, size_t ch)
virtual QSizeF size() const
Get the size of the shape in pt.
Definition KoShape.cpp:820
virtual QRectF outlineRect() const
Definition KoShape.cpp:637
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
void setTransformation(const QTransform &matrix)
Definition KoShape.cpp:417
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:424
virtual void setSize(const QSizeF &size)
Resize the shape.
Definition KoShape.cpp:276
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
const qreal eps
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
QPointF relativeToAbsolute(const QPointF &pt, const QRectF &rc)
KRITAFLAKE_EXPORT QGradient * cloneGradient(const QGradient *gradient)
clones the given gradient
Definition KoFlake.cpp:17
KRITAFLAKE_EXPORT QGradient * mergeGradient(const QGradient *coordsSource, const QGradient *fillSource)
Definition KoFlake.cpp:54
AnchorPosition
Definition KoFlake.h:85
KRITAFLAKE_EXPORT void resizeShape(KoShape *shape, qreal scaleX, qreal scaleY, const QPointF &absoluteStillPoint, bool useGlobalMode)
Definition KoFlake.cpp:209
KRITAFLAKE_EXPORT QPointF toRelative(const QPointF &absolute, const QSizeF &size)
Definition KoFlake.cpp:115
KRITAFLAKE_EXPORT void scaleShape(KoShape *shape, qreal scaleX, qreal scaleY, const QPointF &absoluteStillPoint, const QTransform &postScalingCoveringTransform)
Definition KoFlake.cpp:176
KRITAFLAKE_EXPORT void scaleShapeGlobal(KoShape *shape, qreal scaleX, qreal scaleY, const QPointF &absoluteStillPoint)
Definition KoFlake.cpp:192
KRITAFLAKE_EXPORT QPointF anchorToPoint(AnchorPosition anchor, const QRectF rect, bool *valid=0)
Definition KoFlake.cpp:329
KRITAFLAKE_EXPORT QPointF toAbsolute(const QPointF &relative, const QSizeF &size)
Definition KoFlake.cpp:121
KRITAFLAKE_EXPORT Qt::Orientation significantScaleOrientation(qreal scaleX, qreal scaleY)
Definition KoFlake.cpp:168
KRITAFLAKE_EXPORT void resizeShapeCommon(KoShape *shape, qreal scaleX, qreal scaleY, const QPointF &absoluteStillPoint, bool useGlobalMode, bool usePostScaling, const QTransform &postScalingCoveringTransform)
Definition KoFlake.cpp:313