From 7668818ab4b731f512ecca964066050a4491b514 Mon Sep 17 00:00:00 2001 From: Jeremy HU Date: Sat, 1 Oct 2022 21:20:03 +1000 Subject: [PATCH] First almost working version of stitching line feature --- dust3d/mesh/mesh_generator.cc | 62 +++++++------ dust3d/mesh/mesh_generator.h | 4 +- dust3d/mesh/stitch_mesh_builder.cc | 137 +++++++++++++++++++++++++---- dust3d/mesh/stitch_mesh_builder.h | 10 ++- 4 files changed, 167 insertions(+), 46 deletions(-) diff --git a/dust3d/mesh/mesh_generator.cc b/dust3d/mesh/mesh_generator.cc index 9152af76..57f9ede9 100644 --- a/dust3d/mesh/mesh_generator.cc +++ b/dust3d/mesh/mesh_generator.cc @@ -375,34 +375,42 @@ void MeshGenerator::cutFaceStringToCutTemplate(const std::string &cutFaceString, } } -void MeshGenerator::convertLinksToOrdered(const std::unordered_map> &links, - std::vector *ordered, +void MeshGenerator::flattenLinks(const std::unordered_map &links, + std::vector *array, bool *isCircle) { + if (links.empty()) + return; for (const auto &it: links) { - if (1 == it.second.size()) { - std::unordered_set used; - size_t current = it.first; - bool foundNext = true; - while (foundNext) { - auto findNext = links.find(current); - if (findNext == links.end()) + if (links.end() == links.find(it.second)) { + *isCircle = false; + std::unordered_map reversedLinks; + for (const auto &it: links) + reversedLinks.insert({it.second, it.first}); + size_t current = it.second; + for (;;) { + array->push_back(current); + auto findNext = reversedLinks.find(current); + if (findNext == reversedLinks.end()) break; - used.insert(current); - ordered->push_back(current); - foundNext = false; - for (const auto &it: findNext->second) { - if (used.end() != used.find(it)) - continue; - current = it; - foundNext = true; - break; - } + current = findNext->second; } - break; + std::reverse(array->begin(), array->end()); + return; } } - // TODO: + *isCircle = true; + size_t startIndex = links.begin()->first; + size_t current = startIndex; + for (;;) { + array->push_back(current); + auto findNext = links.find(current); + if (findNext == links.end()) + break; + current = findNext->second; + if (current == startIndex) + break; + } } std::unique_ptr MeshGenerator::combineStitchingMesh(const std::vector &partIdStrings) @@ -430,7 +438,7 @@ std::unique_ptr MeshGenerator::combineStitchingMesh(const st }); } - std::unordered_map> builderNodeLinks; + std::unordered_map builderNodeLinks; for (const auto &edgeIdString: m_partEdgeIds[partIdString]) { auto findEdge = m_snapshot->edges.find(edgeIdString); if (findEdge == m_snapshot->edges.end()) { @@ -447,12 +455,12 @@ std::unique_ptr MeshGenerator::combineStitchingMesh(const st auto findTo = builderNodeIdStringToIndexMap.find(toNodeIdString); if (findTo == builderNodeIdStringToIndexMap.end()) continue; - builderNodeLinks[findFrom->second].insert(findTo->second); + builderNodeLinks[findFrom->second] = findTo->second; } std::vector orderedIndices; bool isCircle = false; - convertLinksToOrdered(builderNodeLinks, &orderedIndices, &isCircle); + flattenLinks(builderNodeLinks, &orderedIndices, &isCircle); std::vector orderedBuilderNodes(orderedIndices.size()); for (size_t i = 0; i < orderedIndices.size(); ++i) orderedBuilderNodes[i] = builderNodes[orderedIndices[i]]; @@ -465,8 +473,12 @@ std::unique_ptr MeshGenerator::combineStitchingMesh(const st auto stitchMeshBuilder = std::make_unique(std::move(splines)); stitchMeshBuilder->build(); - return std::make_unique(stitchMeshBuilder->generatedVertices(), + auto mesh = std::make_unique(stitchMeshBuilder->generatedVertices(), stitchMeshBuilder->generatedFaces()); + if (mesh && mesh->isNull()) + mesh.reset(); + + return mesh; } std::unique_ptr MeshGenerator::combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes) diff --git a/dust3d/mesh/mesh_generator.h b/dust3d/mesh/mesh_generator.h index 709a2dbb..edb8f89c 100644 --- a/dust3d/mesh/mesh_generator.h +++ b/dust3d/mesh/mesh_generator.h @@ -160,8 +160,8 @@ private: static void chamferFace(std::vector *face); static bool isWatertight(const std::vector> &faces); - static void convertLinksToOrdered(const std::unordered_map> &links, - std::vector *ordered, + static void flattenLinks(const std::unordered_map &links, + std::vector *array, bool *isCircle); }; diff --git a/dust3d/mesh/stitch_mesh_builder.cc b/dust3d/mesh/stitch_mesh_builder.cc index cb430eba..97a68dd2 100644 --- a/dust3d/mesh/stitch_mesh_builder.cc +++ b/dust3d/mesh/stitch_mesh_builder.cc @@ -45,18 +45,23 @@ std::vector> StitchMeshBuilder::c { std::vector> stitchingPoints(splines.size()); - size_t targetSegments = 5; // FIXME: decide target segments by ... for (size_t i = 0; i < splines.size(); ++i) { const auto &spline = splines[i]; std::vector polyline(spline.nodes.size()); - for (size_t j = 0; j < spline.nodes.size(); ++j) + std::vector radiuses(spline.nodes.size()); + for (size_t j = 0; j < spline.nodes.size(); ++j) { polyline[j] = spline.nodes[j].origin; - auto segmentPoints = splitPolylineToSegments(polyline, targetSegments); + radiuses[j] = spline.nodes[j].radius; + } + std::vector segmentPoints; + std::vector segmentRadiuses; + splitPolylineToSegments(polyline, radiuses, m_targetSegments, &segmentPoints, &segmentRadiuses); stitchingPoints[i].resize(segmentPoints.size()); for (size_t k = 0; k < segmentPoints.size(); ++k) { stitchingPoints[i][k].originVertex = m_generatedVertices.size(); - // TODO: stitchingPoints[i][k].radius = ... - m_generatedVertices.emplace_back(segmentPoints[k]); + stitchingPoints[i][k].radius = segmentRadiuses[k]; + m_generatedVertices.push_back(segmentPoints[k]); + m_generatedStitchingPoints.push_back(stitchingPoints[i][k]); } } @@ -73,45 +78,55 @@ double StitchMeshBuilder::segmentsLength(const std::vector &segmentPoin return totalLength; } -std::vector StitchMeshBuilder::splitPolylineToSegments(const std::vector &polyline, - size_t targetSegments) +void StitchMeshBuilder::splitPolylineToSegments(const std::vector &polyline, + const std::vector &radiuses, + size_t targetSegments, + std::vector *targetPoints, + std::vector *targetRadiuses) { - std::vector segmentPoints; if (polyline.size() < 2) - return segmentPoints; + return; double totalLength = segmentsLength(polyline); if (Math::isZero(totalLength)) { Vector3 middle = (polyline.front() + polyline.back()) * 0.5; + double radius = (radiuses.front() + radiuses.back()) * 0.5; for (size_t i = 0; i < targetSegments + 1; ++i) { - segmentPoints.push_back(middle); + targetPoints->push_back(middle); + targetRadiuses->push_back(radius); } - return segmentPoints; + return; } double segmentLength = totalLength / targetSegments; double wantLength = segmentLength; - segmentPoints.push_back(polyline.front()); + targetPoints->push_back(polyline.front()); + targetRadiuses->push_back(radiuses.front()); for (size_t j = 1; j < polyline.size(); ++j) { size_t i = j - 1; double lineLength = (polyline[j] - polyline[i]).length(); Vector3 polylineStart = polyline[i]; + double radiusStart = radiuses[i]; while (true) { if (lineLength < wantLength) { wantLength -= lineLength; break; } Vector3 newPosition = (polylineStart * (lineLength - wantLength) + polyline[j] * wantLength) / lineLength; - segmentPoints.push_back(newPosition); + double newRadius = (radiusStart * (lineLength - wantLength) + radiuses[j] * wantLength) / lineLength; + targetPoints->push_back(newPosition); + targetRadiuses->push_back(newRadius); lineLength -= wantLength; polylineStart = newPosition; + radiusStart = newRadius; wantLength = segmentLength; } } - if (segmentPoints.size() < targetSegments + 1) { - segmentPoints.push_back(polyline.back()); + if (targetPoints->size() < targetSegments + 1) { + targetPoints->push_back(polyline.back()); + targetRadiuses->push_back(radiuses.back()); } else { - segmentPoints.back() = polyline.back(); + targetPoints->back() = polyline.back(); + targetRadiuses->back() = radiuses.back(); } - return segmentPoints; } void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector> &stitchingPoints) @@ -148,6 +163,94 @@ void StitchMeshBuilder::build() { std::vector> stitchingPoints = convertSplinesToStitchingPoints(m_splines); generateMeshFromStitchingPoints(stitchingPoints); + + // Generate normals for all vertices + m_generatedNormals.resize(m_generatedVertices.size()); + for (const auto &quad: m_generatedFaces) { + for (size_t i = 0; i < 4; ++i) { + size_t j = (i + 1) % 4; + size_t k = (j + 1) % 4; + auto faceNormal = Vector3::normal(m_generatedVertices[quad[i]], m_generatedVertices[quad[j]], m_generatedVertices[quad[k]]); + m_generatedNormals[quad[i]] += faceNormal; + m_generatedNormals[quad[j]] += faceNormal; + m_generatedNormals[quad[k]] += faceNormal; + } + } + for (auto &it: m_generatedNormals) + it.normalize(); + + // Move all vertices off distance at radius + for (size_t i = 0; i < m_generatedVertices.size(); ++i) { + m_generatedVertices[i] += m_generatedNormals[i] * m_generatedStitchingPoints[i].radius; + } + + // Generate other side of faces + auto oneSideVertexCount = m_generatedVertices.size(); + m_generatedVertices.reserve(oneSideVertexCount * 2); + for (size_t i = 0; i < oneSideVertexCount; ++i) { + m_generatedVertices.emplace_back(m_generatedVertices[i] - m_generatedNormals[i] * m_generatedStitchingPoints[i].radius * 2.0); + } + m_generatedFaces.reserve(m_generatedFaces.size() * 2); + auto oneSideFaceCount = m_generatedFaces.size(); + for (size_t i = 0; i < oneSideFaceCount; ++i) { + auto quad = m_generatedFaces[i]; + m_generatedFaces.emplace_back(std::vector { + oneSideVertexCount + quad[3], + oneSideVertexCount + quad[2], + oneSideVertexCount + quad[1], + oneSideVertexCount + quad[0] + }); + } + + // Stitch layers for first stitching line + for (size_t j = 1; j <= m_targetSegments; ++j) { + size_t i = j - 1; + m_generatedFaces.emplace_back(std::vector { + oneSideVertexCount + i, + i, + j, + oneSideVertexCount + j + }); + } + + // Stitch layers for last stitching line + size_t offsetBetweenFirstAndLastBorder = oneSideVertexCount - (m_targetSegments + 1); + for (size_t j = 1; j <= m_targetSegments; ++j) { + size_t i = j - 1; + m_generatedFaces.emplace_back(std::vector { + oneSideVertexCount + j + offsetBetweenFirstAndLastBorder, + j + offsetBetweenFirstAndLastBorder, + i + offsetBetweenFirstAndLastBorder, + oneSideVertexCount + i + offsetBetweenFirstAndLastBorder + }); + } + + // Stitch layers for all head nodes of stitching lines + for (size_t j = 1; j < m_splines.size(); ++j) { + size_t i = j - 1; + m_generatedFaces.emplace_back(std::vector { + oneSideVertexCount + j * (m_targetSegments + 1), + j * (m_targetSegments + 1), + i * (m_targetSegments + 1), + oneSideVertexCount + i * (m_targetSegments + 1) + }); + } + + // Stitch layers for all tail nodes of stitching lines + size_t offsetBetweenSecondAndThirdBorder = m_targetSegments; + for (size_t j = 1; j < m_splines.size(); ++j) { + size_t i = j - 1; + m_generatedFaces.emplace_back(std::vector { + oneSideVertexCount + i * (m_targetSegments + 1) + offsetBetweenSecondAndThirdBorder, + i * (m_targetSegments + 1) + offsetBetweenSecondAndThirdBorder, + j * (m_targetSegments + 1) + offsetBetweenSecondAndThirdBorder, + oneSideVertexCount + j * (m_targetSegments + 1) + offsetBetweenSecondAndThirdBorder + }); + } + + // TODO: Process circle + + // TODO: Process interpolate } } diff --git a/dust3d/mesh/stitch_mesh_builder.h b/dust3d/mesh/stitch_mesh_builder.h index 49eb2c19..1a411e12 100644 --- a/dust3d/mesh/stitch_mesh_builder.h +++ b/dust3d/mesh/stitch_mesh_builder.h @@ -56,15 +56,21 @@ private: }; std::vector m_splines; + std::vector m_generatedStitchingPoints; std::vector m_generatedVertices; std::vector> m_generatedFaces; + std::vector m_generatedNormals; + size_t m_targetSegments = 5; std::vector> convertSplinesToStitchingPoints(const std::vector &splines); void generateMeshFromStitchingPoints(const std::vector> &stitchingPoints); void generateMeshFromStitchingPoints(const std::vector &a, const std::vector &b); - std::vector splitPolylineToSegments(const std::vector &polyline, - size_t targetSegments); + void splitPolylineToSegments(const std::vector &polyline, + const std::vector &radiuses, + size_t targetSegments, + std::vector *targetPoints, + std::vector *targetRadiuses); double segmentsLength(const std::vector &segmentPoints); };