diff --git a/src/Mod/Mesh/InitGui.py b/src/Mod/Mesh/InitGui.py index bcfd0c1a26..fd4b85696a 100644 --- a/src/Mod/Mesh/InitGui.py +++ b/src/Mod/Mesh/InitGui.py @@ -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" diff --git a/src/Mod/MeshPart/App/CMakeLists.txt b/src/Mod/MeshPart/App/CMakeLists.txt index 212a41bbd4..40931c8fe8 100644 --- a/src/Mod/MeshPart/App/CMakeLists.txt +++ b/src/Mod/MeshPart/App/CMakeLists.txt @@ -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}) +############################################################################ diff --git a/src/Mod/MeshPart/App/MeshFlattening.cpp b/src/Mod/MeshPart/App/MeshFlattening.cpp index 5458d6b3f8..e520a7461e 100644 --- a/src/Mod/MeshPart/App/MeshFlattening.cpp +++ b/src/Mod/MeshPart/App/MeshFlattening.cpp @@ -28,9 +28,104 @@ #include #include +#include +#include +#include +#include + +std::vector> getBoundaries(ColMat vertices, ColMat tris) +{ + // get a hashtable for all edges + // e: v1, v2, num + std::map, std::vector> hash_map; + std::vector> hash_list; + std::map> neighbour_map; + std::vector edge_vector_0; + std::vector> edge_vector; + + + for (long i=0; i hash {v1, v2}; + hash_list.push_back(hash); + if (v1 < v2) + hash_map[hash] = std::vector{v1, v2, 0}; + else + hash_map[hash] = std::vector{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> edges; + for (auto &edge: edge_vector) + { + ColMat 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 fixed_pins; + std::vector 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 FaceUnwrapper::interpolateNurbsFace(const TopoDS_Face& face) +ColMat 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 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 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 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> FaceUnwrapper::getFlatBoundaryNodes() +{ + if (this->ze_nodes.size() == 0) + throw(std::runtime_error("flat vertices not xet computed")); - - - - + ColMat 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); +} diff --git a/src/Mod/MeshPart/App/MeshFlattening.h b/src/Mod/MeshPart/App/MeshFlattening.h index 430190f669..75a641fb2a 100644 --- a/src/Mod/MeshPart/App/MeshFlattening.h +++ b/src/Mod/MeshPart/App/MeshFlattening.h @@ -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 #include +#include + typedef Eigen::Vector3d Vector3; typedef Eigen::Vector2d Vector2; @@ -47,12 +54,17 @@ using RowMat = Eigen::Matrix; typedef Eigen::Triplet trip; typedef Eigen::SparseMatrix spMat; + +std::vector> getBoundaries(ColMat vertices, ColMat tris); + class FaceUnwrapper{ nurbs::NurbsBase2D nu; public: FaceUnwrapper(const TopoDS_Face & face); + FaceUnwrapper(ColMat xyz_nodes, ColMat tris); void findFlatNodes(); - ColMat interpolateNurbsFace(const TopoDS_Face& face); + ColMat interpolateFlatFace(const TopoDS_Face& face); + std::vector> getFlatBoundaryNodes(); bool use_nurbs = true; // the mesh diff --git a/src/Mod/MeshPart/App/MeshFlatteningPy.cpp b/src/Mod/MeshPart/App/MeshFlatteningPy.cpp index b23d38d255..e255ea0c72 100644 --- a/src/Mod/MeshPart/App/MeshFlatteningPy.cpp +++ b/src/Mod/MeshPart/App/MeshFlatteningPy.cpp @@ -63,7 +63,7 @@ FaceUnwrapper* FaceUnwrapper_constructor(py::object face) throw std::invalid_argument("FaceUnwrapper should be initialized with Part.Face"); } -ColMat interpolateNurbsFacePy(FaceUnwrapper& instance, py::object face) +ColMat 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 interpolateNurbsFacePy(FaceUnwrapper& instance, py::object fac { const Part::TopoShapeFacePy* f = static_cast(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_(m, "FaceUnwrapper") .def(py::init(&FaceUnwrapper_constructor)) + .def(py::init, ColMat>()) .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) diff --git a/src/Mod/MeshPart/Gui/CMakeLists.txt b/src/Mod/MeshPart/Gui/CMakeLists.txt index 112af4999b..943449547c 100644 --- a/src/Mod/MeshPart/Gui/CMakeLists.txt +++ b/src/Mod/MeshPart/Gui/CMakeLists.txt @@ -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) diff --git a/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py b/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py new file mode 100644 index 0000000000..5667fe4cb6 --- /dev/null +++ b/src/Mod/MeshPart/Gui/MeshFlatteningCommand.py @@ -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"] \ No newline at end of file diff --git a/src/Mod/MeshPart/InitGui.py b/src/Mod/MeshPart/InitGui.py index 45cbef6f54..82a917f2ea 100644 --- a/src/Mod/MeshPart/InitGui.py +++ b/src/Mod/MeshPart/InitGui.py @@ -30,7 +30,6 @@ #***************************************************************************/ - class MeshPartWorkbench ( Workbench ): "MeshPart workbench object" Icon = """