First almost working version of stitching line feature

master
Jeremy HU 2022-10-01 21:20:03 +10:00
parent 8f61fe168c
commit 7668818ab4
4 changed files with 167 additions and 46 deletions

View File

@ -375,34 +375,42 @@ void MeshGenerator::cutFaceStringToCutTemplate(const std::string &cutFaceString,
} }
} }
void MeshGenerator::convertLinksToOrdered(const std::unordered_map<size_t, std::unordered_set<size_t>> &links, void MeshGenerator::flattenLinks(const std::unordered_map<size_t, size_t> &links,
std::vector<size_t> *ordered, std::vector<size_t> *array,
bool *isCircle) bool *isCircle)
{ {
if (links.empty())
return;
for (const auto &it: links) { for (const auto &it: links) {
if (1 == it.second.size()) { if (links.end() == links.find(it.second)) {
std::unordered_set<size_t> used; *isCircle = false;
size_t current = it.first; std::unordered_map<size_t, size_t> reversedLinks;
bool foundNext = true; for (const auto &it: links)
while (foundNext) { 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;
current = findNext->second;
}
std::reverse(array->begin(), array->end());
return;
}
}
*isCircle = true;
size_t startIndex = links.begin()->first;
size_t current = startIndex;
for (;;) {
array->push_back(current);
auto findNext = links.find(current); auto findNext = links.find(current);
if (findNext == links.end()) if (findNext == links.end())
break; break;
used.insert(current); current = findNext->second;
ordered->push_back(current); if (current == startIndex)
foundNext = false;
for (const auto &it: findNext->second) {
if (used.end() != used.find(it))
continue;
current = it;
foundNext = true;
break; break;
} }
}
break;
}
}
// TODO:
} }
std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineStitchingMesh(const std::vector<std::string> &partIdStrings) std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineStitchingMesh(const std::vector<std::string> &partIdStrings)
@ -430,7 +438,7 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineStitchingMesh(const st
}); });
} }
std::unordered_map<size_t, std::unordered_set<size_t>> builderNodeLinks; std::unordered_map<size_t, size_t> builderNodeLinks;
for (const auto &edgeIdString: m_partEdgeIds[partIdString]) { for (const auto &edgeIdString: m_partEdgeIds[partIdString]) {
auto findEdge = m_snapshot->edges.find(edgeIdString); auto findEdge = m_snapshot->edges.find(edgeIdString);
if (findEdge == m_snapshot->edges.end()) { if (findEdge == m_snapshot->edges.end()) {
@ -447,12 +455,12 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineStitchingMesh(const st
auto findTo = builderNodeIdStringToIndexMap.find(toNodeIdString); auto findTo = builderNodeIdStringToIndexMap.find(toNodeIdString);
if (findTo == builderNodeIdStringToIndexMap.end()) if (findTo == builderNodeIdStringToIndexMap.end())
continue; continue;
builderNodeLinks[findFrom->second].insert(findTo->second); builderNodeLinks[findFrom->second] = findTo->second;
} }
std::vector<size_t> orderedIndices; std::vector<size_t> orderedIndices;
bool isCircle = false; bool isCircle = false;
convertLinksToOrdered(builderNodeLinks, &orderedIndices, &isCircle); flattenLinks(builderNodeLinks, &orderedIndices, &isCircle);
std::vector<StitchMeshBuilder::Node> orderedBuilderNodes(orderedIndices.size()); std::vector<StitchMeshBuilder::Node> orderedBuilderNodes(orderedIndices.size());
for (size_t i = 0; i < orderedIndices.size(); ++i) for (size_t i = 0; i < orderedIndices.size(); ++i)
orderedBuilderNodes[i] = builderNodes[orderedIndices[i]]; orderedBuilderNodes[i] = builderNodes[orderedIndices[i]];
@ -465,8 +473,12 @@ std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combineStitchingMesh(const st
auto stitchMeshBuilder = std::make_unique<StitchMeshBuilder>(std::move(splines)); auto stitchMeshBuilder = std::make_unique<StitchMeshBuilder>(std::move(splines));
stitchMeshBuilder->build(); stitchMeshBuilder->build();
return std::make_unique<MeshCombiner::Mesh>(stitchMeshBuilder->generatedVertices(), auto mesh = std::make_unique<MeshCombiner::Mesh>(stitchMeshBuilder->generatedVertices(),
stitchMeshBuilder->generatedFaces()); stitchMeshBuilder->generatedFaces());
if (mesh && mesh->isNull())
mesh.reset();
return mesh;
} }
std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes) std::unique_ptr<MeshCombiner::Mesh> MeshGenerator::combinePartMesh(const std::string &partIdString, bool *hasError, bool *retryable, bool addIntermediateNodes)

View File

@ -160,8 +160,8 @@ private:
static void chamferFace(std::vector<Vector2> *face); static void chamferFace(std::vector<Vector2> *face);
static bool isWatertight(const std::vector<std::vector<size_t>> &faces); static bool isWatertight(const std::vector<std::vector<size_t>> &faces);
static void convertLinksToOrdered(const std::unordered_map<size_t, std::unordered_set<size_t>> &links, static void flattenLinks(const std::unordered_map<size_t, size_t> &links,
std::vector<size_t> *ordered, std::vector<size_t> *array,
bool *isCircle); bool *isCircle);
}; };

View File

@ -45,18 +45,23 @@ std::vector<std::vector<StitchMeshBuilder::StitchingPoint>> StitchMeshBuilder::c
{ {
std::vector<std::vector<StitchingPoint>> stitchingPoints(splines.size()); std::vector<std::vector<StitchingPoint>> stitchingPoints(splines.size());
size_t targetSegments = 5; // FIXME: decide target segments by ...
for (size_t i = 0; i < splines.size(); ++i) { for (size_t i = 0; i < splines.size(); ++i) {
const auto &spline = splines[i]; const auto &spline = splines[i];
std::vector<Vector3> polyline(spline.nodes.size()); std::vector<Vector3> polyline(spline.nodes.size());
for (size_t j = 0; j < spline.nodes.size(); ++j) std::vector<double> radiuses(spline.nodes.size());
for (size_t j = 0; j < spline.nodes.size(); ++j) {
polyline[j] = spline.nodes[j].origin; polyline[j] = spline.nodes[j].origin;
auto segmentPoints = splitPolylineToSegments(polyline, targetSegments); radiuses[j] = spline.nodes[j].radius;
}
std::vector<Vector3> segmentPoints;
std::vector<double> segmentRadiuses;
splitPolylineToSegments(polyline, radiuses, m_targetSegments, &segmentPoints, &segmentRadiuses);
stitchingPoints[i].resize(segmentPoints.size()); stitchingPoints[i].resize(segmentPoints.size());
for (size_t k = 0; k < segmentPoints.size(); ++k) { for (size_t k = 0; k < segmentPoints.size(); ++k) {
stitchingPoints[i][k].originVertex = m_generatedVertices.size(); stitchingPoints[i][k].originVertex = m_generatedVertices.size();
// TODO: stitchingPoints[i][k].radius = ... stitchingPoints[i][k].radius = segmentRadiuses[k];
m_generatedVertices.emplace_back(segmentPoints[k]); m_generatedVertices.push_back(segmentPoints[k]);
m_generatedStitchingPoints.push_back(stitchingPoints[i][k]);
} }
} }
@ -73,45 +78,55 @@ double StitchMeshBuilder::segmentsLength(const std::vector<Vector3> &segmentPoin
return totalLength; return totalLength;
} }
std::vector<Vector3> StitchMeshBuilder::splitPolylineToSegments(const std::vector<Vector3> &polyline, void StitchMeshBuilder::splitPolylineToSegments(const std::vector<Vector3> &polyline,
size_t targetSegments) const std::vector<double> &radiuses,
size_t targetSegments,
std::vector<Vector3> *targetPoints,
std::vector<double> *targetRadiuses)
{ {
std::vector<Vector3> segmentPoints;
if (polyline.size() < 2) if (polyline.size() < 2)
return segmentPoints; return;
double totalLength = segmentsLength(polyline); double totalLength = segmentsLength(polyline);
if (Math::isZero(totalLength)) { if (Math::isZero(totalLength)) {
Vector3 middle = (polyline.front() + polyline.back()) * 0.5; 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) { 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 segmentLength = totalLength / targetSegments;
double wantLength = segmentLength; 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) { for (size_t j = 1; j < polyline.size(); ++j) {
size_t i = j - 1; size_t i = j - 1;
double lineLength = (polyline[j] - polyline[i]).length(); double lineLength = (polyline[j] - polyline[i]).length();
Vector3 polylineStart = polyline[i]; Vector3 polylineStart = polyline[i];
double radiusStart = radiuses[i];
while (true) { while (true) {
if (lineLength < wantLength) { if (lineLength < wantLength) {
wantLength -= lineLength; wantLength -= lineLength;
break; break;
} }
Vector3 newPosition = (polylineStart * (lineLength - wantLength) + polyline[j] * wantLength) / lineLength; 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; lineLength -= wantLength;
polylineStart = newPosition; polylineStart = newPosition;
radiusStart = newRadius;
wantLength = segmentLength; wantLength = segmentLength;
} }
} }
if (segmentPoints.size() < targetSegments + 1) { if (targetPoints->size() < targetSegments + 1) {
segmentPoints.push_back(polyline.back()); targetPoints->push_back(polyline.back());
targetRadiuses->push_back(radiuses.back());
} else { } else {
segmentPoints.back() = polyline.back(); targetPoints->back() = polyline.back();
targetRadiuses->back() = radiuses.back();
} }
return segmentPoints;
} }
void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector<std::vector<StitchMeshBuilder::StitchingPoint>> &stitchingPoints) void StitchMeshBuilder::generateMeshFromStitchingPoints(const std::vector<std::vector<StitchMeshBuilder::StitchingPoint>> &stitchingPoints)
@ -148,6 +163,94 @@ void StitchMeshBuilder::build()
{ {
std::vector<std::vector<StitchingPoint>> stitchingPoints = convertSplinesToStitchingPoints(m_splines); std::vector<std::vector<StitchingPoint>> stitchingPoints = convertSplinesToStitchingPoints(m_splines);
generateMeshFromStitchingPoints(stitchingPoints); 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<size_t> {
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<size_t> {
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<size_t> {
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<size_t> {
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<size_t> {
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
} }
} }

View File

@ -56,15 +56,21 @@ private:
}; };
std::vector<Spline> m_splines; std::vector<Spline> m_splines;
std::vector<StitchingPoint> m_generatedStitchingPoints;
std::vector<Vector3> m_generatedVertices; std::vector<Vector3> m_generatedVertices;
std::vector<std::vector<size_t>> m_generatedFaces; std::vector<std::vector<size_t>> m_generatedFaces;
std::vector<Vector3> m_generatedNormals;
size_t m_targetSegments = 5;
std::vector<std::vector<StitchingPoint>> convertSplinesToStitchingPoints(const std::vector<Spline> &splines); std::vector<std::vector<StitchingPoint>> convertSplinesToStitchingPoints(const std::vector<Spline> &splines);
void generateMeshFromStitchingPoints(const std::vector<std::vector<StitchingPoint>> &stitchingPoints); void generateMeshFromStitchingPoints(const std::vector<std::vector<StitchingPoint>> &stitchingPoints);
void generateMeshFromStitchingPoints(const std::vector<StitchingPoint> &a, void generateMeshFromStitchingPoints(const std::vector<StitchingPoint> &a,
const std::vector<StitchingPoint> &b); const std::vector<StitchingPoint> &b);
std::vector<Vector3> splitPolylineToSegments(const std::vector<Vector3> &polyline, void splitPolylineToSegments(const std::vector<Vector3> &polyline,
size_t targetSegments); const std::vector<double> &radiuses,
size_t targetSegments,
std::vector<Vector3> *targetPoints,
std::vector<double> *targetRadiuses);
double segmentsLength(const std::vector<Vector3> &segmentPoints); double segmentsLength(const std::vector<Vector3> &segmentPoints);
}; };