#include #include #include #include #include "regionfiller.h" // // This is an implementation of the following paper // // // A. Nasri1∗, M. Sabin2 and Z. Yasseen1 // #define isEven(n) (0 == (n) % 2) #define isOdd(n) (!isEven(n)) RegionFiller::RegionFiller(const std::vector *vertices, const std::vector> *polylines) : m_sourceVertices(vertices), m_sourcePolylines(new std::vector>(*polylines)) { } RegionFiller::~RegionFiller() { delete m_sourcePolylines; } bool RegionFiller::resolveEvenSidedEvenSumOfSegmentsAndBothL2L2AreEven() { return resolveOddSidedEvenSumOfSegments(m_sideNum / 2); } std::vector RegionFiller::createPointsToMapBetween(size_t fromIndex, size_t toIndex, size_t segments, std::map, std::vector> *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 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 *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 RegionFiller::createPointsBetween(size_t fromIndex, size_t toIndex, size_t segments) { std::vector newPointIndices; createPointsBetween(fromIndex, toIndex, segments, &newPointIndices); return newPointIndices; } void RegionFiller::collectEdgePoints(size_t polyline, int startPos, int stopPos, std::vector *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 RegionFiller::collectEdgePoints(size_t polyline, int startPos, int stopPos) { std::vector pointIndices; collectEdgePoints(polyline, startPos, stopPos, &pointIndices); return pointIndices; } std::vector RegionFiller::reversed(const std::vector &pointIndices) { std::vector 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 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 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 cornerVertices; std::vector> edgeSegments; // tuple 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> 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 firstSegmentPoints; std::vector secondSegmentPoints; std::vector 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::vector> 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 firstSegmentPoints; std::vector secondSegmentPoints; std::vector 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> 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> 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 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 areas(m_sideNum); for (int i = 0; i < m_sideNum; ++i) { areas[i] = gRaySegmentNums[i] * gRaySegmentNums[(i + 1) % m_sideNum]; } std::vector> eIndices(m_sideNum); for (int i = 0; i < m_sideNum; ++i) { auto length = gRaySegmentNums[(i + 1) % gRaySegmentNums.size()]; eIndices[i] = std::vector{(int)m_sideSegmentNums[i] - length - 1, (int)m_sideSegmentNums[i] - length }; if (eIndices[i][0] < 0 || eIndices[i][1] < 0) return false; } std::vector 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>> segmentsWithG(m_sideNum); for (int i = 0; i < m_sideNum; ++i) { segmentsWithG[i].resize(2); } std::vector 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>> 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 e0c1; collectEdgePoints(i, eIndices[i][1], (*m_sourcePolylines)[i].size() - 1, &e0c1); std::vector c1e1; collectEdgePoints(j, 0, eIndices[j][0], &c1e1); std::vector> 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 e1e2 = { (*m_sourcePolylines)[i][eIndices[i][0]], (*m_sourcePolylines)[i][eIndices[i][1]] }; std::vector gCoreEdge = { gFace[j], gFace[i] }; std::vector> 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 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 areas(m_sideNum); for (int i = 0; i < m_sideNum; ++i) { areas[i] = gRaySegmentNums[i] * gRaySegmentNums[(i + 1) % m_sideNum]; } std::vector> 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)length, (int)length + 1 }; } else { eIndices[i] = std::vector{(int)length, (int)length }; } if (eIndices[i][0] < 0 || eIndices[i][1] < 0) { qDebug() << "resolveEvenSideEvenSumOfSegmentsAndL1IsEvenL2IsOdd failed"; return false; } } std::vector 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 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>> 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>> 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 e0c1; collectEdgePoints(i, eIndices[i][1], (*m_sourcePolylines)[i].size() - 1, &e0c1); std::vector c1e1; collectEdgePoints(j, 0, eIndices[j][0], &c1e1); std::vector> 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 e1e2 = { (*m_sourcePolylines)[i][eIndices[i][0]], (*m_sourcePolylines)[i][eIndices[i][1]] }; std::vector g1g0 = { gFace[(k + 1) % gFace.size()], gFace[k % gFace.size()] }; std::vector> 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::vector> map; { std::vector> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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::vector> map; std::vector> 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> 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> 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> 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> 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> 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> 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> 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> 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::getOldAndNewVertices() { return m_oldAndNewVertices; } const std::vector> &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> 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 ®ion: m_newRegions) { std::vector 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 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 &c0, const std::vector &c1, const std::vector &d0, const std::vector &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> 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 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 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> ®ion) { // 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> ®ion) { 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(region[c].begin(), region[c].begin() + c0.size()); auto d0 = region[b]; auto d1 = std::vector(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 ®ion: m_newRegions) { if (!createCoonsPatch(region)) return false; } return true; }