File indexing completed on 2024-05-12 15:58:18

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_green_coordinates_math.h"
0008 
0009 #include <cmath>
0010 #include <kis_global.h>
0011 #include <kis_algebra_2d.h>
0012 using namespace KisAlgebra2D;
0013 
0014 
0015 /**
0016  * Implementation of the Green Coordinates transformation method
0017  * by Yaron Lipman, David Levin, Daniel Cohen-Or
0018  *
0019  * http://www.math.tau.ac.il/~lipmanya/GC/gc.htm
0020  */
0021 
0022 struct PrecalculatedCoords
0023 {
0024     QVector<qreal> psi; // for each edge
0025     QVector<qreal> phi; // for each vertex
0026 };
0027 
0028 
0029 struct Q_DECL_HIDDEN KisGreenCoordinatesMath::Private
0030 {
0031     Private () : transformedCageDirection(0) {}
0032 
0033     QVector<qreal> originalCageEdgeSizes;
0034     QVector<QPointF> transformedCageNormals;
0035     int transformedCageDirection;
0036 
0037     QVector<PrecalculatedCoords> precalculatedCoords;
0038 
0039     void precalculateOnePoint(const QVector<QPointF> &originalCage,
0040                               PrecalculatedCoords *coords,
0041                               const QPointF &pt,
0042                               int polygonDirection);
0043 
0044     inline void precalculateOneEdge(const QPointF &pt,
0045                                     const QPointF &v1,
0046                                     const QPointF &v2,
0047                                     qreal *edge_psi,
0048                                     qreal *vertex1_phi,
0049                                     qreal *vertex2_phi,
0050                                     int polygonDirection);
0051 };
0052 
0053 inline void KisGreenCoordinatesMath::
0054 Private::precalculateOneEdge(const QPointF &pt,
0055                              const QPointF &v1,
0056                              const QPointF &v2,
0057                              qreal *edge_psi,
0058                              qreal *vertex1_phi,
0059                              qreal *vertex2_phi,
0060                              int polygonDirection)
0061 {
0062     QPointF a = v2 - v1;
0063     QPointF b = v1 - pt;
0064     qreal Q = dotProduct(a, a);
0065     qreal S = dotProduct(b, b);
0066     qreal R = dotProduct(2 * a, b);
0067 
0068     qreal BA = dotProduct(b, norm(a) * inwardUnitNormal(a, polygonDirection));
0069     qreal SRT = std::sqrt(4 * S * Q - pow2(R));
0070     qreal L0 = std::log(S);
0071     qreal L1 = std::log(S + Q + R);
0072     qreal A0 = std::atan(R / SRT) / SRT;
0073     qreal A1 = std::atan((2 * Q + R) / SRT) / SRT;
0074     qreal A10 = A1 - A0;
0075     qreal L10 = L1 - L0;
0076 
0077     /**
0078      * The normals in the official paper are calculated somehow
0079      * differently so we must flip the sign of the \psi
0080      * variable. Don't ask me why... (DK)
0081      */
0082     static const qreal magicMultiplier = -1.0;
0083 
0084     *edge_psi = -magicMultiplier * norm(a) / (4 * M_PI) *
0085         ((4 * S - pow2(R) / Q) * A10 + R / (2 * Q) * L10 + L1 - 2);
0086 
0087     *vertex2_phi += -BA / (2 * M_PI) * (L10 / (2 * Q) - A10 * R / Q);
0088     *vertex1_phi +=  BA / (2 * M_PI) * (L10 / (2 * Q) - A10 * (2 + R / Q));
0089 }
0090 
0091 void KisGreenCoordinatesMath::Private::precalculateOnePoint(const QVector<QPointF> &originalCage,
0092                                                             PrecalculatedCoords *coords,
0093                                                             const QPointF &pt,
0094                                                             int polygonDirection)
0095 {
0096     const int numCagePoints = originalCage.size();
0097 
0098     // edge index is defined by the index of the start point
0099     // that is: v0-v1 -> e0, v1-v2 -> e1.
0100 
0101     for (int i = 1; i <= numCagePoints; i++) {
0102         int endIndex = i != numCagePoints ? i : 0;
0103         int startIndex = i - 1;
0104 
0105         precalculateOneEdge(pt,
0106                             originalCage[startIndex],
0107                             originalCage[endIndex],
0108                             &coords->psi[startIndex],
0109                             &coords->phi[startIndex],
0110                             &coords->phi[endIndex],
0111                             polygonDirection);
0112     }
0113 }
0114 
0115 KisGreenCoordinatesMath::KisGreenCoordinatesMath()
0116     : m_d(new Private())
0117 {
0118 }
0119 
0120 KisGreenCoordinatesMath::~KisGreenCoordinatesMath()
0121 {
0122 }
0123 
0124 void KisGreenCoordinatesMath::precalculateGreenCoordinates(const QVector<QPointF> &originalCage, const QVector<QPointF> &points)
0125 {
0126     const int cageDirection = polygonDirection(originalCage);
0127     const int numPoints = points.size();
0128     const int numCagePoints = originalCage.size();
0129 
0130     m_d->originalCageEdgeSizes.resize(numCagePoints);
0131 
0132     for (int i = 1; i <= numCagePoints; i++) {
0133         int endIndex = i != numCagePoints ? i : 0;
0134         int startIndex = i - 1;
0135 
0136         m_d->originalCageEdgeSizes[startIndex] =
0137             norm(originalCage[endIndex] - originalCage[startIndex]);
0138     }
0139 
0140     m_d->precalculatedCoords.resize(numPoints);
0141 
0142     for (int i = 0; i < numPoints; i++) {
0143         m_d->precalculatedCoords[i].psi.resize(numCagePoints);
0144         m_d->precalculatedCoords[i].phi.resize(numCagePoints);
0145 
0146         m_d->precalculateOnePoint(originalCage,
0147                                   &m_d->precalculatedCoords[i],
0148                                   points[i],
0149                                   cageDirection);
0150     }
0151 }
0152 
0153 void KisGreenCoordinatesMath::generateTransformedCageNormals(const QVector<QPointF> &transformedCage)
0154 {
0155     m_d->transformedCageDirection = polygonDirection(transformedCage);
0156 
0157     const int numCagePoints = transformedCage.size();
0158     m_d->transformedCageNormals.resize(numCagePoints);
0159 
0160     for (int i = 1; i <= numCagePoints; i++) {
0161         int endIndex = i != numCagePoints ? i : 0;
0162         int startIndex = i - 1;
0163 
0164         QPointF transformedEdge =
0165             transformedCage[endIndex] - transformedCage[startIndex];
0166 
0167         qreal scaleCoeff =
0168             norm(transformedEdge) / m_d->originalCageEdgeSizes[startIndex];
0169 
0170         m_d->transformedCageNormals[startIndex] =
0171             scaleCoeff * inwardUnitNormal(transformedEdge, m_d->transformedCageDirection);
0172     }
0173 }
0174 
0175 QPointF KisGreenCoordinatesMath::transformedPoint(int pointIndex, const QVector<QPointF> &transformedCage)
0176 {
0177     QPointF result;
0178 
0179     const int numCagePoints = transformedCage.size();
0180 
0181 
0182     PrecalculatedCoords *coords = &m_d->precalculatedCoords[pointIndex];
0183 
0184     for (int i = 0; i < numCagePoints; i++) {
0185         result += coords->phi[i] * transformedCage[i];
0186         result += coords->psi[i] * m_d->transformedCageNormals[i];
0187     }
0188 
0189     return result;
0190 }
0191