13#include <config-gsl.h>
16#include <gsl/gsl_multimin.h>
19#include <QtCore/qmath.h>
22#include <boost/math/distributions/normal.hpp>
24#include <QPainterPath>
35 for (
int i = 0; i < selectionPath.elementCount(); i++) {
36 QPainterPath::Element element = selectionPath.elementAt(i);
37 if (element.type == QPainterPath::CurveToDataElement)
continue;
56 const qreal minHiLevel = std::pow(0.5, 1.0 / exponent);
57 qreal ptWeightNode = 0.0;
59 for (
int i = 0; i < selectionPath.elementCount(); i++) {
60 if (selectionPath.elementAt(i).isMoveTo())
continue;
62 const int prevI = i > 0 ? i - 1 : selectionPath.elementCount() - 1;
63 const QPointF edgeP1 = selectionPath.elementAt(prevI);
64 const QPointF edgeP2 = selectionPath.elementAt(i);
66 const QPointF edgeVec = edgeP1 - edgeP2;
67 const QPointF
q1 = pt - edgeP1;
68 const QPointF
q2 = pt - edgeP2;
78 if (proj1 * proj2 >= 0) {
79 QPointF nearestPointVec =
80 qAbs(proj1) < qAbs(proj2) ?
q1 :
q2;
84 QLineF line(edgeP1, edgeP2);
88 hi = qMax(minHiLevel, hi);
93 ptWeightNode += 1.0 /
pow2(hi);
99 return 1.0 / std::sqrt(ptWeightNode);
103 return searchForMax ?
104 std::numeric_limits<qreal>::min() :
105 std::numeric_limits<qreal>::max();
109 qreal exponent,
bool searchForMax,
113 const qreal minStepThreshold = 0.3;
114 const int numCandidatesThreshold = 4;
121 int totalSamples = numSamples;
122 int effectiveSamples = numSamples & 0x1 ?
123 (numSamples - 1) / 2 + 1 : numSamples;
125 QRectF
rect = path.boundingRect();
127 qreal xOffset =
rect.width() / (totalSamples + 1);
128 qreal xStep = effectiveSamples == totalSamples ? xOffset : 2 * xOffset;
130 qreal yOffset =
rect.height() / (totalSamples + 1);
131 qreal yStep = effectiveSamples == totalSamples ? yOffset : 2 * yOffset;
133 if (xStep < minStepThreshold || yStep < minStepThreshold) {
138 int numCandidates = 0;
139 QPointF extremumPoint;
142 const qreal
eps = 1e-3;
143 int sanityNumRows = 0;
144 for (qreal y =
rect.y() + yOffset; y <
rect.bottom() -
eps; y += yStep) {
145 int sanityNumColumns = 0;
148 for (qreal x =
rect.x() + xOffset; x <
rect.right() -
eps; x += xStep) {
151 const QPointF pt(x, y);
152 if (!path.contains(pt))
continue;
155 bool isExtremum = searchForMax ?
value > extremumValue :
value < extremumValue;
162 extremumValue =
value;
170 bool success = numFound && numCandidates >= numCandidatesThreshold;
173 *result = extremumPoint;
175 int newNumSamples = 2 * numSamples + 1;
177 exponent, searchForMax,
181 if (!success && numFound > 0) {
182 *result = extremumPoint;
192 struct GradientDescentParams {
193 QPainterPath selectionPath;
198 double errorFunc (
const gsl_vector * x,
void *paramsPtr)
200 double vX = gsl_vector_get(x, 0);
201 double vY = gsl_vector_get(x, 1);
203 const GradientDescentParams *params =
204 static_cast<const GradientDescentParams*
>(paramsPtr);
207 params->selectionPath,
210 return params->searchForMax ? 1.0 / weight : weight;
217 const gsl_multimin_fminimizer_type *T =
218 gsl_multimin_fminimizer_nmsimplex2;
219 gsl_multimin_fminimizer *s = 0;
221 gsl_multimin_function minex_func;
230 exponent, searchForMax,
234 if (!centerExists || !selectionPath.contains(center)) {
237 if (selectionPath.boundingRect().width() >= 2.0 &&
238 selectionPath.boundingRect().height() >= 2.0) {
243 return searchForMax ? 1.0 : 0.0;
247 x = gsl_vector_alloc (2);
248 gsl_vector_set (x, 0, center.x());
249 gsl_vector_set (x, 1, center.y());
252 ss = gsl_vector_alloc (2);
253 gsl_vector_set (ss, 0, 10);
254 gsl_vector_set (ss, 1, 10);
256 GradientDescentParams
p;
258 p.selectionPath = selectionPath;
259 p.exponent = exponent;
260 p.searchForMax = searchForMax;
264 minex_func.f = errorFunc;
265 minex_func.params = (
void*)&
p;
267 s = gsl_multimin_fminimizer_alloc (T, 2);
268 gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
270 qreal result = searchForMax ?
276 status = gsl_multimin_fminimizer_iterate(s);
281 size = gsl_multimin_fminimizer_size (s);
282 status = gsl_multimin_test_size (size, 1e-6);
284 if (status == GSL_SUCCESS)
291 result = searchForMax ? 1.0 / s->fval : s->fval;
294 while (status == GSL_CONTINUE && iter < 10000);
298 gsl_multimin_fminimizer_free (s);
310 return searchForMax ?
323 QPainterPath finalPath;
326 Q_FOREACH (
const QPolygonF poly, polygons) {
330 const qreal
length =
p.length();
331 const qreal lengthStep =
333 sizePortion, minLinearSize);
335 int numSamples = qMax(qCeil(
length / lengthStep), minNumSamples);
337 if (numSamples > poly.size()) {
338 finalPath.addPolygon(poly);
339 finalPath.closeSubpath();
343 const qreal portionStep = 1.0 / numSamples;
346 for (qreal t = 0.0; t < 1.0; t += portionStep) {
347 newPoly <<
p.pointAtPercent(t);
350 finalPath.addPolygon(newPoly);
351 finalPath.closeSubpath();
359 : m_exponent(exponent)
392 exponent, searchForMax,
397 dbgKrita <<
"WARNING: Couldn't calculate findBestStartingPoint for:";
qreal length(const QPointF &vec)
float value(const T *src, size_t ch)
~KisPolygonalGradientShapeStrategy() override
static QPointF testingCalculatePathCenter(int numSamples, const QPainterPath &path, qreal exponent, bool searchForMax)
double valueAt(double x, double y) const override
KisPolygonalGradientShapeStrategy(const QPainterPath &selectionPath, qreal exponent)
QPainterPath m_selectionPath
static bool qFuzzyCompare(half p1, half p2)
#define KIS_ASSERT_RECOVER(cond)
#define KIS_ASSERT_RECOVER_NOOP(cond)
qreal kisDistanceToLine(const QPointF &m, const QLineF &line)
QPainterPath simplifyPath(const QPainterPath &path, qreal sizePortion, qreal minLinearSize, int minNumSamples)
PointTypeTraits< T >::value_type dotProduct(const T &a, const T &b)
int size(const Forest< T > &forest)
qreal KRITAIMAGE_EXPORT maxDimensionPortion(const QRectF &bounds, qreal portion, qreal minValue)
bool findBestStartingPoint(int numSamples, const QPainterPath &path, qreal exponent, bool searchForMax, qreal initialExtremumValue, QPointF *result)
qreal getDisnormedGradientValue(const QPointF &pt, const QPainterPath &selectionPath, qreal exponent)
qreal calculateMaxWeight(const QPainterPath &selectionPath, qreal exponent, bool searchForMax)
qreal initialExtremumValue(bool searchForMax)
QPointF centerFromPath(const QPainterPath &selectionPath)