MeshFlattener: boundary extractor
This commit is contained in:
@@ -29,7 +29,6 @@
|
||||
#* Werner Mayer 2004 *
|
||||
#***************************************************************************/
|
||||
|
||||
|
||||
class MeshWorkbench (Workbench):
|
||||
"Mesh workbench object"
|
||||
def __init__(self):
|
||||
@@ -40,6 +39,13 @@ class MeshWorkbench (Workbench):
|
||||
def Initialize(self):
|
||||
import Mesh
|
||||
import MeshGui
|
||||
# try:
|
||||
import MeshFlatteningCommand
|
||||
toolbar = MeshFlatteningCommand.initialize()
|
||||
self.appendToolbar(toolbar)
|
||||
# except ImportError:
|
||||
# import FreeCAD as app
|
||||
# app.Message("MeshPart not found")
|
||||
def GetClassName(self):
|
||||
return "MeshGui::Workbench"
|
||||
|
||||
|
||||
@@ -80,6 +80,9 @@ SET_PYTHON_PREFIX_SUFFIX(MeshPart)
|
||||
|
||||
INSTALL(TARGETS MeshPart DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
|
||||
|
||||
################################ flat mesh ###############################
|
||||
SET(FLATMESH_SRCS
|
||||
MeshFlatteningPy.cpp
|
||||
MeshFlattening.cpp
|
||||
@@ -91,9 +94,11 @@ SET(FLATMESH_SRCS
|
||||
)
|
||||
|
||||
|
||||
|
||||
add_library(flatmesh SHARED ${FLATMESH_SRCS})
|
||||
SET_PYTHON_PREFIX_SUFFIX(flatmesh)
|
||||
target_link_libraries(flatmesh ${PYTHON_LIBRARIES} ${MeshPart_LIBS})
|
||||
|
||||
SET_BIN_DIR(flatmesh flatmesh /Mod/MeshPart)
|
||||
install(TARGETS flatmesh DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
############################################################################
|
||||
|
||||
@@ -28,9 +28,104 @@
|
||||
#include <Geom_Surface.hxx>
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
|
||||
std::vector<ColMat<double, 3>> getBoundaries(ColMat<double, 3> vertices, ColMat<long, 3> tris)
|
||||
{
|
||||
// get a hashtable for all edges
|
||||
// e: v1, v2, num
|
||||
std::map<std::set<long>, std::vector<long>> hash_map;
|
||||
std::vector<std::set<long>> hash_list;
|
||||
std::map<long, std::vector<long>> neighbour_map;
|
||||
std::vector<long> edge_vector_0;
|
||||
std::vector<std::vector<long>> edge_vector;
|
||||
|
||||
|
||||
for (long i=0; i<tris.rows(); i++)
|
||||
{
|
||||
for (long j=0; j<3; j++)
|
||||
{
|
||||
long k = j + 1;
|
||||
if (k == 3)
|
||||
k = 0;
|
||||
long v1 = tris(i, j);
|
||||
long v2 = tris(i, k);
|
||||
std::set<long> hash {v1, v2};
|
||||
hash_list.push_back(hash);
|
||||
if (v1 < v2)
|
||||
hash_map[hash] = std::vector<long>{v1, v2, 0};
|
||||
else
|
||||
hash_map[hash] = std::vector<long>{v2, v1, 0};
|
||||
}
|
||||
}
|
||||
for (auto & hash: hash_list)
|
||||
hash_map[hash][2] += 1;
|
||||
|
||||
for (auto &hash: hash_map)
|
||||
{
|
||||
if (hash.second[2] == 1)
|
||||
{
|
||||
long v0 = hash.second[0];
|
||||
long v1 = hash.second[1];
|
||||
|
||||
neighbour_map[v0].push_back(v1);
|
||||
neighbour_map[v1].push_back(v0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while (neighbour_map.size() != 0)
|
||||
{
|
||||
long start_index = neighbour_map.begin()->first;
|
||||
long close_index = start_index;
|
||||
long next_index = neighbour_map[start_index][1];
|
||||
long temporary_next;
|
||||
edge_vector_0.clear();
|
||||
edge_vector_0.push_back(close_index);
|
||||
edge_vector_0.push_back(start_index);
|
||||
neighbour_map.erase(start_index);
|
||||
edge_vector_0.push_back(next_index);
|
||||
while (next_index != close_index)
|
||||
{
|
||||
temporary_next = neighbour_map[next_index][0];
|
||||
if (temporary_next != start_index)
|
||||
{
|
||||
start_index = next_index;
|
||||
next_index = temporary_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
start_index = next_index;
|
||||
next_index = neighbour_map[start_index][1];
|
||||
}
|
||||
neighbour_map.erase(start_index);
|
||||
edge_vector_0.push_back(next_index);
|
||||
|
||||
}
|
||||
edge_vector.push_back(edge_vector_0);
|
||||
}
|
||||
std::vector<ColMat<double, 3>> edges;
|
||||
for (auto &edge: edge_vector)
|
||||
{
|
||||
ColMat<double, 3> edge_vertices;
|
||||
edge_vertices.resize(edge.size(), 3);
|
||||
int i = 0;
|
||||
for (auto index: edge)
|
||||
{
|
||||
edge_vertices.row(i) = vertices.row(index);
|
||||
i++;
|
||||
}
|
||||
edges.push_back(edge_vertices);
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
FaceUnwrapper::FaceUnwrapper(const TopoDS_Face& face)
|
||||
{
|
||||
int i = 0;
|
||||
long i = 0;
|
||||
// transform to nurbs:
|
||||
TopLoc_Location location;
|
||||
|
||||
@@ -76,15 +171,18 @@ FaceUnwrapper::FaceUnwrapper(const TopoDS_Face& face)
|
||||
|
||||
void FaceUnwrapper::findFlatNodes()
|
||||
{
|
||||
std::vector<long> fixed_pins;
|
||||
std::vector<long> fixed_pins; //TODO: INPUT
|
||||
LscmRelax mesh_flattener(this->xyz_nodes.transpose(), this->tris.transpose(), fixed_pins);
|
||||
mesh_flattener.lscm();
|
||||
mesh_flattener.relax(0.9);
|
||||
this->ze_nodes = mesh_flattener.flat_vertices.transpose();
|
||||
}
|
||||
|
||||
ColMat<double, 3> FaceUnwrapper::interpolateNurbsFace(const TopoDS_Face& face)
|
||||
ColMat<double, 3> FaceUnwrapper::interpolateFlatFace(const TopoDS_Face& face)
|
||||
{
|
||||
if (this->uv_nodes.size() == 0)
|
||||
throw(std::runtime_error("no uv-coordinates found, interpolating with nurbs is only possible if the Flattener was constructed with a nurbs."));
|
||||
|
||||
// extract xyz poles, knots, weights, degree
|
||||
const Handle(Geom_Surface) &_surface = BRep_Tool::Surface(face);
|
||||
const Handle(Geom_BSplineSurface) &_bspline = Handle(Geom_BSplineSurface)::DownCast(_surface);
|
||||
@@ -93,10 +191,10 @@ ColMat<double, 3> FaceUnwrapper::interpolateNurbsFace(const TopoDS_Face& face)
|
||||
|
||||
Eigen::VectorXd weights;
|
||||
weights.resize(_bspline->NbUPoles() * _bspline->NbVPoles());
|
||||
int i = 0;
|
||||
for (int u=1; u <= _bspline->NbUPoles(); u++)
|
||||
long i = 0;
|
||||
for (long u=1; u <= _bspline->NbUPoles(); u++)
|
||||
{
|
||||
for (int v=1; v <= _bspline->NbVPoles(); v++)
|
||||
for (long v=1; v <= _bspline->NbVPoles(); v++)
|
||||
{
|
||||
weights[i] = _bspline->Weight(u, v);
|
||||
i++;
|
||||
@@ -107,11 +205,11 @@ ColMat<double, 3> FaceUnwrapper::interpolateNurbsFace(const TopoDS_Face& face)
|
||||
Eigen::VectorXd v_knots;
|
||||
u_knots.resize(_uknots.Size());
|
||||
v_knots.resize(_vknots.Size());
|
||||
for (int u=1; u <= _uknots.Size(); u++)
|
||||
for (long u=1; u <= _uknots.Size(); u++)
|
||||
{
|
||||
u_knots[u - 1] = _uknots.Value(u);
|
||||
}
|
||||
for (int v=1; v <= _vknots.Size(); v++)
|
||||
for (long v=1; v <= _vknots.Size(); v++)
|
||||
{
|
||||
v_knots[v - 1] = _vknots.Value(v);
|
||||
}
|
||||
@@ -134,9 +232,22 @@ ColMat<double, 3> FaceUnwrapper::interpolateNurbsFace(const TopoDS_Face& face)
|
||||
}
|
||||
|
||||
|
||||
FaceUnwrapper::FaceUnwrapper(ColMat< double, int(3) > xyz_nodes, ColMat< long int, int(3) > tris)
|
||||
{
|
||||
this->tris = tris;
|
||||
this->xyz_nodes = xyz_nodes;
|
||||
|
||||
}
|
||||
|
||||
std::vector<ColMat<double, 3>> FaceUnwrapper::getFlatBoundaryNodes()
|
||||
{
|
||||
if (this->ze_nodes.size() == 0)
|
||||
throw(std::runtime_error("flat vertices not xet computed"));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ColMat<double, 3> flat_vertices;
|
||||
flat_vertices.resize(this->ze_nodes.rows(), 3);
|
||||
flat_vertices.setZero();
|
||||
flat_vertices.col(0) << this->ze_nodes.col(0);
|
||||
flat_vertices.col(1) << this->ze_nodes.col(1);
|
||||
return getBoundaries(flat_vertices, this->tris);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
// idea:
|
||||
// - unwrap any meshed shells and output a 2d face (meshing is done externally)
|
||||
// - unwrap faces which are nurbs and return nurbs (no cuts, meshing internally)
|
||||
|
||||
|
||||
#ifndef MESHFLATTENING
|
||||
#define MESHFLATTENING
|
||||
|
||||
@@ -33,6 +38,8 @@
|
||||
#include <Eigen/SparseCore>
|
||||
#include <Eigen/QR>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
typedef Eigen::Vector3d Vector3;
|
||||
typedef Eigen::Vector2d Vector2;
|
||||
@@ -47,12 +54,17 @@ using RowMat = Eigen::Matrix<type, size, Eigen::Dynamic>;
|
||||
typedef Eigen::Triplet<double> trip;
|
||||
typedef Eigen::SparseMatrix<double> spMat;
|
||||
|
||||
|
||||
std::vector<ColMat<double, 3>> getBoundaries(ColMat<double, 3> vertices, ColMat<long, 3> tris);
|
||||
|
||||
class FaceUnwrapper{
|
||||
nurbs::NurbsBase2D nu;
|
||||
public:
|
||||
FaceUnwrapper(const TopoDS_Face & face);
|
||||
FaceUnwrapper(ColMat<double, 3> xyz_nodes, ColMat<long, 3> tris);
|
||||
void findFlatNodes();
|
||||
ColMat<double, 3> interpolateNurbsFace(const TopoDS_Face& face);
|
||||
ColMat<double, 3> interpolateFlatFace(const TopoDS_Face& face);
|
||||
std::vector<ColMat<double, 3>> getFlatBoundaryNodes();
|
||||
|
||||
bool use_nurbs = true;
|
||||
// the mesh
|
||||
|
||||
@@ -63,7 +63,7 @@ FaceUnwrapper* FaceUnwrapper_constructor(py::object face)
|
||||
throw std::invalid_argument("FaceUnwrapper should be initialized with Part.Face");
|
||||
}
|
||||
|
||||
ColMat<double, 3> interpolateNurbsFacePy(FaceUnwrapper& instance, py::object face)
|
||||
ColMat<double, 3> interpolateFlatFacePy(FaceUnwrapper& instance, py::object face)
|
||||
{
|
||||
std::cout << face.ptr()->ob_type->tp_name << std::endl;
|
||||
std::cout << Part::TopoShapeFacePy::Type.tp_name << std::endl;
|
||||
@@ -71,7 +71,7 @@ ColMat<double, 3> interpolateNurbsFacePy(FaceUnwrapper& instance, py::object fac
|
||||
{
|
||||
const Part::TopoShapeFacePy* f = static_cast<Part::TopoShapeFacePy*>(face.ptr());
|
||||
const TopoDS_Face& myFace = TopoDS::Face(f->getTopoShapePtr()->getShape());
|
||||
return instance.interpolateNurbsFace(myFace);
|
||||
return instance.interpolateFlatFace(myFace);
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("FaceUnwrapper.interpolateNurbs should be initialized with Part.Face");
|
||||
@@ -114,8 +114,10 @@ PYBIND11_MODULE(flatmesh, m)
|
||||
|
||||
py::class_<FaceUnwrapper>(m, "FaceUnwrapper")
|
||||
.def(py::init(&FaceUnwrapper_constructor))
|
||||
.def(py::init<ColMat<double, 3>, ColMat<long, 3>>())
|
||||
.def("findFlatNodes", &FaceUnwrapper::findFlatNodes)
|
||||
.def("interpolateNurbsFace", &interpolateNurbsFacePy)
|
||||
.def("interpolateFlatFace", &interpolateFlatFacePy)
|
||||
.def("getFlatBoundaryNodes", &FaceUnwrapper::getFlatBoundaryNodes)
|
||||
.def_readonly("tris", &FaceUnwrapper::tris)
|
||||
.def_readonly("nodes", &FaceUnwrapper::xyz_nodes)
|
||||
.def_readonly("uv_nodes", &FaceUnwrapper::uv_nodes)
|
||||
|
||||
@@ -86,3 +86,12 @@ SET_BIN_DIR(MeshPartGui MeshPartGui /Mod/MeshPart)
|
||||
SET_PYTHON_PREFIX_SUFFIX(MeshPartGui)
|
||||
|
||||
INSTALL(TARGETS MeshPartGui DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
|
||||
|
||||
SET(FLATMESH_PY_SRCS
|
||||
MeshFlatteningCommand.py
|
||||
)
|
||||
|
||||
fc_copy_sources(MeshPartGui "${CMAKE_BINARY_DIR}/Mod/MeshPart" ${FLATMESH_PY_SRCS})
|
||||
INSTALL(FILES ${FLATMESH_PY_SRCS} DESTINATION Mod/MeshPart)
|
||||
|
||||
42
src/Mod/MeshPart/Gui/MeshFlatteningCommand.py
Normal file
42
src/Mod/MeshPart/Gui/MeshFlatteningCommand.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import numpy as np
|
||||
import Mesh
|
||||
import FreeCAD as app
|
||||
import flatMesh
|
||||
import Part
|
||||
|
||||
|
||||
|
||||
class BaseCommand(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
class CreateFlatMesh(BaseCommand):
|
||||
"""create an involute gear"""
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'not_yet.svg', 'MenuText': 'unwrap mesh', 'ToolTip': 'find a flat representation of a mesh'}
|
||||
|
||||
def Activated(self):
|
||||
obj = Gui.Selection.getSelection()[0] # obj must be a Mesh (Mesh-Design->Meshes->Create-Mesh)
|
||||
mesh = Mesh.Mesh(obj.Mesh) # copy of the mesh to set new vertices later on
|
||||
points = np.array([[i.x, i.y, i.z] for i in obj.Mesh.Points])
|
||||
faces = np.array([list(i) for i in obj.Mesh.Topology[1]])
|
||||
flattener = flatMesh.FaceUnwrapper(points, faces)
|
||||
flattener.findFlatNodes()
|
||||
boundaries = flattener.getFlatBoundaryNodes()
|
||||
wires = []
|
||||
for edge in boundaries:
|
||||
pi = Part.makePolygon([app.Vector(*node) for node in edge])
|
||||
wires.append(pi)
|
||||
Part.show(Part.Wire(wires))
|
||||
|
||||
|
||||
def initialize():
|
||||
Gui.addCommand('CreateFlatMesh', CreateFlatMesh())
|
||||
return ["CreateFlatMesh"]
|
||||
@@ -30,7 +30,6 @@
|
||||
#***************************************************************************/
|
||||
|
||||
|
||||
|
||||
class MeshPartWorkbench ( Workbench ):
|
||||
"MeshPart workbench object"
|
||||
Icon = """
|
||||
|
||||
Reference in New Issue
Block a user