dust3d/src/regionfiller.cpp

1497 lines
55 KiB
C++
Raw Normal View History

2019-12-14 13:28:14 +00:00
#include <numeric>
#include <cmath>
#include <QDebug>
#include <set>
#include "regionfiller.h"
//
// This is an implementation of the following paper
//
// <Filling N-Sided Regions by Quad Meshes for Subdivision Surfaces>
// A. Nasri1, M. Sabin2 and Z. Yasseen1
//
#define isEven(n) (0 == (n) % 2)
#define isOdd(n) (!isEven(n))
RegionFiller::RegionFiller(const std::vector<Node> *vertices,
const std::vector<std::vector<size_t>> *polylines) :
m_sourceVertices(vertices),
m_sourcePolylines(new std::vector<std::vector<size_t>>(*polylines))
{
}
RegionFiller::~RegionFiller()
{
delete m_sourcePolylines;
}
bool RegionFiller::resolveEvenSidedEvenSumOfSegmentsAndBothL2L2AreEven()
{
return resolveOddSidedEvenSumOfSegments(m_sideNum / 2);
}
std::vector<size_t> RegionFiller::createPointsToMapBetween(size_t fromIndex,
size_t toIndex,
size_t segments,
std::map<std::pair<size_t, size_t>, std::vector<size_t>> *map)
{
size_t a = fromIndex;
size_t b = toIndex;
bool needReverse = false;
if (a > b) {
std::swap(a, b);
needReverse = true;
}
auto key = std::make_pair(a, b);
auto findPoints = map->find(key);
if (findPoints != map->end()) {
return needReverse ? reversed(findPoints->second) : findPoints->second;
}
std::vector<size_t> newPointIndices;
createPointsBetween(a, b, segments, &newPointIndices);
map->insert({key, newPointIndices});
return needReverse ? reversed(newPointIndices) : newPointIndices;
}
void RegionFiller::createPointsBetween(size_t fromIndex,
size_t toIndex,
size_t segments,
std::vector<size_t> *newPointIndices)
{
if (fromIndex == toIndex || 0 == segments)
return;
auto fromVertex = m_oldAndNewVertices[fromIndex];
auto toVertex = m_oldAndNewVertices[toIndex];
float stepFactor = 1.0 / segments;
newPointIndices->push_back(fromIndex);
for (size_t i = 1; i <= segments - 1; ++i) {
float factor = stepFactor * i;
RegionFiller::Node node;
node.position = fromVertex.position * (1.0 - factor) + toVertex.position * factor;
node.radius = fromVertex.radius * (1.0 - factor) + toVertex.radius * factor;
if (m_centerSources.find(toVertex.source) != m_centerSources.end()) {
node.source = fromVertex.source;
} else if (m_centerSources.find(fromVertex.source) != m_centerSources.end()) {
node.source = toVertex.source;
} else {
if (factor > 0.5) {
node.source = toVertex.source;
} else {
node.source = fromVertex.source;
}
}
size_t newIndex = m_oldAndNewVertices.size();
m_oldAndNewVertices.push_back(node);
newPointIndices->push_back(newIndex);
}
newPointIndices->push_back(toIndex);
}
std::vector<size_t> RegionFiller::createPointsBetween(size_t fromIndex,
size_t toIndex,
size_t segments)
{
std::vector<size_t> newPointIndices;
createPointsBetween(fromIndex, toIndex, segments, &newPointIndices);
return newPointIndices;
}
void RegionFiller::collectEdgePoints(size_t polyline, int startPos, int stopPos,
std::vector<size_t> *pointIndices)
{
const auto &edgeIndices = (*m_sourcePolylines)[polyline];
int step = startPos < stopPos ? 1 : -1;
int totalSteps = std::abs(startPos - stopPos) + 1;
int current = startPos;
for (int i = 0; i < totalSteps; ++i) {
pointIndices->push_back(edgeIndices[current]);
current += step;
}
}
std::vector<size_t> RegionFiller::collectEdgePoints(size_t polyline, int startPos, int stopPos)
{
std::vector<size_t> pointIndices;
collectEdgePoints(polyline, startPos, stopPos, &pointIndices);
return pointIndices;
}
std::vector<size_t> RegionFiller::reversed(const std::vector<size_t> &pointIndices)
{
std::vector<size_t> newPointIndices = pointIndices;
std::reverse(newPointIndices.begin(), newPointIndices.end());
return newPointIndices;
}
float RegionFiller::averageRadius(size_t *maxNodeIndex)
{
float sumOfRadius = 0;
size_t num = 0;
float maxRadius = 0;
for (const auto &polyline: *m_sourcePolylines) {
for (const auto &it: polyline) {
const auto &vertex = (*m_sourceVertices)[it];
if (vertex.radius >= maxRadius) {
maxRadius = vertex.radius;
if (nullptr != maxNodeIndex)
*maxNodeIndex = it;
}
sumOfRadius += vertex.radius;
++num;
}
}
if (0 == num)
return 0;
auto radius = sumOfRadius / num;
return radius;
}
bool RegionFiller::resolveOddSidedEvenSumOfSegments(int siUpperBound)
{
auto si = [&](int i) {
int sum = 0;
for (int j = 1; j <= siUpperBound; ++j) {
sum += std::pow(-1, j + 1) *
m_sideSegmentNums[(i + 2 * j - 1) % m_sideSegmentNums.size()];
}
return sum;
};
auto di = [&](int i) {
return std::ceil(si(i) * 0.5);
};
std::vector<int> gRaySegmentNums(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
gRaySegmentNums[i] = di(i);
if (gRaySegmentNums[i] <= 0) {
qDebug() << "resolveOddSidedEvenSumOfSegments failed, di:" << gRaySegmentNums[i];
return false;
}
}
std::vector<int> eIndices(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
auto length = gRaySegmentNums[(i + 1) % gRaySegmentNums.size()];
eIndices[i] = m_sideSegmentNums[i] - length;
}
std::set<size_t> cornerVertices;
std::vector<std::tuple<int, int, int>> edgeSegments; // tuple<begin, end, edgeIndex>
for (int i = 0; i < m_sideNum; ++i) {
if (gRaySegmentNums[i] <= 0) {
edgeSegments.push_back(std::make_tuple(0, (*m_sourcePolylines)[i].size() - 1, i));
continue;
}
const auto &pointEdgeIndex = eIndices[i];
cornerVertices.insert((*m_sourcePolylines)[i][pointEdgeIndex]);
if (pointEdgeIndex > 0)
edgeSegments.push_back(std::make_tuple(0, pointEdgeIndex, i));
if (pointEdgeIndex < (int)(*m_sourcePolylines)[i].size() - 1)
edgeSegments.push_back(std::make_tuple(pointEdgeIndex, (*m_sourcePolylines)[i].size() - 1, i));
}
std::vector<std::pair<QVector3D, float>> gPositionAndAreas;
for (size_t i = 0; i < edgeSegments.size(); ++i) {
size_t j = (i + 1) % edgeSegments.size();
size_t m = (j + 2) % edgeSegments.size();
const auto &segmentI = edgeSegments[i];
const auto &segmentJ = edgeSegments[j];
const auto &segmentM = edgeSegments[m];
std::vector<size_t> firstSegmentPoints;
std::vector<size_t> secondSegmentPoints;
std::vector<size_t> forthSegmentPoints;
collectEdgePoints(std::get<2>(segmentI), std::get<0>(segmentI), std::get<1>(segmentI),
&firstSegmentPoints);
collectEdgePoints(std::get<2>(segmentJ), std::get<0>(segmentJ), std::get<1>(segmentJ),
&secondSegmentPoints);
collectEdgePoints(std::get<2>(segmentM), std::get<0>(segmentM), std::get<1>(segmentM),
&forthSegmentPoints);
if (cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)]) != cornerVertices.end() &&
cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentI)][std::get<1>(segmentI)]) != cornerVertices.end()) {
const auto &p1 = (*m_sourceVertices)[(*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)]];
const auto &p2 = (*m_sourceVertices)[(*m_sourcePolylines)[std::get<2>(segmentI)][std::get<1>(segmentI)]];
gPositionAndAreas.push_back(std::make_pair((p1.position + p2.position) * 0.5,
(firstSegmentPoints.size() - 1) * (secondSegmentPoints.size() - 1) * 0.5));
continue;
}
if (cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)]) != cornerVertices.end() &&
cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentJ)][std::get<1>(segmentJ)]) != cornerVertices.end()) {
++i;
const auto &p1 = (*m_sourceVertices)[(*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)]];
const auto &p2 = (*m_sourceVertices)[(*m_sourcePolylines)[std::get<2>(segmentI)][std::get<1>(segmentI)]];
const auto &p3 = (*m_sourceVertices)[(*m_sourcePolylines)[std::get<2>(segmentJ)][std::get<1>(segmentJ)]];
gPositionAndAreas.push_back(std::make_pair(p1.position + p2.position - p3.position,
(firstSegmentPoints.size() - 1) * (secondSegmentPoints.size() - 1)));
}
}
QVector3D gTop;
float gBottom = 0;
for (const auto &it: gPositionAndAreas) {
gTop += it.first / it.second;
gBottom += 1.0 / it.second;
}
auto gPosition = gTop / gBottom;
size_t gIndex = m_oldAndNewVertices.size();
Node node;
node.position = gPosition;
node.radius = averageRadius(&node.source);
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
std::map<std::pair<size_t, size_t>, std::vector<size_t>> map;
for (size_t i = 0; i < edgeSegments.size(); ++i) {
size_t j = (i + 1) % edgeSegments.size();
size_t m = (j + 2) % edgeSegments.size();
const auto &segmentI = edgeSegments[i];
const auto &segmentJ = edgeSegments[j];
const auto &segmentM = edgeSegments[m];
std::vector<size_t> firstSegmentPoints;
std::vector<size_t> secondSegmentPoints;
std::vector<size_t> forthSegmentPoints;
collectEdgePoints(std::get<2>(segmentI), std::get<0>(segmentI), std::get<1>(segmentI),
&firstSegmentPoints);
collectEdgePoints(std::get<2>(segmentJ), std::get<0>(segmentJ), std::get<1>(segmentJ),
&secondSegmentPoints);
collectEdgePoints(std::get<2>(segmentM), std::get<0>(segmentM), std::get<1>(segmentM),
&forthSegmentPoints);
if (cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)]) != cornerVertices.end() &&
cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentI)][std::get<1>(segmentI)]) != cornerVertices.end()) {
std::vector<std::vector<size_t>> region = {
firstSegmentPoints,
createPointsToMapBetween((*m_sourcePolylines)[std::get<2>(segmentI)][std::get<1>(segmentI)],
gIndex, forthSegmentPoints.size() - 1, &map),
createPointsToMapBetween(gIndex,
(*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)], secondSegmentPoints.size() - 1, &map)
};
m_newRegions.push_back(region);
continue;
}
if (cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)]) != cornerVertices.end() &&
cornerVertices.find((*m_sourcePolylines)[std::get<2>(segmentJ)][std::get<1>(segmentJ)]) != cornerVertices.end()) {
++i;
std::vector<std::vector<size_t>> region = {
firstSegmentPoints,
secondSegmentPoints,
createPointsToMapBetween((*m_sourcePolylines)[std::get<2>(segmentJ)][std::get<1>(segmentJ)],
gIndex, firstSegmentPoints.size() - 1, &map),
createPointsToMapBetween(gIndex,
(*m_sourcePolylines)[std::get<2>(segmentI)][std::get<0>(segmentI)], secondSegmentPoints.size() - 1, &map)
};
m_newRegions.push_back(region);
}
}
return true;
}
bool RegionFiller::resolveEvenSidedEvenSumOfSegmentsAndBothL2L2AreOdd()
{
return resolveOddSidedOddSumOfSegments(m_sideNum / 2);
}
bool RegionFiller::resolveOddSidedOddSumOfSegments(int siUpperBound)
{
auto si = [&](int i) {
int sum = 0;
for (int j = 1; j <= siUpperBound; ++j) {
sum += std::pow(-1, j + 1) *
(m_sideSegmentNums[(i + 2 * j - 1) % m_sideSegmentNums.size()] - 1);
}
return sum;
};
auto di = [&](int i) {
return std::round(si(i) * 0.5);
};
std::vector<int> gRaySegmentNums(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
gRaySegmentNums[i] = di(i);
if (gRaySegmentNums[i] <= 0) {
qDebug() << "resolveOddSidedOddSumOfSegments failed, di:" << gRaySegmentNums[i];
return false;
}
}
std::vector<int> areas(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
areas[i] = gRaySegmentNums[i] * gRaySegmentNums[(i + 1) % m_sideNum];
}
std::vector<std::vector<int>> eIndices(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
auto length = gRaySegmentNums[(i + 1) % gRaySegmentNums.size()];
eIndices[i] = std::vector<int>{(int)m_sideSegmentNums[i] - length - 1,
(int)m_sideSegmentNums[i] - length
};
if (eIndices[i][0] < 0 || eIndices[i][1] < 0)
return false;
}
std::vector<float> eEdgeLengths(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
eEdgeLengths[i] = ((*m_sourceVertices)[(*m_sourcePolylines)[i][eIndices[i][0]]].position -
(*m_sourceVertices)[(*m_sourcePolylines)[i][eIndices[i][1]]].position).length();
}
float averageDislocationEdgeLength = std::accumulate(eEdgeLengths.begin(), eEdgeLengths.end(), 0.0) / eEdgeLengths.size();
auto gi = [&](int i) {
int j = (i + m_sideNum - 1) % m_sideNum;
return (*m_sourceVertices)[(*m_sourcePolylines)[i][eIndices[i][0]]].position +
(*m_sourceVertices)[(*m_sourcePolylines)[j][eIndices[j][1]]].position -
(*m_sourceVertices)[(*m_sourcePolylines)[i][0]].position;
};
auto gOffsetTargets = [&](int i) {
int h = (i + m_sideNum - 1) % m_sideNum;
return ((*m_sourceVertices)[(*m_sourcePolylines)[i][eIndices[i][0]]].position +
(*m_sourceVertices)[(*m_sourcePolylines)[h][eIndices[h][1]]].position) * 0.5;
};
QVector3D gTop;
float gBottom = 0;
for (int i = 0; i < m_sideNum; ++i) {
gTop += gi(i) / areas[i];
gBottom += 1.0 / areas[i];
}
auto gPosition = gTop / gBottom;
size_t gSource = 0;
auto gRadius = averageRadius(&gSource);
m_centerSources.insert(gSource);
std::vector<std::vector<std::vector<size_t>>> segmentsWithG(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
segmentsWithG[i].resize(2);
}
std::vector<size_t> gFace(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
int j = (i + 1) % m_sideNum;
auto offsetTarget = gOffsetTargets(j);
auto gPositionPerEdge = gPosition + (offsetTarget - gPosition).normalized() * averageDislocationEdgeLength * 0.7;
size_t gIndex = m_oldAndNewVertices.size();
gFace[j] = gIndex;
Node node;
node.position = gPositionPerEdge;
node.radius = gRadius;
node.source = gSource;
m_oldAndNewVertices.push_back(node);
createPointsBetween(gIndex, (*m_sourcePolylines)[i][eIndices[i][1]], gRaySegmentNums[i], &segmentsWithG[i][1]);
createPointsBetween(gIndex, (*m_sourcePolylines)[j][eIndices[j][0]], gRaySegmentNums[j], &segmentsWithG[j][0]);
}
m_newFaces.push_back(gFace);
std::vector<std::vector<std::vector<size_t>>> reversedSegmentsWithG(m_sideNum);
reversedSegmentsWithG = segmentsWithG;
for (int i = 0; i < m_sideNum; ++i) {
for (int x = 0; x < 2; ++x)
std::reverse(reversedSegmentsWithG[i][x].begin(), reversedSegmentsWithG[i][x].end());
}
for (int i = 0; i < m_sideNum; ++i) {
int j = (i + 1) % m_sideNum;
std::vector<size_t> e0c1;
collectEdgePoints(i, eIndices[i][1], (*m_sourcePolylines)[i].size() - 1, &e0c1);
std::vector<size_t> c1e1;
collectEdgePoints(j, 0, eIndices[j][0], &c1e1);
std::vector<std::vector<size_t>> region = {
e0c1,
c1e1,
reversedSegmentsWithG[j][0],
segmentsWithG[i][1]
};
m_newRegions.push_back(region);
}
// rectangle slices
for (int i = 0; i < m_sideNum; ++i) {
int j = (i + 1) % m_sideNum;
std::vector<size_t> e1e2 = {
(*m_sourcePolylines)[i][eIndices[i][0]],
(*m_sourcePolylines)[i][eIndices[i][1]]
};
std::vector<size_t> gCoreEdge = {
gFace[j],
gFace[i]
};
std::vector<std::vector<size_t>> region = {
e1e2,
reversedSegmentsWithG[i][1],
gCoreEdge,
segmentsWithG[i][0],
};
m_newRegions.push_back(region);
}
return true;
}
bool RegionFiller::resolveEvenSideEvenSumOfSegmentsAndL1IsOddL2IsEven()
{
std::rotate(m_sourcePolylines->begin(), m_sourcePolylines->begin() + 1, m_sourcePolylines->end());
prepare();
return resolveEvenSideEvenSumOfSegmentsAndL1IsEvenL2IsOdd();
}
bool RegionFiller::resolveEvenSideEvenSumOfSegmentsAndL1IsEvenL2IsOdd()
{
auto modifiedSideSegmentNums = m_sideSegmentNums;
for (int i = 0; i < m_sideNum; i += 2) {
modifiedSideSegmentNums[i] -= 1;
}
auto si = [&](int i) {
int sum = 0;
int halfSideNum = m_sideNum / 2;
for (int j = 1; j <= halfSideNum; ++j) {
sum += std::pow(-1, j + 1) *
(modifiedSideSegmentNums[(i + 2 * j - 1) % m_sideSegmentNums.size()]);
}
return sum;
};
auto di = [&](int i) {
return std::round(si(i) * 0.5);
};
std::vector<int> gRaySegmentNums(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
gRaySegmentNums[i] = di(i);
if (gRaySegmentNums[i] <= 0) {
qDebug() << "resolveEvenSideEvenSumOfSegmentsAndL1IsEvenL2IsOdd failed, di:" << gRaySegmentNums[i];
return false;
}
}
std::vector<int> areas(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
areas[i] = gRaySegmentNums[i] * gRaySegmentNums[(i + 1) % m_sideNum];
}
std::vector<std::vector<int>> eIndices(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
auto length = gRaySegmentNums[(i + gRaySegmentNums.size() - 1) % gRaySegmentNums.size()];
if (0 == i % 2) {
eIndices[i] = std::vector<int>{(int)length,
(int)length + 1
};
} else {
eIndices[i] = std::vector<int>{(int)length,
(int)length
};
}
if (eIndices[i][0] < 0 || eIndices[i][1] < 0) {
qDebug() << "resolveEvenSideEvenSumOfSegmentsAndL1IsEvenL2IsOdd failed";
return false;
}
}
std::vector<float> eEdgeLengths(m_sideNum, (float)0.0);
int validEdgeNum = 0;
for (int i = 0; i < m_sideNum; ++i) {
const auto &firstIndex = eIndices[i][0];
const auto &secondIndex = eIndices[i][1];
if (firstIndex == secondIndex)
continue;
validEdgeNum++;
eEdgeLengths[i] = ((*m_sourceVertices)[(*m_sourcePolylines)[i][firstIndex]].position -
(*m_sourceVertices)[(*m_sourcePolylines)[i][secondIndex]].position).length();
}
float averageDislocationEdgeLength = std::accumulate(eEdgeLengths.begin(), eEdgeLengths.end(), 0.0) / validEdgeNum;
auto gi = [&](int i) {
int h = (i + m_sideNum - 1) % m_sideNum;
return (*m_sourceVertices)[(*m_sourcePolylines)[i][eIndices[i][0]]].position +
(*m_sourceVertices)[(*m_sourcePolylines)[h][eIndices[h][1]]].position -
(*m_sourceVertices)[(*m_sourcePolylines)[i][0]].position;
};
auto gOffsetTargets = [&](int i) {
int h = (i + m_sideNum - 1) % m_sideNum;
return ((*m_sourceVertices)[(*m_sourcePolylines)[i][eIndices[i][0]]].position +
(*m_sourceVertices)[(*m_sourcePolylines)[h][eIndices[h][1]]].position) * 0.5;
};
QVector3D gTop;
float gBottom = 0;
for (int i = 0; i < m_sideNum; ++i) {
gTop += gi(i) / areas[i];
gBottom += 1.0 / areas[i];
}
auto gPosition = gTop / gBottom;
size_t gSource = 0;
auto gRadius = averageRadius(&gSource);
std::vector<size_t> gFace(m_sideNum / 2);
for (size_t k = 0; k < gFace.size(); ++k) {
int i = k * 2;
int h = (i + m_sideNum - 1) % m_sideNum;
auto offsetTarget = gOffsetTargets(i) * 0.5 + gOffsetTargets(h) * 0.5;
size_t gIndex = m_oldAndNewVertices.size();
auto gPositionPerEdge = gPosition + (offsetTarget - gPosition).normalized() * averageDislocationEdgeLength * 0.7;
Node node;
node.position = gPositionPerEdge;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
gFace[k] = gIndex;
}
m_newFaces.push_back(gFace);
std::vector<std::vector<std::vector<size_t>>> segmentsWithG(m_sideNum);
for (int i = 0; i < m_sideNum; ++i) {
segmentsWithG[i].resize(2);
}
for (int i = 0; i < m_sideNum; ++i) {
int k = i / 2;
const auto &g0Index = gFace[k % gFace.size()];
const auto &g1Index = gFace[(k + 1) % gFace.size()];
if (eIndices[i][0] == eIndices[i][1]) {
createPointsBetween(g1Index, (*m_sourcePolylines)[i][eIndices[i][0]], gRaySegmentNums[i],
&segmentsWithG[i][0]);
segmentsWithG[i][1] = segmentsWithG[i][0];
} else {
createPointsBetween(g0Index, (*m_sourcePolylines)[i][eIndices[i][0]], gRaySegmentNums[i],
&segmentsWithG[i][0]);
createPointsBetween(g1Index, (*m_sourcePolylines)[i][eIndices[i][1]], gRaySegmentNums[i],
&segmentsWithG[i][1]);
}
}
std::vector<std::vector<std::vector<size_t>>> reversedSegmentsWithG(m_sideNum);
reversedSegmentsWithG = segmentsWithG;
for (int i = 0; i < m_sideNum; ++i) {
for (int x = 0; x < 2; ++x)
std::reverse(reversedSegmentsWithG[i][x].begin(), reversedSegmentsWithG[i][x].end());
}
for (int i = 0; i < m_sideNum; ++i) {
int j = (i + 1) % m_sideNum;
std::vector<size_t> e0c1;
collectEdgePoints(i, eIndices[i][1], (*m_sourcePolylines)[i].size() - 1, &e0c1);
std::vector<size_t> c1e1;
collectEdgePoints(j, 0, eIndices[j][0], &c1e1);
std::vector<std::vector<size_t>> region = {
e0c1, c1e1, reversedSegmentsWithG[j][0], segmentsWithG[i][1]
};
m_newRegions.push_back(region);
}
// Rectangle slices
for (size_t k = 0; k < gFace.size(); ++k) {
int i = k * 2;
std::vector<size_t> e1e2 = {
(*m_sourcePolylines)[i][eIndices[i][0]],
(*m_sourcePolylines)[i][eIndices[i][1]]
};
std::vector<size_t> g1g0 = {
gFace[(k + 1) % gFace.size()],
gFace[k % gFace.size()]
};
std::vector<std::vector<size_t>> region = {
g1g0, segmentsWithG[i][0], e1e2, reversedSegmentsWithG[i][1]
};
m_newRegions.push_back(region);
}
return true;
}
bool RegionFiller::resolveQuadrilateralRegionMismatchSimilarCase(int m, int n, int p, int q, bool pqSwapped)
{
return resolveQuadrilateralRegionWithIntegerSolution(m, n, p, q, pqSwapped);
}
bool RegionFiller::resolveQuadrilateralRegionWithNonIntegerSolution(int m, int n, int p, int q, bool pqSwapped)
{
float aPlusB = m_sideSegmentNums[m] - m_sideSegmentNums[n];
float aFloat = ((m_sideSegmentNums[m] - m_sideSegmentNums[n]) + (m_sideSegmentNums[p] - m_sideSegmentNums[q])) / 2.0;
float bFloat = (m_sideSegmentNums[m] - m_sideSegmentNums[n]) - aFloat;
float a = std::ceil(aFloat);
float b = std::ceil(bFloat);
int c0EdgeIndex = pqSwapped ? (*m_sourcePolylines)[m].size() - 1 : 0;
int c1EdgeIndex = pqSwapped ? (*m_sourcePolylines)[p].size() - 1 : 0;
int c2EdgeIndex = pqSwapped ? (*m_sourcePolylines)[n].size() - 1 : 0;
int c3EdgeIndex = pqSwapped ? (*m_sourcePolylines)[q].size() - 1 : 0;
int c0AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[q].size() - 1;
int c1AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[m].size() - 1;
int c2AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[p].size() - 1;
int c3AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[n].size() - 1;
const auto &c0Index = (*m_sourcePolylines)[m][c0EdgeIndex];
const auto &c1Index = (*m_sourcePolylines)[p][c1EdgeIndex];
const auto &c0 = (*m_sourceVertices)[c0Index].position;
const auto &c1 = (*m_sourceVertices)[c1Index].position;
int f0 = m_sideSegmentNums[n] / 2;
int f1 = m_sideSegmentNums[n] - f0;
int f2 = std::ceil((m_sideSegmentNums[p] - a) / 2.0);
int f3 = m_sideSegmentNums[p] - a - f2;
if (f3 < 0) {
qDebug() << "resolveQuadrilateralRegionWithNonIntegerSolution failed, f3:" << f3;
return false;
}
int e0EdgeIndex = f0;
if (pqSwapped)
e0EdgeIndex = (*m_sourcePolylines)[m].size() - 1 - e0EdgeIndex;
const auto &e0Index = (*m_sourcePolylines)[m][e0EdgeIndex];
int e2EdgeIndex = f0 + aPlusB;
if (pqSwapped)
e2EdgeIndex = (*m_sourcePolylines)[m].size() - 1 - e2EdgeIndex;
const auto &e2Index = (*m_sourcePolylines)[m][e2EdgeIndex];
int e3EdgeIndex = f2;
if (pqSwapped)
e3EdgeIndex = (*m_sourcePolylines)[p].size() - 1 - e3EdgeIndex;
const auto &e3Index = (*m_sourcePolylines)[p][e3EdgeIndex];
int e4EdgeIndex = (*m_sourcePolylines)[p].size() - 1 - f3;
if (pqSwapped)
e4EdgeIndex = (*m_sourcePolylines)[p].size() - 1 - e4EdgeIndex;
const auto &e4Index = (*m_sourcePolylines)[p][e4EdgeIndex];
int e5EdgeIndex = f3;
if (pqSwapped)
e5EdgeIndex = (*m_sourcePolylines)[q].size() - 1 - e5EdgeIndex;
const auto &e5Index = (*m_sourcePolylines)[q][e5EdgeIndex];
int e6EdgeIndex = (*m_sourcePolylines)[q].size() - 1 - f2;
if (pqSwapped)
e6EdgeIndex = (*m_sourcePolylines)[q].size() - 1 - e6EdgeIndex;
const auto &e6Index = (*m_sourcePolylines)[q][e6EdgeIndex];
int e7EdgeIndex = f1;
if (pqSwapped)
e7EdgeIndex = (*m_sourcePolylines)[n].size() - 1 - e7EdgeIndex;
const auto &e7Index = (*m_sourcePolylines)[n][e7EdgeIndex];
const auto &e0 = (*m_sourceVertices)[e0Index].position;
const auto &e2 = (*m_sourceVertices)[e2Index].position;
const auto &e3 = (*m_sourceVertices)[e3Index].position;
const auto &e4 = (*m_sourceVertices)[e4Index].position;
const auto &e5 = (*m_sourceVertices)[e5Index].position;
const auto &e6 = (*m_sourceVertices)[e6Index].position;
const auto &e7 = (*m_sourceVertices)[e7Index].position;
auto g1InitialPosition = e0 + e6 - c0;
auto g2InitialPosition = e2 + e3 - c1;
auto gpInitialPosition1 = (e5 + e4) * 0.5;
auto gpInitialPosition2 = gpInitialPosition1;
auto g1a = f0 * f2;
auto g2a = f1 * f2;
auto gp1a = f0 * f3 + f0 * b;
auto gp2a = f1 * f3 + f1 * a;
float weightG1 = 1.0 / g1a;
float weightG2 = 1.0 / g2a;
float weightGp1 = 1.0 / gp1a;
float weightGp2 = 1.0 / gp2a;
size_t gSource = 0;
auto gRadius = averageRadius(&gSource);
auto gPosition = (g1InitialPosition * weightG1 +
g2InitialPosition * weightG2 +
gpInitialPosition1 * weightGp1 +
gpInitialPosition2 * weightGp2) /
(weightG1 + weightG2 + weightGp1 + weightGp2);
auto averageOffsetDistance = e0.distanceToPoint(e2);
auto g1Position = gPosition + (((e0 + e6) * 0.5) - gPosition).normalized() * averageOffsetDistance * 0.7;
auto g2Position = gPosition + (((e2 + e3) * 0.5) - gPosition).normalized() * averageOffsetDistance * 0.7;
auto gpPosition = gPosition + (e7 - gPosition).normalized() * averageOffsetDistance * 0.7;
auto g1Index = m_oldAndNewVertices.size();
{
Node node;
node.position = g1Position;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
}
auto g2Index = m_oldAndNewVertices.size();
{
Node node;
node.position = g2Position;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
}
auto gpIndex = m_oldAndNewVertices.size();
if (0 != f3) {
Node node;
node.position = gpPosition;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
} else {
gpIndex = e7Index;
}
std::map<std::pair<size_t, size_t>, std::vector<size_t>> map;
{
std::vector<std::vector<size_t>> region = {
collectEdgePoints(m, c0EdgeIndex, e0EdgeIndex),
createPointsToMapBetween(e0Index, g1Index, f2, &map),
createPointsToMapBetween(g1Index, e6Index, f0, &map),
collectEdgePoints(q, e6EdgeIndex, c0AlternativeEdgeIndex)
};
m_newRegions.push_back(region);
}
{
std::vector<std::vector<size_t>> region = {
collectEdgePoints(m, e0EdgeIndex, e2EdgeIndex),
createPointsToMapBetween(e2Index, g2Index, f2, &map),
createPointsToMapBetween(g2Index, g1Index, aPlusB, &map),
createPointsToMapBetween(g1Index, e0Index, f2, &map)
};
m_newRegions.push_back(region);
}
{
std::vector<std::vector<size_t>> region = {
collectEdgePoints(m, e2EdgeIndex, c1AlternativeEdgeIndex),
collectEdgePoints(p, c1EdgeIndex, e3EdgeIndex),
createPointsToMapBetween(e3Index, g2Index, f1, &map),
createPointsToMapBetween(g2Index, e2Index, f2, &map)
};
m_newRegions.push_back(region);
}
if (gpIndex != e7Index) {
std::vector<std::vector<size_t>> region = {
createPointsToMapBetween(g2Index, e3Index, f1, &map),
collectEdgePoints(p, e3EdgeIndex, e4EdgeIndex),
createPointsToMapBetween(e4Index, gpIndex, f1, &map),
createPointsToMapBetween(gpIndex, g2Index, a, &map)
};
m_newRegions.push_back(region);
} else {
std::vector<std::vector<size_t>> region = {
createPointsToMapBetween(g2Index, e3Index, f1, &map),
collectEdgePoints(p, e3EdgeIndex, c2AlternativeEdgeIndex),
collectEdgePoints(n, c2EdgeIndex, e7EdgeIndex),
createPointsToMapBetween(gpIndex, g2Index, a, &map)
};
m_newRegions.push_back(region);
}
if (gpIndex != e7Index) {
std::vector<std::vector<size_t>> region = {
collectEdgePoints(p, e4EdgeIndex, c2AlternativeEdgeIndex),
collectEdgePoints(n, c2EdgeIndex, e7EdgeIndex),
createPointsToMapBetween(e7Index, gpIndex, f3, &map),
createPointsToMapBetween(gpIndex, e4Index, f1, &map)
};
m_newRegions.push_back(region);
}
if (gpIndex != e7Index) {
std::vector<std::vector<size_t>> region = {
createPointsToMapBetween(gpIndex, e7Index, f3, &map),
collectEdgePoints(n, e7EdgeIndex, c3AlternativeEdgeIndex),
collectEdgePoints(q, c3EdgeIndex, e5EdgeIndex),
createPointsToMapBetween(e5Index, gpIndex, f0, &map)
};
m_newRegions.push_back(region);
}
if (gpIndex != e7Index) {
std::vector<std::vector<size_t>> region = {
createPointsToMapBetween(g1Index, gpIndex, b, &map),
createPointsToMapBetween(gpIndex, e5Index, f0, &map),
collectEdgePoints(q, e5EdgeIndex, e6EdgeIndex),
createPointsToMapBetween(e6Index, g1Index, f0, &map)
};
m_newRegions.push_back(region);
} else {
std::vector<std::vector<size_t>> region = {
createPointsToMapBetween(g1Index, gpIndex, b, &map),
collectEdgePoints(n, e7EdgeIndex, c3AlternativeEdgeIndex),
collectEdgePoints(q, c3EdgeIndex, e6EdgeIndex),
createPointsToMapBetween(e6Index, g1Index, f0, &map)
};
m_newRegions.push_back(region);
}
{
std::vector<std::vector<size_t>> region = {
createPointsToMapBetween(g1Index, g2Index, aPlusB, &map),
createPointsToMapBetween(g2Index, gpIndex, a, &map),
createPointsToMapBetween(gpIndex, g1Index, b, &map)
};
m_newRegions.push_back(region);
}
return true;
}
bool RegionFiller::resolveQuadrilateralRegionWithIntegerSolution(int m, int n, int p, int q, bool pqSwapped)
{
// a + b = m n
// a b = p q
// (a + b) + (a - b) = (m - n) + (p - q)
// a = ((m - n) + (p - q)) / 2
int a = ((m_sideSegmentNums[m] - m_sideSegmentNums[n]) + (m_sideSegmentNums[p] - m_sideSegmentNums[q])) / 2;
int b = (m_sideSegmentNums[m] - m_sideSegmentNums[n]) - a;
bool isMismatchSimilar = 0 == b;
int f0 = m_sideSegmentNums[n] / 2;
int f1 = m_sideSegmentNums[n] - f0;
int f2 = (m_sideSegmentNums[q] - b) / 2;
int f3 = m_sideSegmentNums[q] - b - f2;
if (f3 < 0) {
qDebug() << "resolveQuadrilateralRegionWithIntegerSolution failed, f3:" << f3;
return false;
}
float a1 = f0 * f2;
float a2 = f0 * b;
float a3 = f0 * f3;
float a4 = f1 * f2;
float a5 = f1 * a;
float a6 = f1 * f3;
float a7 = a * f2;
float a8 = b * f2;
float a9 = a * b;
int c0EdgeIndex = pqSwapped ? (*m_sourcePolylines)[m].size() - 1 : 0;
int c1EdgeIndex = pqSwapped ? (*m_sourcePolylines)[p].size() - 1 : 0;
int c2EdgeIndex = pqSwapped ? (*m_sourcePolylines)[n].size() - 1 : 0;
int c3EdgeIndex = pqSwapped ? (*m_sourcePolylines)[q].size() - 1 : 0;
int c0AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[q].size() - 1;
int c1AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[m].size() - 1;
int c2AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[p].size() - 1;
int c3AlternativeEdgeIndex = pqSwapped ? 0 : (*m_sourcePolylines)[n].size() - 1;
const auto &c0Index = (*m_sourcePolylines)[m][c0EdgeIndex];
const auto &c1Index = (*m_sourcePolylines)[p][c1EdgeIndex];
const auto &c2Index = (*m_sourcePolylines)[n][c2EdgeIndex];
const auto &c3Index = (*m_sourcePolylines)[q][c3EdgeIndex];
const auto &c0 = (*m_sourceVertices)[c0Index].position;
const auto &c1 = (*m_sourceVertices)[c1Index].position;
const auto &c2 = (*m_sourceVertices)[c2Index].position;
const auto &c3 = (*m_sourceVertices)[c3Index].position;
int e0EdgeIndex = f0;
if (pqSwapped)
e0EdgeIndex = (*m_sourcePolylines)[m].size() - 1 - e0EdgeIndex;
const auto &e0Index = (*m_sourcePolylines)[m][e0EdgeIndex];
int e1EdgeIndex = f0 + a;
if (pqSwapped)
e1EdgeIndex = (*m_sourcePolylines)[m].size() - 1 - e1EdgeIndex;
const auto &e1Index = (*m_sourcePolylines)[m][e1EdgeIndex];
int e2EdgeIndex = f0 + a + b;
if (pqSwapped)
e2EdgeIndex = (*m_sourcePolylines)[m].size() - 1 - e2EdgeIndex;
const auto &e2Index = (*m_sourcePolylines)[m][e2EdgeIndex];
int e3EdgeIndex = f2;
if (pqSwapped)
e3EdgeIndex = (*m_sourcePolylines)[p].size() - 1 - e3EdgeIndex;
const auto &e3Index = (*m_sourcePolylines)[p][e3EdgeIndex];
int e4EdgeIndex = f2 + a;
if (pqSwapped)
e4EdgeIndex = (*m_sourcePolylines)[p].size() - 1 - e4EdgeIndex;
const auto &e4Index = (*m_sourcePolylines)[p][e4EdgeIndex];
int e5EdgeIndex = f3;
if (pqSwapped)
e5EdgeIndex = (*m_sourcePolylines)[q].size() - 1 - e5EdgeIndex;
const auto &e5Index = (*m_sourcePolylines)[q][e5EdgeIndex];
int e6EdgeIndex = f3 + b;
if (pqSwapped)
e6EdgeIndex = (*m_sourcePolylines)[q].size() - 1 - e6EdgeIndex;
const auto &e6Index = (*m_sourcePolylines)[q][e6EdgeIndex];
int e7EdgeIndex = f1;
if (pqSwapped)
e7EdgeIndex = (*m_sourcePolylines)[n].size() - 1 - e7EdgeIndex;
const auto &e7Index = (*m_sourcePolylines)[n][e7EdgeIndex];
const auto &e0 = (*m_sourceVertices)[e0Index].position;
const auto &e1 = (*m_sourceVertices)[e1Index].position;
const auto &e2 = (*m_sourceVertices)[e2Index].position;
const auto &e3 = (*m_sourceVertices)[e3Index].position;
const auto &e4 = (*m_sourceVertices)[e4Index].position;
const auto &e5 = (*m_sourceVertices)[e5Index].position;
const auto &e6 = (*m_sourceVertices)[e6Index].position;
const auto &e7 = (*m_sourceVertices)[e7Index].position;
auto g1 = e1 + e5 - c0;
auto gn = e1 + e4 - c1;
auto g2 = e4 + e7 - c2;
auto gp = e5 + e7 - c3;
auto weight1 = 1.0 / (a1 + a2 + a7 + a9);
auto weightn = 1.0 / (a4 + a5 + a8 + a9);
auto weight2 = 1.0 / (a6);
auto weightp = 1.0 / (a3);
auto gPosition = (g1 * weight1 + gn * weightn + g2 * weight2 + gp * weightp) /
(weight1 + weightn + weight2 + weightp);
size_t gSource = 0;
auto gRadius = averageRadius(&gSource);
auto averageOffsetDistance = e0.distanceToPoint(e2);
if (!isMismatchSimilar)
averageOffsetDistance *= 0.5;
auto g1Position = gPosition + (e6 - gPosition).normalized() * averageOffsetDistance * 0.7;
auto gnPosition = gPosition + (e1 - gPosition).normalized() * averageOffsetDistance * 0.7;
auto g2Position = gPosition + (e3 - gPosition).normalized() * averageOffsetDistance * 0.7;
auto gpPosition = gPosition + (e7 - gPosition).normalized() * averageOffsetDistance * 0.7;
if (isMismatchSimilar) {
gpPosition = ((*m_sourceVertices)[e0Index].position +
(*m_sourceVertices)[e5Index].position +
(*m_sourceVertices)[e7Index].position +
(*m_sourceVertices)[e4Index].position) / 4.0;
gnPosition = ((*m_sourceVertices)[e0Index].position +
gpPosition +
(*m_sourceVertices)[e3Index].position +
(*m_sourceVertices)[c1Index].position) / 4.0;
}
size_t g1Index = m_oldAndNewVertices.size();
if (!isMismatchSimilar) {
Node node;
node.position = g1Position;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
}
size_t gnIndex = m_oldAndNewVertices.size();
{
Node node;
node.position = gnPosition;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
}
size_t g2Index = m_oldAndNewVertices.size();
if (!isMismatchSimilar) {
Node node;
node.position = g2Position;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
}
size_t gpIndex = m_oldAndNewVertices.size();
{
Node node;
node.position = gpPosition;
node.radius = gRadius;
node.source = gSource;
m_centerSources.insert(node.source);
m_oldAndNewVertices.push_back(node);
}
if (isMismatchSimilar) {
g1Index = gpIndex;
g2Index = gnIndex;
}
std::map<std::pair<size_t, size_t>, std::vector<size_t>> map;
std::vector<std::vector<size_t>> region1 = {
collectEdgePoints(m, c0EdgeIndex, e0EdgeIndex),
createPointsToMapBetween(e0Index, g1Index, f2, &map),
createPointsToMapBetween(g1Index, e6Index, f0, &map),
collectEdgePoints(q, e6EdgeIndex, c0AlternativeEdgeIndex)
};
m_newRegions.push_back(region1);
if (e5Index != e6Index) {
std::vector<std::vector<size_t>> region2 = {
createPointsToMapBetween(e6Index, g1Index, f0, &map),
createPointsToMapBetween(g1Index, gpIndex, b, &map),
createPointsToMapBetween(gpIndex, e5Index, f0, &map),
collectEdgePoints(q, e5EdgeIndex, e6EdgeIndex)
};
m_newRegions.push_back(region2);
}
std::vector<std::vector<size_t>> region3 = {
createPointsToMapBetween(e5Index, gpIndex, f0, &map),
createPointsToMapBetween(gpIndex, e7Index, f3, &map),
collectEdgePoints(n, e7EdgeIndex, c3AlternativeEdgeIndex),
collectEdgePoints(q, c3EdgeIndex, e5EdgeIndex)
};
m_newRegions.push_back(region3);
std::vector<std::vector<size_t>> region4 = {
collectEdgePoints(m, e2EdgeIndex, c1AlternativeEdgeIndex),
collectEdgePoints(p, c1EdgeIndex, e3EdgeIndex),
createPointsToMapBetween(e3Index, g2Index, f1, &map),
createPointsToMapBetween(g2Index, e2Index, f2, &map)
};
m_newRegions.push_back(region4);
if (e3Index != e4Index) {
std::vector<std::vector<size_t>> region5 = {
createPointsToMapBetween(g2Index, e3Index, f1, &map),
collectEdgePoints(p, e3EdgeIndex, e4EdgeIndex),
createPointsToMapBetween(e4Index, gpIndex, f1, &map),
createPointsToMapBetween(gpIndex, g2Index, a, &map)
};
m_newRegions.push_back(region5);
}
std::vector<std::vector<size_t>> region6 = {
createPointsToMapBetween(gpIndex, e4Index, f1, &map),
collectEdgePoints(p, e4EdgeIndex, c2AlternativeEdgeIndex),
collectEdgePoints(n, c2EdgeIndex, e7EdgeIndex),
createPointsToMapBetween(e7Index, gpIndex, f3, &map)
};
m_newRegions.push_back(region6);
if (g1Index != gnIndex && e0Index != e1Index) {
std::vector<std::vector<size_t>> region7 = {
collectEdgePoints(m, e0EdgeIndex, e1EdgeIndex),
createPointsToMapBetween(e1Index, gnIndex, f2, &map),
createPointsToMapBetween(gnIndex, g1Index, a, &map),
createPointsToMapBetween(g1Index, e0Index, f2, &map)
};
m_newRegions.push_back(region7);
}
if (g2Index != gnIndex && e1Index != e2Index) {
std::vector<std::vector<size_t>> region8 = {
collectEdgePoints(m, e1EdgeIndex, e2EdgeIndex),
createPointsToMapBetween(e2Index, g2Index, f2, &map),
createPointsToMapBetween(g2Index, gnIndex, b, &map),
createPointsToMapBetween(gnIndex, e1Index, f2, &map)
};
m_newRegions.push_back(region8);
}
if (!isMismatchSimilar) {
std::vector<std::vector<size_t>> region9 = {
createPointsToMapBetween(g1Index, gnIndex, a, &map),
createPointsToMapBetween(gnIndex, g2Index, b, &map),
createPointsToMapBetween(g2Index, gpIndex, a, &map),
createPointsToMapBetween(gpIndex, g1Index, b, &map)
};
m_newRegions.push_back(region9);
}
return true;
}
bool RegionFiller::resolveQuadrilateralRegion()
{
int diff02 = std::abs(m_sideSegmentNums[0] - m_sideSegmentNums[2]);
int diff13 = std::abs(m_sideSegmentNums[1] - m_sideSegmentNums[3]);
int m, n, p, q;
if (diff02 >= diff13) {
if (m_sideSegmentNums[0] > m_sideSegmentNums[2]) {
m = 0;
n = 2;
p = 1;
q = 3;
} else {
m = 2;
n = 0;
p = 3;
q = 1;
}
} else {
if (m_sideSegmentNums[1] > m_sideSegmentNums[3]) {
m = 1;
n = 3;
p = 2;
q = 0;
} else {
m = 3;
n = 1;
p = 0;
q = 2;
}
}
bool pqSwapped = false;
if (m_sideSegmentNums[p] < m_sideSegmentNums[q]) {
std::swap(p, q);
pqSwapped = true;
}
if (diff02 != diff13) {
if (isEven(m_sumOfSegments)) {
return resolveQuadrilateralRegionWithIntegerSolution(m, n, p, q, pqSwapped);
} else {
return resolveQuadrilateralRegionWithNonIntegerSolution(m, n, p, q, pqSwapped);
}
} else {
return resolveQuadrilateralRegionMismatchSimilarCase(m, n, p, q, pqSwapped);
}
}
const std::vector<RegionFiller::Node> &RegionFiller::getOldAndNewVertices()
{
return m_oldAndNewVertices;
}
const std::vector<std::vector<size_t>> &RegionFiller::getNewFaces()
{
return m_newFaces;
}
void RegionFiller::prepare()
{
m_sideNum = m_sourcePolylines->size();
m_sideSegmentNums.clear();
for (const auto &it: *m_sourcePolylines) {
auto l = (int)it.size() - 1;
m_sideSegmentNums.push_back(l);
}
m_sumOfSegments = std::accumulate(m_sideSegmentNums.begin(), m_sideSegmentNums.end(), 0);
}
bool RegionFiller::resolveQuadrilateralRegionDirectCase()
{
std::vector<std::vector<size_t>> region = {
collectEdgePoints(0, 0, (*m_sourcePolylines)[0].size() - 1),
collectEdgePoints(1, 0, (*m_sourcePolylines)[1].size() - 1),
collectEdgePoints(2, 0, (*m_sourcePolylines)[2].size() - 1),
collectEdgePoints(3, 0, (*m_sourcePolylines)[3].size() - 1)
};
m_newRegions.push_back(region);
return true;
}
void RegionFiller::fillWithoutPartition()
{
m_oldAndNewVertices = *m_sourceVertices;
m_newFaces.clear();
convertPolylinesToFaces();
}
bool RegionFiller::fill()
{
m_oldAndNewVertices = *m_sourceVertices;
prepare();
if (4 == m_sideNum) {
if (m_sideSegmentNums[0] == m_sideSegmentNums[2] &&
m_sideSegmentNums[1] == m_sideSegmentNums[3]) {
if (!resolveQuadrilateralRegionDirectCase()) {
qDebug() << "resolveQuadrilateralRegionDirectCase failed";
return false;
}
} else {
if (!resolveQuadrilateralRegion()) {
qDebug() << "resolveQuadrilateralRegion failed";
return false;
}
}
} else {
if (isOdd(m_sideNum)) {
if (isEven(m_sumOfSegments)) {
if (!resolveOddSidedEvenSumOfSegments(m_sideNum)) {
qDebug() << "resolveOddSidedEvenSumOfSegments failed, m_sideNum:" << m_sideNum;
return false;
}
} else {
if (!resolveOddSidedOddSumOfSegments(m_sideNum)) {
qDebug() << "resolveOddSidedOddSumOfSegments failed, m_sideNum:" << m_sideNum;
return false;
}
}
} else {
int l1 = 0;
int l2 = 0;
int halfSideNum = m_sideNum / 2;
for (int i = 1; i <= halfSideNum; ++i) {
l1 += m_sideSegmentNums[(i * 2 - 1) % m_sideNum];
l2 += m_sideSegmentNums[(i * 2 - 1 - 1) % m_sideNum];
}
if (isEven(l1) && isEven(l2)) {
if (!resolveEvenSidedEvenSumOfSegmentsAndBothL2L2AreEven()) {
qDebug() << "resolveEvenSidedEvenSumOfSegmentsAndBothL2L2AreEven failed";
return false;
}
} else if (isOdd(l1) && isOdd(l2)) {
if (!resolveEvenSidedEvenSumOfSegmentsAndBothL2L2AreOdd()) {
qDebug() << "resolveEvenSidedEvenSumOfSegmentsAndBothL2L2AreOdd failed";
return false;
}
} else if (isEven(l1) && isOdd(l2)) {
if (!resolveEvenSideEvenSumOfSegmentsAndL1IsEvenL2IsOdd()) {
qDebug() << "resolveEvenSideEvenSumOfSegmentsAndL1IsEvenL2IsOdd failed";
return false;
}
} else {
if (!resolveEvenSideEvenSumOfSegmentsAndL1IsOddL2IsEven()) {
qDebug() << "resolveEvenSideEvenSumOfSegmentsAndL1IsOddL2IsEven failed";
return false;
}
}
}
}
if (!convertRegionsToPatches()) {
qDebug() << "convertRegionsToPatches failed";
return false;
}
return true;
}
void RegionFiller::convertRegionsToFaces()
{
if (m_newRegions.empty()) {
qDebug() << "No region, should not happen";
return;
}
for (const auto &region: m_newRegions) {
std::vector<size_t> face;
for (const auto &line: region) {
for (size_t i = 1; i < line.size(); ++i) {
face.push_back(line[i]);
}
}
if (face.size() < 3) {
qDebug() << "Face length invalid:" << face.size();
continue;
}
m_newFaces.push_back(face);
}
}
void RegionFiller::convertPolylinesToFaces()
{
if (m_sourcePolylines->empty())
return;
std::vector<size_t> face;
for (const auto &line: *m_sourcePolylines) {
for (size_t i = 1; i < line.size(); ++i) {
face.push_back(line[i]);
}
}
if (face.size() < 3) {
return;
}
m_newFaces.push_back(face);
}
bool RegionFiller::createCoonsPatchFrom(const std::vector<size_t> &c0,
const std::vector<size_t> &c1,
const std::vector<size_t> &d0,
const std::vector<size_t> &d1,
bool fillLastTriangle)
{
auto Lc_Position = [&](int s, int t) {
float factor = (float)t / d0.size();
return (1.0 - factor) * m_oldAndNewVertices[c0[s]].position + factor * m_oldAndNewVertices[c1[s]].position;
};
auto Ld_Position = [&](int s, int t) {
float factor = (float)s / c0.size();
return (1.0 - factor) * m_oldAndNewVertices[d0[t]].position + factor * m_oldAndNewVertices[d1[t]].position;
};
auto B_Position = [&](int s, int t) {
float tFactor = (float)t / d0.size();
float sFactor = (float)s / c0.size();
return m_oldAndNewVertices[c0[0]].position * (1.0 - sFactor) * (1.0 - tFactor) +
m_oldAndNewVertices[c0[c0.size() - 1]].position * sFactor * (1.0 - tFactor) +
m_oldAndNewVertices[c1[0]].position * (1.0 - sFactor) * tFactor +
m_oldAndNewVertices[c1[c1.size() - 1]].position * sFactor * tFactor;
};
auto C_Position = [&](int s, int t) {
return Lc_Position(s, t) + Ld_Position(s, t) - B_Position(s, t);
};
auto Lc_Radius = [&](int s, int t) {
float factor = (float)t / d0.size();
return (1.0 - factor) * m_oldAndNewVertices[c0[s]].radius + factor * m_oldAndNewVertices[c1[s]].radius;
};
auto Ld_Radius = [&](int s, int t) {
float factor = (float)s / c0.size();
return (1.0 - factor) * m_oldAndNewVertices[d0[t]].radius + factor * m_oldAndNewVertices[d1[t]].radius;
};
auto B_Radius = [&](int s, int t) {
float tFactor = (float)t / d0.size();
float sFactor = (float)s / c0.size();
return m_oldAndNewVertices[c0[0]].radius * (1.0 - sFactor) * (1.0 - tFactor) +
m_oldAndNewVertices[c0[c0.size() - 1]].radius * sFactor * (1.0 - tFactor) +
m_oldAndNewVertices[c1[0]].radius * (1.0 - sFactor) * tFactor +
m_oldAndNewVertices[c1[c1.size() - 1]].radius * sFactor * tFactor;
};
auto C_Radius = [&](int s, int t) {
return Lc_Radius(s, t) + Ld_Radius(s, t) - B_Radius(s, t);
};
auto getSource = [&](size_t i, size_t j, float factor) {
if (m_centerSources.find(m_oldAndNewVertices[j].source) != m_centerSources.end()) {
return m_oldAndNewVertices[i].source;
} else if (m_centerSources.find(m_oldAndNewVertices[i].source) != m_centerSources.end()) {
return m_oldAndNewVertices[j].source;
} else {
if (factor > 0.5) {
return m_oldAndNewVertices[j].source;
}
return m_oldAndNewVertices[i].source;
}
};
auto Lc_Source = [&](int s, int t) {
float factor = (float)t / d0.size();
return getSource(c0[s], c1[s], factor);
};
auto C_Source = [&](int s, int t) {
return Lc_Source(s, t);
};
std::vector<std::vector<size_t>> grid(c0.size());
for (int s = 1; s < (int)c0.size() - 1; ++s) {
grid[s].resize(d0.size());
for (int t = 1; t < (int)d0.size() - 1; ++t) {
Node node;
node.position = C_Position(s, t);
node.radius = C_Radius(s, t);
node.source = C_Source(s, t);
grid[s][t] = m_oldAndNewVertices.size();
m_oldAndNewVertices.push_back(node);
}
}
grid[0].resize(d0.size());
grid[c0.size() - 1].resize(d0.size());
for (size_t i = 0; i < c0.size(); ++i) {
grid[i][0] = c0[i];
grid[i][d0.size() - 1] = c1[i];
}
for (size_t i = 0; i < d0.size(); ++i) {
grid[0][i] = d0[i];
grid[c0.size() - 1][i] = d1[i];
}
for (int s = 1; s < (int)c0.size(); ++s) {
for (int t = 1; t < (int)d0.size(); ++t) {
std::vector<size_t> face = {
grid[s - 1][t - 1],
grid[s - 1][t],
grid[s][t],
grid[s][t - 1]
};
m_newFaces.push_back(face);
}
}
if (fillLastTriangle) {
std::vector<size_t> face = {
grid[c0.size() - 1][d0.size() - 2],
grid[c0.size() - 2][d0.size() - 2],
grid[c0.size() - 2][d0.size() - 1]
};
m_newFaces.push_back(face);
}
return true;
}
bool RegionFiller::createCoonsPatch(const std::vector<std::vector<size_t>> &region)
{
// https://en.wikipedia.org/wiki/Coons_patch
if (region.size() != 4) {
if (region.size() == 3) {
createCoonsPatchThreeSidedRegion(region);
return true;
}
qDebug() << "Invalid region edges:" << region.size();
return false;
}
const auto &c0 = region[0];
auto c1 = region[2];
std::reverse(c1.begin(), c1.end());
const auto &d1 = region[1];
auto d0 = region[3];
std::reverse(d0.begin(), d0.end());
if (c0.empty() ||
c0.size() != c1.size() ||
d0.empty() ||
d0.size() != d1.size()) {
qDebug() << "Invalid region size:" << c0.size() << c1.size() << d0.size() << d1.size();
return false;
}
return createCoonsPatchFrom(c0, c1, d0, d1, false);
}
bool RegionFiller::createCoonsPatchThreeSidedRegion(const std::vector<std::vector<size_t>> &region)
{
if (region.size() != 3) {
qDebug() << "Not three sided region";
return false;
}
int longest = 0;
size_t longestLength = region[longest].size();
for (int i = 1; i < 3; ++i) {
if (region[i].size() > longestLength) {
longest = i;
longestLength = region[i].size();
}
}
int c = longest;
int a = (c + 1) % 3;
int b = (a + 1) % 3;
auto c0 = region[a];
std::reverse(c0.begin(), c0.end());
auto c1 = std::vector<size_t>(region[c].begin(), region[c].begin() + c0.size());
auto d0 = region[b];
auto d1 = std::vector<size_t>(region[c].begin() + region[c].size() - d0.size(), region[c].begin() + region[c].size());
std::reverse(d1.begin(), d1.end());
bool fillTriangle = region[c].size() != region[a].size() &&
region[a].size() != region[b].size();
if (c0.size() < d0.size()) {
if (!createCoonsPatchFrom(d0, d1, c0, c1, fillTriangle))
return false;
} else {
if (!createCoonsPatchFrom(c0, c1, d0, d1, fillTriangle))
return false;
}
return true;
}
bool RegionFiller::convertRegionsToPatches()
{
if (m_newRegions.empty()) {
qDebug() << "No region, should not happen";
return false;
}
for (const auto &region: m_newRegions) {
if (!createCoonsPatch(region))
return false;
}
return true;
}