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