Optimize endpoint face generation
parent
0ae6426587
commit
3af98c5334
|
@ -538,6 +538,9 @@ HEADERS += src/fileforever.h
|
||||||
SOURCES += src/partpreviewimagesgenerator.cpp
|
SOURCES += src/partpreviewimagesgenerator.cpp
|
||||||
HEADERS += src/partpreviewimagesgenerator.h
|
HEADERS += src/partpreviewimagesgenerator.h
|
||||||
|
|
||||||
|
SOURCES += src/remeshhole.cpp
|
||||||
|
HEADERS += src/remeshhole.h
|
||||||
|
|
||||||
SOURCES += src/main.cpp
|
SOURCES += src/main.cpp
|
||||||
|
|
||||||
HEADERS += src/version.h
|
HEADERS += src/version.h
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||||
|
#include <CGAL/Surface_mesh.h>
|
||||||
|
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/remesh.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
|
||||||
|
#include <boost/function_output_iterator.hpp>
|
||||||
|
#include "remeshhole.h"
|
||||||
|
|
||||||
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
|
||||||
|
typedef Kernel::Point_3 Point;
|
||||||
|
typedef CGAL::Surface_mesh<Kernel::Point_3> Mesh;
|
||||||
|
typedef boost::graph_traits<Mesh>::halfedge_descriptor halfedge_descriptor;
|
||||||
|
typedef boost::graph_traits<Mesh>::edge_descriptor edge_descriptor;
|
||||||
|
typedef boost::graph_traits<Mesh>::vertex_iterator vertex_iterator;
|
||||||
|
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
|
||||||
|
|
||||||
|
struct halfedge2edge
|
||||||
|
{
|
||||||
|
halfedge2edge(const Mesh& m, std::vector<edge_descriptor>& edges)
|
||||||
|
: m_mesh(m), m_edges(edges)
|
||||||
|
{}
|
||||||
|
void operator()(const halfedge_descriptor& h) const
|
||||||
|
{
|
||||||
|
m_edges.push_back(edge(h, m_mesh));
|
||||||
|
}
|
||||||
|
const Mesh& m_mesh;
|
||||||
|
std::vector<edge_descriptor>& m_edges;
|
||||||
|
};
|
||||||
|
|
||||||
|
void remeshHole(std::vector<QVector3D> &vertices,
|
||||||
|
const std::vector<size_t> &hole,
|
||||||
|
std::vector<std::vector<size_t>> &newFaces)
|
||||||
|
{
|
||||||
|
if (hole.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Mesh mesh;
|
||||||
|
|
||||||
|
double targetEdgeLength = 0;
|
||||||
|
for (size_t i = 1; i < hole.size(); ++i) {
|
||||||
|
targetEdgeLength += (vertices[hole[i - 1]] - vertices[hole[i]]).length();
|
||||||
|
}
|
||||||
|
targetEdgeLength /= hole.size();
|
||||||
|
|
||||||
|
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> meshFace;
|
||||||
|
std::vector<size_t> originalIndices;
|
||||||
|
originalIndices.reserve(hole.size());
|
||||||
|
for (const auto &v: hole) {
|
||||||
|
originalIndices.push_back(v);
|
||||||
|
const auto &position = vertices[v];
|
||||||
|
meshFace.push_back(mesh.add_vertex(Point(position.x(), position.y(), position.z())));
|
||||||
|
}
|
||||||
|
mesh.add_face(meshFace);
|
||||||
|
|
||||||
|
std::vector<edge_descriptor> border;
|
||||||
|
|
||||||
|
CGAL::Polygon_mesh_processing::triangulate_faces(mesh);
|
||||||
|
|
||||||
|
CGAL::Polygon_mesh_processing::border_halfedges(faces(mesh),
|
||||||
|
mesh,
|
||||||
|
boost::make_function_output_iterator(halfedge2edge(mesh, border)));
|
||||||
|
|
||||||
|
auto ecm = mesh.add_property_map<edge_descriptor, bool>("ecm").first;
|
||||||
|
for (edge_descriptor e: border)
|
||||||
|
ecm[e] = true;
|
||||||
|
|
||||||
|
Mesh::Property_map<Mesh::Vertex_index, size_t> meshPropertyMap;
|
||||||
|
bool created;
|
||||||
|
boost::tie(meshPropertyMap, created) = mesh.add_property_map<Mesh::Vertex_index, size_t>("v:source", 0);
|
||||||
|
|
||||||
|
size_t vertexIndex = 0;
|
||||||
|
for (auto vertexIt = mesh.vertices_begin(); vertexIt != mesh.vertices_end(); vertexIt++) {
|
||||||
|
meshPropertyMap[*vertexIt] = originalIndices[vertexIndex++];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int nb_iter = 3;
|
||||||
|
|
||||||
|
CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(mesh),
|
||||||
|
targetEdgeLength,
|
||||||
|
mesh,
|
||||||
|
CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter)
|
||||||
|
.protect_constraints(true)
|
||||||
|
.edge_is_constrained_map(ecm));
|
||||||
|
|
||||||
|
for (auto vertexIt = mesh.vertices_begin() + vertexIndex; vertexIt != mesh.vertices_end(); vertexIt++) {
|
||||||
|
auto point = mesh.point(*vertexIt);
|
||||||
|
originalIndices.push_back(vertices.size());
|
||||||
|
vertices.push_back(QVector3D(
|
||||||
|
CGAL::to_double(point.x()),
|
||||||
|
CGAL::to_double(point.y()),
|
||||||
|
CGAL::to_double(point.z())
|
||||||
|
));
|
||||||
|
meshPropertyMap[*vertexIt] = originalIndices[vertexIndex++];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &faceIt: mesh.faces()) {
|
||||||
|
CGAL::Vertex_around_face_iterator<Mesh> vbegin, vend;
|
||||||
|
std::vector<size_t> faceIndices;
|
||||||
|
for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(mesh.halfedge(faceIt), mesh);
|
||||||
|
vbegin != vend;
|
||||||
|
++vbegin) {
|
||||||
|
faceIndices.push_back(meshPropertyMap[*vbegin]);
|
||||||
|
}
|
||||||
|
newFaces.push_back(faceIndices);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef DUST3D_REMESH_HOLE_H
|
||||||
|
#define DUST3D_REMESH_HOLE_H
|
||||||
|
#include <QVector3D>
|
||||||
|
|
||||||
|
void remeshHole(std::vector<QVector3D> &vertices,
|
||||||
|
const std::vector<size_t> &hole,
|
||||||
|
std::vector<std::vector<size_t>> &newFaces);
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,6 +5,7 @@
|
||||||
#include "meshstitcher.h"
|
#include "meshstitcher.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "boxmesh.h"
|
#include "boxmesh.h"
|
||||||
|
#include "remeshhole.h"
|
||||||
|
|
||||||
size_t StrokeMeshBuilder::Node::nextOrNeighborOtherThan(size_t neighborIndex) const
|
size_t StrokeMeshBuilder::Node::nextOrNeighborOtherThan(size_t neighborIndex) const
|
||||||
{
|
{
|
||||||
|
@ -230,7 +231,6 @@ void StrokeMeshBuilder::buildMesh()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<size_t>> cuts;
|
|
||||||
for (size_t i = 0; i < m_nodeIndices.size(); ++i) {
|
for (size_t i = 0; i < m_nodeIndices.size(); ++i) {
|
||||||
auto &node = m_nodes[m_nodeIndices[i]];
|
auto &node = m_nodes[m_nodeIndices[i]];
|
||||||
if (!qFuzzyIsNull(node.cutRotation)) {
|
if (!qFuzzyIsNull(node.cutRotation)) {
|
||||||
|
@ -243,16 +243,18 @@ void StrokeMeshBuilder::buildMesh()
|
||||||
node.traverseDirection, node.baseNormal);
|
node.traverseDirection, node.baseNormal);
|
||||||
std::vector<size_t> cut;
|
std::vector<size_t> cut;
|
||||||
insertCutVertices(cutVertices, &cut, node.index, node.traverseDirection);
|
insertCutVertices(cutVertices, &cut, node.index, node.traverseDirection);
|
||||||
cuts.push_back(cut);
|
m_cuts.push_back(cut);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stich cuts
|
void StrokeMeshBuilder::stitchCuts()
|
||||||
|
{
|
||||||
for (size_t i = m_isRing ? 0 : 1; i < m_nodeIndices.size(); ++i) {
|
for (size_t i = m_isRing ? 0 : 1; i < m_nodeIndices.size(); ++i) {
|
||||||
size_t h = (i + m_nodeIndices.size() - 1) % m_nodeIndices.size();
|
size_t h = (i + m_nodeIndices.size() - 1) % m_nodeIndices.size();
|
||||||
const auto &nodeH = m_nodes[m_nodeIndices[h]];
|
const auto &nodeH = m_nodes[m_nodeIndices[h]];
|
||||||
const auto &nodeI = m_nodes[m_nodeIndices[i]];
|
const auto &nodeI = m_nodes[m_nodeIndices[i]];
|
||||||
const auto &cutH = cuts[h];
|
const auto &cutH = m_cuts[h];
|
||||||
auto reversedCutI = edgeloopFlipped(cuts[i]);
|
auto reversedCutI = edgeloopFlipped(m_cuts[i]);
|
||||||
std::vector<std::pair<std::vector<size_t>, QVector3D>> edgeLoops = {
|
std::vector<std::pair<std::vector<size_t>, QVector3D>> edgeLoops = {
|
||||||
{cutH, -nodeH.traverseDirection},
|
{cutH, -nodeH.traverseDirection},
|
||||||
{reversedCutI, nodeI.traverseDirection},
|
{reversedCutI, nodeI.traverseDirection},
|
||||||
|
@ -267,7 +269,7 @@ void StrokeMeshBuilder::buildMesh()
|
||||||
|
|
||||||
// Fill endpoints
|
// Fill endpoints
|
||||||
if (!m_isRing) {
|
if (!m_isRing) {
|
||||||
if (cuts.size() < 2)
|
if (m_cuts.size() < 2)
|
||||||
return;
|
return;
|
||||||
if (!qFuzzyIsNull(m_hollowThickness)) {
|
if (!qFuzzyIsNull(m_hollowThickness)) {
|
||||||
// Generate mesh for hollow
|
// Generate mesh for hollow
|
||||||
|
@ -293,8 +295,8 @@ void StrokeMeshBuilder::buildMesh()
|
||||||
m_generatedFaces.push_back(newFace);
|
m_generatedFaces.push_back(newFace);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<size_t>> revisedCuts = {cuts[0],
|
std::vector<std::vector<size_t>> revisedCuts = {m_cuts[0],
|
||||||
edgeloopFlipped(cuts[cuts.size() - 1])};
|
edgeloopFlipped(m_cuts[m_cuts.size() - 1])};
|
||||||
for (const auto &cut: revisedCuts) {
|
for (const auto &cut: revisedCuts) {
|
||||||
for (size_t i = 0; i < cut.size(); ++i) {
|
for (size_t i = 0; i < cut.size(); ++i) {
|
||||||
size_t j = (i + 1) % cut.size();
|
size_t j = (i + 1) % cut.size();
|
||||||
|
@ -307,8 +309,28 @@ void StrokeMeshBuilder::buildMesh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_generatedFaces.push_back(cuts[0]);
|
if (m_cuts[0].size() <= 4) {
|
||||||
m_generatedFaces.push_back(edgeloopFlipped(cuts[cuts.size() - 1]));
|
m_generatedFaces.push_back(m_cuts[0]);
|
||||||
|
m_generatedFaces.push_back(edgeloopFlipped(m_cuts[m_cuts.size() - 1]));
|
||||||
|
} else {
|
||||||
|
auto remeshAndAddCut = [&](const std::vector<size_t> &inputFace) {
|
||||||
|
std::vector<std::vector<size_t>> remeshedFaces;
|
||||||
|
size_t oldVertexCount = m_generatedVertices.size();
|
||||||
|
remeshHole(m_generatedVertices,
|
||||||
|
inputFace,
|
||||||
|
remeshedFaces);
|
||||||
|
size_t oldIndex = inputFace[0];
|
||||||
|
for (size_t i = oldVertexCount; i < m_generatedVertices.size(); ++i) {
|
||||||
|
m_generatedVerticesCutDirects.push_back(m_generatedVerticesCutDirects[oldIndex]);
|
||||||
|
m_generatedVerticesSourceNodeIndices.push_back(m_generatedVerticesSourceNodeIndices[oldIndex]);
|
||||||
|
m_generatedVerticesInfos.push_back(m_generatedVerticesInfos[oldIndex]);
|
||||||
|
}
|
||||||
|
for (const auto &it: remeshedFaces)
|
||||||
|
m_generatedFaces.push_back(it);
|
||||||
|
};
|
||||||
|
remeshAndAddCut(m_cuts[0]);
|
||||||
|
remeshAndAddCut(edgeloopFlipped(m_cuts[m_cuts.size() - 1]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -722,5 +744,6 @@ bool StrokeMeshBuilder::build()
|
||||||
|
|
||||||
buildMesh();
|
buildMesh();
|
||||||
applyDeform();
|
applyDeform();
|
||||||
|
stitchCuts();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,8 @@ private:
|
||||||
std::vector<GeneratedVertexInfo> m_generatedVerticesInfos;
|
std::vector<GeneratedVertexInfo> m_generatedVerticesInfos;
|
||||||
std::vector<std::vector<size_t>> m_generatedFaces;
|
std::vector<std::vector<size_t>> m_generatedFaces;
|
||||||
|
|
||||||
|
std::vector<std::vector<size_t>> m_cuts;
|
||||||
|
|
||||||
bool prepare();
|
bool prepare();
|
||||||
std::vector<QVector3D> makeCut(const QVector3D &cutCenter,
|
std::vector<QVector3D> makeCut(const QVector3D &cutCenter,
|
||||||
float radius,
|
float radius,
|
||||||
|
@ -116,6 +118,7 @@ private:
|
||||||
std::vector<size_t> edgeloopFlipped(const std::vector<size_t> &edgeLoop);
|
std::vector<size_t> edgeloopFlipped(const std::vector<size_t> &edgeLoop);
|
||||||
void reviseNodeBaseNormal(Node &node);
|
void reviseNodeBaseNormal(Node &node);
|
||||||
void applyDeform();
|
void applyDeform();
|
||||||
|
void stitchCuts();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue