mlivesu / cinolib

A generic programming header only C++ library for processing polygonal and polyhedral meshes
MIT License
930 stars 101 forks source link

Controlling the number of cells and the optimization parameters to generate Polyhedral mesh #184

Closed Rumi381 closed 2 months ago

Rumi381 commented 2 months ago

Hi, @mlivesu. I am new to Cinolib. I tried the following code to create a Polyhedral mesh for a given input mesh. I have some queries regarding this. Would you please help me with the following questions?:

1) When we are generating the tetrahedral mesh with 'tetgen_wrap(serialized_xyz_from_vec3d(m_in.vector_verts()), serialized_vids_from_polys(m_in.vector_polys()), edges, opt, verts, tets); DrawableTetmesh<> m_tet(verts, tets)' or Polyhedral mesh with 'DrawablePolyhedralmesh<> m_poly; dual_mesh(m_tet, m_poly, true)', can we control the number of cells generated in both resulting meshes?

2) Is any CVT (Centroidal Voronoi Tessellation) optimization automatically performing behind the scenes? If so, can we control the level of CVT optimization with any arguments or parameters?

3) According to the latest version of Cinolib, the library supports only the '.mesh', '.hedra', and '.ovm' file types to save the Polyhedral mesh. I am confused about the content of the '.mesh' file format (which contains the number of hexahedra at the bottom of the file!) and the '.hedra' or '.ovm' file format for the same Polyhedral mesh. Is it possible to save the polyhedral mesh in '.obj' format, which is generally used for such cases in libraries like 'libgl' and 'Geogram'? Can you suggest any software that can open, visualize, and save the '.hedra' or '.ovm' file types and save them as a different file format?

It would be great if you could guide me on the above queries. Here is the code I tried:

include <cinolib/meshes/meshes.h>

include <cinolib/gl/glcanvas.h>

include <cinolib/tetgen_wrap.h>

include <cinolib/vector_serialization.h>

include <cinolib/dual_mesh.h>

include <cinolib/string_utilities.h>

int main(int argc, char **argv) { using namespace cinolib;

std::string s = (argc==2) ? std::string(argv[1]) : std::string(DATA_PATH) + "/3holes.obj";
std::string basename = get_file_path(s,true);

DrawableTrimesh<> m_in(s.c_str());
m_in.rotate(vec3d(0,0,1), M_PI_2);
m_in.rotate(vec3d(0,1,0), to_rad(15.0));
m_in.update_bbox();

// make tetmesh
std::vector<uint>   edges, tets;
std::vector<double> verts;
double vol_thresh = 0.01 * m_in.bbox().diag(); // force tets to be smaller than 5% of bbox diag
char opt[100];
sprintf(opt, "YQqa%f", vol_thresh);
tetgen_wrap(serialized_xyz_from_vec3d(m_in.vector_verts()), serialized_vids_from_polys(m_in.vector_polys()), edges, opt, verts, tets);
DrawableTetmesh<> m_tet(verts, tets);

m_tet.save((basename+"_tetrahedral.mesh").c_str());
m_tet.save((basename+"_tetrahedral.hedra").c_str());
m_tet.save((basename+"_tetrahedral.ovm").c_str());

m_tet.translate(vec3d(m_in.bbox().delta_x()*1.2,0,0));
m_tet.update_bbox();

// make polyhedral (voronoi) mesh
DrawablePolyhedralmesh<> m_poly;
dual_mesh(m_tet, m_poly, true);

m_poly.save((basename+"_polyhedral.mesh").c_str());
m_poly.save((basename+"_polyhedral.hedra").c_str());
m_poly.save((basename+"_polyhedral.ovm").c_str());

m_poly.translate(vec3d(m_in.bbox().delta_x()*1.2,0,0));
m_poly.update_bbox();

MeshSlicer slicer;
slicer.Z_thresh = 0.6f;
slicer.slice(m_in);
slicer.slice(m_tet);
slicer.slice(m_poly);

m_in.updateGL();
m_tet.updateGL();
m_poly.updateGL();

GLcanvas gui;
gui.push(&m_in);
gui.push(&m_tet);
gui.push(&m_poly);

return gui.launch();

}

mlivesu commented 2 months ago

Hi, I'll try to answer your questions one by one

1) CinoLib does not natively implement any tetmeshing facility. For this part it only exposes a wrap to the Tetgen library. You can control the parameters with which Tetgen is called through a dedicated string (YQqa%f in your case). You may want to refer to this page to understand how to play with these parameters. Tetgen does not allow you to precisely select how many vertices/tets you will have in your output mesh. This is because there exist so called indecomposable polyhedra that cannot be tetrahedralized without adding additional (Steiner) points. A good read for this topic is the article On Indecomposable Polyhedra and the Number of Steiner Points, from the same author of the Tetgen library. Regarding the number of cells in your final polyhedral mesh: this is obtained by dualizing the primal tetrahedral mesh, so the number of vertices in the tetmesh will correspond exactly to the number of cells in the polyhedral mesh. As a consequence there is no way for you to prescribe a fixed number of output cells

2) there is no CVT. If you are interested in such a thing I suggest you take a look at Bruno Levy's geogram library. This is a great tool for Voronoi meshing, both in 2D and 3D

3) The obj file format is indeed supported by CinoLib, but this format is for surface meshes only! You cannot encode a volumetric mesh in an obj file. The ovm files are supported by the OpenVolumeMesh library, which introduced this format, and also by Geogram as far as I know. The hedra file format is something that I made up for personal use and is not supported by any other toolkit that I am aware of. There is limited interoperability for general polyhedral meshes so far. I think the only possible (though limited) choice is to rely on the ovm file format.

Rumi381 commented 2 months ago

Thank you, @mlivesu, for the detailed response of my questions. As follow up questions, would you please guide me on the following questions?:

1) Which clipping method is happening behind the scenes? I see your code generate more smoother surface compared to Geogram.

2) If I want to create a standalone application for generating the Polygonal mesh in 2D and Polyhedral mesh in 3D, not use the full fledged cinolib library, is it possible? If so, which files do I need to achieve this?

mlivesu commented 2 months ago
  1. Which clipping method is happening behind the scenes? I see your code generate more smoother surface compared to Geogram.

you mean for the dual meshing? I wrote that code long time ago, but I don't recall me doing anything special. I guess I am doing the standard thing. Not sure what Bruno does in Geogram. Can you provide a visual example?

  1. If I want to create a standalone application for generating the Polygonal mesh in 2D and Polyhedral mesh in 3D, not use the full fledged cinolib library, is it possible? If so, which files do I need to achieve this?

It's hard to answer this one. There are many includes happening inside cinolib. I would start from the files that contain what you need, delete everything else, and then iteratively try to compile and restore all the files that are requested until your compiler is happy

Rumi381 commented 2 months ago

In Geogram, the boundary cells are clipped against the surface triangles, which is why the surface of the resulting mesh has a triangulated texture (not Voronoi texture), as shown in the figure below: Line_texure

Though there is a parameter to avoid the triangulated texture, the result becomes very rough, as shown in the figure below: Polyhedral_mesh

But for the same input mesh, I see the smooth Voronoi texture on the surface with cinolib, as shown in the figure below: VoronoiTexture

I am sure whether it is how you set up the visualization in a certain way or how your clipping is being done to generate the smooth Voronoi surface.

By the way, is there any plan to publish a Python API/Wrapper for cinolib?

mlivesu commented 2 months ago

it might just be a matter of shading (flat vs smooth). Also different canvases use different openGL setups, so it becomes difficult to distinguish between differences in the actual geometry from differences in the shading/lighting. If you can share the actual OBJs I can do a precise check and give you the correct answer

for the python bindings: I'd love to add that (and many other things!) to cinolib, but I have little time to spend on such improvements. So eventually yes, but I am not currently working at it and I cannot say when this will happen :)

Rumi381 commented 2 months ago

Hi, @mlivesu. The attached zip folder contains the following files: 1) 3holes.obj -> the input mesh 2) 3holes_poly.obj -> output mesh from Geogram without any boundary surface processing 3) 3holes_poly_90.obj -> output mesh from Geogram with normal_angle_threshold=90 4,5,6) -> output meshes from cinolib for the same input mesh.

Please let me know your findings. PolyhedralMesh.zip

mlivesu commented 2 months ago

Hi, I gave a quick look and I feel we are comparing apples and oranges here.

Geogram is computing a CVT by sampling the input surface with a fixed number of samples (how many? it should be a parameter). Cinolib is just dualizing the input triangle mesh, creating as many cells as the number of input vertices.

Overall there seems to be many more input vertices than the number of CVT seeds used in Geogram. This is why cinolib's results look smoother.

Rumi381 commented 2 months ago

Got it! Thank you, @mlivesu. And I am eagerly waiting for the Python wrapper for cinolib. I hope you find some time soon.

mlivesu commented 2 months ago

I'll keep you posted :) I am closing this issue as it seems resolved.