Krita Source Code Documentation
Loading...
Searching...
No Matches
KisBezierPatchParamSpaceUtils.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2023 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6#ifndef KISBEZIERPATCHPARAMSPACEUTILS_H
7#define KISBEZIERPATCHPARAMSPACEUTILS_H
8
9#include <optional>
10#include "kis_assert.h"
11#include "kis_algebra_2d.h"
12
13#include <QDebug>
14#include <kis_debug.h>
15
17{
18
22struct Range
23{
24 qreal start = 0.0;
25 qreal end = 0.0;
26
27 bool isEmpty() const {
28 return qFuzzyCompare(start, end);
29 }
30
31 qreal length() const {
32 return end - start;
33 }
34
35 qreal mid() const {
36 return 0.5 * (end + start);
37 }
38
39 bool contains(qreal value) const {
40 return value > start && value < end &&
42 qFuzzyCompare(value, end);
43 }
44
49 Range squeezedRange(const Range &alphaRange) const {
51 return {lerp(start, end, alphaRange.start), lerp(start, end, alphaRange.end)};
52 }
53
54 void squeezeRange(const Range &alphaRange) {
55 *this = squeezedRange(alphaRange);
56 }
57
73 std::optional<qreal> forwardDistanceTo(const Range &rhs) {
74 if (rhs.start > end) {
75 return rhs.start - end;
76 } else if (start > rhs.end) {
77 return -(start - rhs.end);
78 } else {
79 return std::nullopt;
80 }
81 }
82
83 static Range fromRectX(const QRectF &rc) {
84 return {rc.left(), rc.right()};
85 }
86
87 static Range fromRectY(const QRectF &rc) {
88 return {rc.top(), rc.bottom()};
89 }
90
91 static QRectF makeRectF(const Range &xRange, const Range &yRange) {
92 return {xRange.start, yRange.start, xRange.length(), yRange.length()};
93 }
94};
95
96QDebug operator<<(QDebug dbg, const Range &r)
97{
98 dbg.nospace() << "Range(" << r.start << ", " << r.end << ")";
99
100 return dbg.space();
101}
102
134template <typename Func>
135std::pair<Range, Range> calcTightSrcRectRangeInParamSpace1D(const Range &searchParamRange,
136 const Range &searchSrcRange,
137 const Range &rect,
138 Func func,
139 qreal srcPrecision,
140 std::optional<Range> squeezeRange = std::nullopt)
141{
142 Range leftSideParamRange = searchParamRange;
143 Range rightSideParamRange = searchParamRange;
144
145 if (qFuzzyCompare(rect.start, searchSrcRange.start)) {
146 leftSideParamRange = {searchParamRange.start, searchParamRange.start};
147 } else {
148 // search left side
149 while (1) {
150 KIS_SAFE_ASSERT_RECOVER_BREAK(!leftSideParamRange.isEmpty());
151
152 qreal currentSplitParam = leftSideParamRange.mid();
153 Range currentSplitSrcRange = func(currentSplitParam);
154 if (squeezeRange) {
155 currentSplitSrcRange.squeezeRange(*squeezeRange);
156 }
157
158 std::optional<qreal> forwardDistance = currentSplitSrcRange.forwardDistanceTo(rect);
159
160 if (!forwardDistance || qFuzzyIsNull(*forwardDistance)) {
161 leftSideParamRange.end = currentSplitParam;
162 rightSideParamRange.start = std::max(currentSplitParam, rightSideParamRange.start);
163 } else if (*forwardDistance > 0) {
164 KIS_SAFE_ASSERT_RECOVER_NOOP(currentSplitParam >= leftSideParamRange.start);
165 leftSideParamRange.start = currentSplitParam;
166 rightSideParamRange.start = std::max(currentSplitParam, rightSideParamRange.start);
167 } else if (*forwardDistance < 0) {
168 KIS_SAFE_ASSERT_RECOVER_NOOP(currentSplitParam <= rightSideParamRange.end);
169 rightSideParamRange.end = currentSplitParam;
170 leftSideParamRange.end = currentSplitParam;
171 }
172
173 if (forwardDistance && std::abs(*forwardDistance) < srcPrecision) {
174 break;
175 }
176 }
177 }
178
179 if (qFuzzyCompare(rect.end, searchSrcRange.end)) {
180 rightSideParamRange = {searchParamRange.end, searchParamRange.end};
181 } else {
182 // search right side
183 while (1) {
184 KIS_SAFE_ASSERT_RECOVER_BREAK(!rightSideParamRange.isEmpty());
185
186 qreal currentSplitParam = rightSideParamRange.mid();
187 Range currentSplitSrcRange = func(currentSplitParam);
188
189 if (squeezeRange) {
190 currentSplitSrcRange.squeezeRange(*squeezeRange);
191 }
192
193 std::optional<qreal> forwardDistance = currentSplitSrcRange.forwardDistanceTo(rect);
194
195 if (!forwardDistance || qFuzzyIsNull(*forwardDistance)) {
196 rightSideParamRange.start = currentSplitParam;
197 } else if (*forwardDistance > 0) {
198 rightSideParamRange.start = currentSplitParam;
199 } else if (*forwardDistance < 0) {
200 rightSideParamRange.end = currentSplitParam;
201 }
202
203 if (forwardDistance && std::abs(*forwardDistance) < srcPrecision) {
204 break;
205 }
206 }
207 }
208
209 return std::make_pair(Range{leftSideParamRange.start, rightSideParamRange.end},
210 Range{leftSideParamRange.end, rightSideParamRange.start});
211}
212
213} // namespace KisBezierUtils
214
215#endif // KISBEZIERPATCHPARAMSPACEUTILS_H
float value(const T *src, size_t ch)
QPointF lerp(const QPointF &p1, const QPointF &p2, qreal t)
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)
#define KIS_SAFE_ASSERT_RECOVER_BREAK(cond)
Definition kis_assert.h:127
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
Point lerp(const Point &pt1, const Point &pt2, qreal t)
std::pair< Range, Range > calcTightSrcRectRangeInParamSpace1D(const Range &searchParamRange, const Range &searchSrcRange, const Range &rect, Func func, qreal srcPrecision, std::optional< Range > squeezeRange=std::nullopt)
QDebug operator<<(QDebug dbg, const Range &r)
std::optional< qreal > forwardDistanceTo(const Range &rhs)
static Range fromRectY(const QRectF &rc)
Range squeezedRange(const Range &alphaRange) const
static Range fromRectX(const QRectF &rc)
static QRectF makeRectF(const Range &xRange, const Range &yRange)
void squeezeRange(const Range &alphaRange)