Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_safe_transform.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 <QTransform>
10#include <QLineF>
11#include <QPolygonF>
12
13
14#include "kis_debug.h"
15#include "kis_algebra_2d.h"
16
17
18
19struct Q_DECL_HIDDEN KisSafeTransform::Private
20{
21 bool needsClipping = true;
22
23 QRect bounds;
24 QTransform forwardTransform;
26
27 QPolygonF srcClipPolygon;
28 QPolygonF dstClipPolygon;
29
30 bool getHorizon(const QTransform &t, QLineF *horizon) {
31 static const qreal eps = 1e-10;
32
33 QPointF vanishingX(t.m11() / t.m13(), t.m12() / t.m13());
34 QPointF vanishingY(t.m21() / t.m23(), t.m22() / t.m23());
35
36 if (qAbs(t.m13()) < eps && qAbs(t.m23()) < eps) {
37 *horizon = QLineF();
38 return false;
39 } else if (qAbs(t.m23()) < eps) {
40 QPointF diff = t.map(QPointF(0.0, 10.0)) - t.map(QPointF());
41 vanishingY = vanishingX + diff;
42 } else if (qAbs(t.m13()) < eps) {
43 QPointF diff = t.map(QPointF(10.0, 0.0)) - t.map(QPointF());
44 vanishingX = vanishingY + diff;
45 }
46
47 *horizon = QLineF(vanishingX, vanishingY);
48 return true;
49 }
50
51 qreal getCrossSign(const QLineF &horizon, const QRectF &rc) {
52 if (rc.isEmpty()) return 1.0;
53
54 QPointF diff = horizon.p2() - horizon.p1();
55 return KisAlgebra2D::signPZ(KisAlgebra2D::crossProduct(diff, rc.center() - horizon.p1()));
56 }
57
58 QPolygonF getCroppedPolygon(const QLineF &baseHorizon, const QRect &rc, const qreal crossCoeff) {
59 if (rc.isEmpty()) return QPolygonF();
60
61 QRectF boundsRect(rc);
62 QPolygonF polygon(boundsRect);
63 QPolygonF result;
64
65 // calculate new (offset) horizon to avoid infinity
66 const qreal offsetLength = 10.0;
67 const QPointF horizonOffset = offsetLength * crossCoeff *
68 KisAlgebra2D::rightUnitNormal(baseHorizon.p2() - baseHorizon.p1());
69
70 const QLineF horizon = baseHorizon.translated(horizonOffset);
71
72 // base vectors to calculate the side of the horizon
73 const QPointF &basePoint = horizon.p1();
74 const QPointF horizonVec = horizon.p2() - basePoint;
75
76
77 // iteration
78 QPointF prevPoint = polygon[polygon.size() - 1];
79 qreal prevCross = crossCoeff * KisAlgebra2D::crossProduct(horizonVec, prevPoint - basePoint);
80
81 for (int i = 0; i < polygon.size(); i++) {
82 const QPointF &pt = polygon[i];
83
84 qreal cross = crossCoeff * KisAlgebra2D::crossProduct(horizonVec, pt - basePoint);
85
86 if ((cross >= 0 && prevCross >= 0) || (cross == 0 && prevCross < 0)) {
87 result << pt;
88 } else if (cross * prevCross < 0) {
89 QPointF intersection;
90 QLineF edge(prevPoint, pt);
91 QLineF::IntersectType intersectionType =
92 horizon.intersects(edge, &intersection);
93
94 KIS_ASSERT_RECOVER_NOOP(intersectionType != QLineF::NoIntersection);
95
96 result << intersection;
97
98 if (cross > 0) {
99 result << pt;
100 }
101 }
102
103 prevPoint = pt;
104 prevCross = cross;
105 }
106
107 if (result.size() > 0 && !result.isClosed()) {
108 result << result.first();
109 }
110
111 return result;
112 }
113
114};
115
116KisSafeTransform::KisSafeTransform(const QTransform &transform,
117 const QRect &bounds,
118 const QRect &srcInterestRect)
119 : m_d(new Private)
120{
121 m_d->bounds = bounds;
122
123 m_d->forwardTransform = transform;
124 m_d->backwardTransform = transform.inverted();
125
126 m_d->needsClipping = transform.type() > QTransform::TxShear;
127
128 if (m_d->needsClipping) {
129 m_d->srcClipPolygon = QPolygonF(QRectF(m_d->bounds));
130 m_d->dstClipPolygon = QPolygonF(QRectF(m_d->bounds));
131
132 qreal crossCoeff = 1.0;
133
134 QLineF srcHorizon;
135 if (m_d->getHorizon(m_d->backwardTransform, &srcHorizon)) {
136 crossCoeff = m_d->getCrossSign(srcHorizon, srcInterestRect);
137 m_d->srcClipPolygon = m_d->getCroppedPolygon(srcHorizon, m_d->bounds, crossCoeff);
138 }
139
140 QLineF dstHorizon;
141 if (m_d->getHorizon(m_d->forwardTransform, &dstHorizon)) {
142 crossCoeff = m_d->getCrossSign(dstHorizon, mapRectForward(srcInterestRect));
143 m_d->dstClipPolygon = m_d->getCroppedPolygon(dstHorizon, m_d->bounds, crossCoeff);
144 }
145 }
146}
147
151
152QPolygonF KisSafeTransform::srcClipPolygon() const
153{
154 return m_d->srcClipPolygon;
155}
156
157QPolygonF KisSafeTransform::dstClipPolygon() const
158{
159 return m_d->dstClipPolygon;
160}
161
162QPolygonF KisSafeTransform::mapForward(const QPolygonF &p)
163{
164 QPolygonF poly;
165
166 if (!m_d->needsClipping) {
167 poly = m_d->forwardTransform.map(p);
168 } else {
169 poly = m_d->srcClipPolygon.intersected(p);
170 poly = m_d->forwardTransform.map(poly).intersected(QRectF(m_d->bounds));
171 }
172
173 return poly;
174}
175
176QPolygonF KisSafeTransform::mapBackward(const QPolygonF &p)
177{
178 QPolygonF poly;
179
180 if (!m_d->needsClipping) {
181 poly = m_d->backwardTransform.map(p);
182 } else {
183 poly = m_d->dstClipPolygon.intersected(p);
184 poly = m_d->backwardTransform.map(poly).intersected(QRectF(m_d->bounds));
185 }
186
187 return poly;
188}
189
190QRectF KisSafeTransform::mapRectForward(const QRectF &rc)
191{
192 return mapForward(rc).boundingRect();
193}
194
195QRectF KisSafeTransform::mapRectBackward(const QRectF &rc)
196{
197 return mapBackward(rc).boundingRect();
198}
199
201{
202 return mapRectForward(QRectF(rc)).toAlignedRect();
203}
204
206{
207 return mapRectBackward(QRectF(rc)).toAlignedRect();
208}
209
const Params2D p
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define bounds(x, a, b)
const qreal eps
PointTypeTraits< T >::value_type crossProduct(const T &a, const T &b)
T rightUnitNormal(const T &a)
const QScopedPointer< Private > m_d
bool getHorizon(const QTransform &t, QLineF *horizon)
QRectF mapRectForward(const QRectF &rc)
KisSafeTransform(const QTransform &transform, const QRect &bounds, const QRect &srcInterestRect)
QPolygonF getCroppedPolygon(const QLineF &baseHorizon, const QRect &rc, const qreal crossCoeff)
QPolygonF mapForward(const QPolygonF &p)
QRectF mapRectBackward(const QRectF &rc)
QPolygonF mapBackward(const QPolygonF &p)
qreal getCrossSign(const QLineF &horizon, const QRectF &rc)