First almost working version of stitching line feature
parent
8f61fe168c
commit
7668818ab4
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue