HMesh Module¶
hmesh ¶
The hmesh module provides an halfedge based mesh representation. In addition this module contains a variety of functions for mesh manipulation and inspection. Specifcally, the module contains functions for mesh simplification, smoothing, subdivision, and editing of vertices, faces, and edges. The volumetric_isocontour function allows us to create a polygonal mesh from volumetric data by isocontouring. The skeleton_to_feq function allows us to turn a skeleton graph into a Face Extrusion Quad Mesh.
Manifold ¶
The Manifold class represents a halfedge based mesh. It is maybe a bit grand to call a mesh class Manifold, but meshes based on the halfedge representation are manifold (if we ignore a few corner cases) unlike some other representations. This class contains a number of methods for mesh manipulation and inspection. Note also that numerous further functions are available to manipulate meshes stored as Manifolds.
Many of the functions below accept arguments called hid, fid, or vid. These are simply indices of halfedges, faces and vertices, respectively: integer numbers that identify the corresponding mesh element. Using a plain integer to identify a mesh entity means that, for instance, a vertex index can also be used as an index into, say, a NumPy array without any conversion.
Source code in pygel3d/hmesh.py
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | |
__init__ ¶
Construct a Manifold object. If orig is None, a new empty Manifold is created. If orig is a Manifold, a copy of it is created. If orig is a c_void_p, it is assumed to be a pointer to a Manifold object created in C++. In this case, the object is not copied, but the pointer is used directly. If orig is anything else, a TypeError is raised.
Source code in pygel3d/hmesh.py
from_triangles
classmethod
¶
Given a list of vertices and triangles (faces), this function produces a Manifold mesh.
Source code in pygel3d/hmesh.py
from_points
classmethod
¶
from_points(pts: ArrayLike, xaxis: ArrayLike = np.array([1, 0, 0]), yaxis: ArrayLike = np.array([0, 1, 0])) -> Self
This function computes the Delaunay triangulation of pts. You need to specify xaxis and yaxis if they are not canonical. The function returns a Manifold with the resulting triangles. Clearly, this function will give surprising results if the surface represented by the points is not well represented as a 2.5D surface, aka a height field.
Source code in pygel3d/hmesh.py
merge_with ¶
Merge this Manifold with another one given as the argument. This function does not return anything. It simply combines the two meshes in the Manifold on which the method is called.
Source code in pygel3d/hmesh.py
add_face ¶
Add a face to the Manifold. This function takes a list of 3D points, pts, as argument and creates a face in the mesh with those points as vertices. The function returns the index of the created face.
Source code in pygel3d/hmesh.py
positions ¶
Retrieve an array containing the vertex positions of the Manifold. It is not a copy: any changes are made to the actual vertex positions.
Source code in pygel3d/hmesh.py
no_allocated_vertices ¶
Number of vertices. This number could be higher than the number of actually used vertices, but corresponds to the size of the array allocated for vertices.
Source code in pygel3d/hmesh.py
no_allocated_faces ¶
Number of faces. This number could be higher than the number of actually used faces, but corresponds to the size of the array allocated for faces.
Source code in pygel3d/hmesh.py
no_allocated_halfedges ¶
Number of halfedges. This number could be higher than the number of actually used halfedges, but corresponds to the size of the array allocated for halfedges.
Source code in pygel3d/hmesh.py
vertices ¶
faces ¶
halfedges ¶
circulate_vertex ¶
Circulate a vertex. Passed a vertex index, vid, and second argument, mode='f', this function will return an iterable with all faces incident on vid arranged in counter clockwise order. Similarly, if mode is 'h', incident halfedges (outgoing) are returned, and for mode = 'v', all neighboring vertices are returned.
Source code in pygel3d/hmesh.py
circulate_face ¶
Circulate a face. Passed a face index, fid, and second argument, mode='f', this function will return an iterable with all faces that share an edge with fid (in counter clockwise order). If the argument is mode='h', the halfedges themselves are returned. For mode='v', the incident vertices of the face are returned.
Source code in pygel3d/hmesh.py
next_halfedge ¶
prev_halfedge ¶
opposite_halfedge ¶
incident_face ¶
incident_vertex ¶
remove_vertex ¶
Remove vertex vid from the Manifold. This function merges all faces around the vertex into one and then removes this resulting face.
remove_face ¶
Removes a face, fid, from the Manifold. If it is an interior face it is simply replaced by an invalid index. If the face contains boundary edges, these are removed. Situations may arise where the mesh is no longer manifold because the situation at a boundary vertex is not homeomorphic to a half disk. This, we can probably ignore since from the data structure point of view it is not really a problem that a vertex is incident on two holes - a hole can be seen as a special type of face. The function returns false if the index of the face is not valid, otherwise the function must complete.
Source code in pygel3d/hmesh.py
remove_edge ¶
Remove an edge, hid, from the Manifold. This function will remove the faces on either side and the edge itself in the process. Thus, it is a simple application of remove_face.
Source code in pygel3d/hmesh.py
vertex_in_use ¶
check if vertex, vid, is in use. This function returns true if the id corresponds to a vertex that is currently in the mesh and false otherwise. vid could be invalid or it could correspond to a vertex which is not active. The function returns false in both cases. It is important to call this function before using a vertex id (vid) which might have been invalidated by a previous operation on the mesh.
Source code in pygel3d/hmesh.py
face_in_use ¶
check if face, fid, is in use. This function returns true if the id corresponds to a face that is currently in the mesh and false otherwise. fid could be invalid or it could correspond to a face which is not active. The function returns false in both cases. It is important to call this function before using a face id (fid) which might have been invalidated by a previous operation on the mesh.
Source code in pygel3d/hmesh.py
halfedge_in_use ¶
check if halfedge hid is in use. This function returns true if the id corresponds to a halfedge that is currently in the mesh and false otherwise. hid could be invalid or it could correspond to a halfedge which is not active. The function returns false in both cases. It is important to call this function before using a halfedge id (hid) which might have been invalidated by a previous operation on the mesh.
Source code in pygel3d/hmesh.py
flip_edge ¶
Flip the edge, hid, separating two faces. The function first verifies that the edge is flippable. This entails making sure that all of the following are true. 1. adjacent faces are triangles. 2. neither end point has valency three or less. 3. the vertices that will be connected are not already. If the tests are passed, the flip is performed and the function returns True. Otherwise False.
Source code in pygel3d/hmesh.py
collapse_edge ¶
Collapse an edge hid. The vertex incident_vertex(opposite_halfedge) is the one being removed while incident_vertex(hid) survives. avg_vertices indicates whether the positions of the two vertices should be averaged when collapsed. Before collapsing hid, a number of tests are made:
- For the two vertices adjacent to the edge, we generate a list of all their neighbouring vertices. We then generate a list of the vertices that occur in both these lists. That is, we find all vertices connected by edges to both endpoints of the edge and store these in a list.
- For both faces incident on the edge, check whether they are triangular. If this is the case, the face will be removed, and it is ok that the the third vertex is connected to both endpoints. Thus the third vertex in such a face is removed from the list generated in 1.
- If the list is now empty, all is well. Otherwise, there would be a vertex in the new mesh with two edges connecting it to the same vertex. Return false.
- TETRAHEDRON TEST: If the valency of both vertices is three, and the incident faces are triangles, we also disallow the operation. Reason: A vertex valency of two and two triangles incident on the adjacent vertices makes the construction collapse.
- VALENCY 4 TEST: If a triangle is adjacent to the edge being collapsed, it disappears. This means the valency of the remaining edge vertex is decreased by one. A valency two vertex reduced to a valency one vertex is considered illegal.
- PREVENT MERGING HOLES: Collapsing an edge with boundary endpoints and valid faces results in the creation where two holes meet. A non manifold situation. We could relax this...
- New test: if the same face is in the one-ring of both vertices but not adjacent to the common edge, then the result of a collapse would be a one ring where the same face occurs twice. This is disallowed as the resulting face would be non-simple. If the tests are passed, the collapse is performed and the function returns True. Otherwise False.
Source code in pygel3d/hmesh.py
split_face_by_edge ¶
Split a face. The face, fid, is split by creating an edge with endpoints v0 and v1 (the next two arguments). The vertices of the old face between v0 and v1 (in counter clockwise order) continue to belong to fid. The vertices between v1 and v0 belong to the new face. A handle to the new face is returned.
Source code in pygel3d/hmesh.py
split_face_by_vertex ¶
Split a polygon, fid, by inserting a vertex at the barycenter. This function is less likely to create flipped triangles than the split_face_triangulate function. On the other hand, it introduces more vertices and probably makes the triangles more acute. The vertex id of the inserted vertex is returned.
Source code in pygel3d/hmesh.py
split_edge ¶
Insert a new vertex on halfedge hid. The new halfedge is insterted as the previous edge to hid. The vertex id of the inserted vertex is returned.
stitch_boundary_edges ¶
Stitch two halfedges. Two boundary halfedges, h0 and h1, can be stitched together. This can be used to build a complex mesh from a bunch of simple faces.
Source code in pygel3d/hmesh.py
merge_faces ¶
Merges two faces into a single polygon. The merged faces are those shared by the edge for which hid is one of the two corresponding halfedges. This function returns true if the merging was possible and false otherwise. Currently merge only fails if the mesh is already illegal. Thus it should, in fact, never fail.
Source code in pygel3d/hmesh.py
close_hole ¶
Close hole given by hid (i.e. the face referenced by hid). Returns index of the created face or the face that was already there if, in fact, hid was not next to a hole.
Source code in pygel3d/hmesh.py
cleanup ¶
Remove unused items from Mesh. This function remaps all vertices, halfedges and faces such that the arrays do not contain any holes left by unused mesh entities. It is a good idea to call this function when a mesh has been simplified or changed in other ways such that mesh entities have been removed. However, note that it invalidates any attributes that you might have stored in auxilliary arrays.
Source code in pygel3d/hmesh.py
is_halfedge_at_boundary ¶
Returns True if hid is a boundary halfedge, i.e. face on either side is invalid.
is_vertex_at_boundary ¶
edge_length ¶
valency ¶
face_normal ¶
Compute the normal of a face fid. The normal is the average of the normals of the triangles formed from the centroid of face fix and each edge of the face.
Source code in pygel3d/hmesh.py
vertex_normal ¶
Returns the vertex normal of vid. The vertex normal is computed as the angle weighted average of the normals of the incident faces.
Source code in pygel3d/hmesh.py
mixed_area ¶
Returns the mixed area of vertex vid. The mixed area is an approximation of the Voronoi area of the vertex, i.e. the area of the mesh that is closer to vid than to any other vertex. For non-obtuse triangles, we can compute the part of the Voronoi area inside the triangle exacxtly, but for obtuse triangles the Voronoi area extends outside the triangle, and we approximate it.
Source code in pygel3d/hmesh.py
gaussian_curvature ¶
Returns the Gaussian curvature of vertex vid. The curvature is computed as the ratio of the angle defect and the mixed area of vid. The angle defect is 2*pi minus the sum of angles at the vertex.
Source code in pygel3d/hmesh.py
mean_curvature ¶
Returns the mean curvature of vertex vid. The curvature is computed as the ratio of the length of the mean curvaure normal to the mixed area of vid. The mean curvature normal is obtained with the cotan formula, and the sign is positive if the mean curvature normal points in the same direction as the vertex normal and negative otherwise.
Source code in pygel3d/hmesh.py
principal_curvatures ¶
Returns the principal curvatures of vertex vid. The principal curvatures are computed by fitting a quadratic polynomial surface to the vertex and its one-ring neighbours. From the coefficients, we obtain the shape operator and the principal curvatures are the eigenvalues of the shape operator. The directions are the eigenvectors. The function returns a tuple consiting of four values: min and max principal curvature followed by the corresponding principal directions as 3D vectors
Source code in pygel3d/hmesh.py
connected ¶
Returns true if the two argument vertices, v0 and v1, are in each other's one-rings.
no_edges ¶
area ¶
perimeter ¶
MeshDistance ¶
This class allows you to compute the distance from any point in space to a Manifold (which must be triangulated). The constructor creates an instance based on a specific mesh, and the signed_distance function computes the actual distance.
Source code in pygel3d/hmesh.py
signed_distance ¶
Compute the signed distance from each point in pts to the mesh stored in this class instance. pts should be convertible to a length N>=1 array of 3D points. The function returns an array of N distance values with a single distance for each point. The distance corresponding to a point is positive if the point is outside and negative if inside. The upper parameter can be used to threshold how far away the distance is of interest.
Source code in pygel3d/hmesh.py
ray_inside_test ¶
Check whether each point in pts is inside or outside the stored mesh by casting rays. pts should be convertible to a length N>=1 array of 3D points. Effectively, this is the sign of the distance. In some cases casting (multiple) ray is more robust than using the sign computed locally. Returns an array of N integers which are either 1 or 0 depending on whether the corresponding point is inside (1) or outside (0).
Source code in pygel3d/hmesh.py
intersect ¶
intersect(p0: ArrayLike, dir: ArrayLike, _t: float = 0) -> tuple[float, np.ndarray, np.ndarray] | None
Intersect the ray starting in p0 with direction, dir, with the stored mesh. Returns the point of intersection if there is one, otherwise None.
Source code in pygel3d/hmesh.py
valid ¶
This function performs a series of tests to check that this is a valid manifold. This function is not rigorously constructed but seems to catch all problems so far. The function returns true if the mesh is valid and false otherwise.
Source code in pygel3d/hmesh.py
closed ¶
area ¶
volume ¶
bbox ¶
Returns the min and max corners of the bounding box of Manifold m.
Source code in pygel3d/hmesh.py
bsphere ¶
Calculate the bounding sphere of the manifold m. Returns centre,radius
stitch ¶
Stitch together edges of m whose endpoints coincide geometrically. This function allows you to create a mesh as a bunch of faces and then stitch these together to form a coherent whole. What this function adds is a spatial data structure to find out which vertices coincide. The return value is the number of edges that could not be stitched. Often this is because it would introduce a non-manifold situation.
Source code in pygel3d/hmesh.py
obj_save ¶
off_save ¶
x3d_save ¶
obj_load ¶
Load and return Manifold from Wavefront obj file. Returns None if loading failed.
Source code in pygel3d/hmesh.py
off_load ¶
Load and return Manifold from OFF file. Returns None if loading failed.
ply_load ¶
Load and return Manifold from Stanford PLY file. Returns None if loading failed.
x3d_load ¶
Load and return Manifold from X3D file. Returns None if loading failed.
load ¶
Load a Manifold from an X3D/OBJ/OFF/PLY file. Return the loaded Manifold. Returns None if loading failed.
Source code in pygel3d/hmesh.py
save ¶
Save a Manifold, m, to an X3D/OBJ/OFF file.
Source code in pygel3d/hmesh.py
remove_caps ¶
Remove caps from a manifold, m, consisting of only triangles. A cap is a triangle with two very small angles and an angle close to pi, however a cap does not necessarily have a very short edge. Set the ang_thresh to a value close to pi. The closer to pi the less sensitive the cap removal. A cap is removed by flipping the (long) edge E opposite to the vertex V with the angle close to pi. However, the function is more complex. Read code and document more carefully !!!
Source code in pygel3d/hmesh.py
remove_needles ¶
Remove needles from a manifold, m, consisting of only triangles. A needle is a triangle with a single very short edge. It is moved by collapsing the short edge. The thresh parameter sets the length threshold (in terms of the average edge length in the mesh). If average_positions is true then the collapsed vertex is placed at the average position of the end points.
Source code in pygel3d/hmesh.py
close_holes ¶
This function replaces holes in m by faces. It is really a simple function that just finds all loops of edges next to missing faces.
flip_orientation ¶
Flip the orientation of a mesh, m. After calling this function, normals will point the other way and clockwise becomes counter clockwise
merge_coincident_boundary_vertices ¶
Merge vertices of m that are boundary vertices and coincident. However, if one belongs to the other's one ring or the one rings share a vertex, they will not be merged.
Source code in pygel3d/hmesh.py
minimize_curvature ¶
Minimizes mean curvature of m by flipping edges. Hence, no vertices are moved. This is really the same as dihedral angle minimization, except that we weight by edge length.
Source code in pygel3d/hmesh.py
minimize_dihedral_angle ¶
minimize_dihedral_angle(m: Manifold, max_iter: int = 10000, anneal: bool = False, alpha: bool = False, gamma: float = 4.0)
Minimizes dihedral angles in m by flipping edges. Arguments: max_iter is the maximum number of iterations for simulated annealing. anneal tells us the code whether to apply simulated annealing alpha=False means that we use the cosine of angles rather than true angles (faster) gamma is the power to which the angles are raised.
Source code in pygel3d/hmesh.py
maximize_min_angle ¶
Maximizes the minimum angle of triangles by flipping edges of m. Makes the mesh more Delaunay.
optimize_valency ¶
Tries to achieve valence 6 internally and 4 along edges by flipping edges of m.
randomize_mesh ¶
quadric_simplify ¶
quadric_simplify(m: Manifold, keep_fraction: float, singular_thresh: float = 0.0001, error_thresh: float = 1)
Garland Heckbert simplification of mesh m. keep_fraction is the fraction of vertices to retain. The singular_thresh determines how subtle features are preserved. For values close to 1 the surface is treated as smooth even in the presence of sharp edges of low dihedral angle (angle between normals). Close to zero, the method preserves even subtle sharp features better. The error_thresh is the value of the QEM error at which simplification stops. It is relative to the bounding box size. The default value is 1 meaning that simplification continues until the model has been simplified to a number of vertices approximately equal to keep_fraction times the original number of vertices.
Source code in pygel3d/hmesh.py
average_edge_length ¶
median_edge_length ¶
refine_edges ¶
Split all edges in m which are longer than the threshold (second arg) length. A split edge results in a new vertex of valence two.
cc_split ¶
Perform a Catmull-Clark split on m, i.e. a split where each face is divided into new quadrilateral faces formed by connecting a corner with a point on each incident edge and a point at the centre of the face.
Source code in pygel3d/hmesh.py
loop_split ¶
Perform a loop split on m where each edge is divided into two segments, and four new triangles are created for each original triangle.
root3_subdivide ¶
Leif Kobbelt's subdivision scheme applied to m. A vertex is placed in the center of each face and all old edges are flipped.
rootCC_subdivide ¶
This subdivision scheme creates a vertex inside each original (quad) face of m, producing four triangles. Triangles sharing an old edge are then merged. Two steps produce something similar to Catmull-Clark.
Source code in pygel3d/hmesh.py
butterfly_subdivide ¶
Butterfly subidiviosn on m. An interpolatory scheme. Creates the same connectivity as Loop.
cc_smooth ¶
If called after cc_split, this function completes a step of Catmull-Clark subdivision of m.
cc_subdivide ¶
loop_subdivide ¶
volume_preserving_cc_smooth ¶
This function does the same type of smoothing as in Catmull-Clark subdivision, but to preserve volume it actually performs two steps, and the second step is negative as in Taubin smoothing.
Source code in pygel3d/hmesh.py
regularize_quads ¶
This function smooths a quad mesh by regularizing quads. Essentially, regularization just makes them more rectangular.
Source code in pygel3d/hmesh.py
loop_smooth ¶
taubin_smooth ¶
This function performs Taubin smoothing on the mesh m for iter number of iterations.
laplacian_smooth ¶
This function performs Laplacian smoothing on the mesh m for iter number of iterations. w is the weight applied.
anisotropic_smooth ¶
This function performs anisotropic smoothing on the mesh m for iter number of iterations. A bilateral filtering controlled by sharpness is performed on the face normals followed by a rotation of the faces to match the new normals. The updated vertex positions are the average positions of the corners of the rotated faces. For sharpness==0 the new normal is simply the area weighted average of the normals of incident faces. For sharpness>0 the weight of the neighbouring face normals is a Gaussian function of the angle between the face normals. The greater the sharpness, the more the smoothing is anisotropic.
Source code in pygel3d/hmesh.py
volumetric_isocontour ¶
volumetric_isocontour(data: ArrayLike, bbox_min: ArrayLike | None = None, bbox_max: ArrayLike | None = None, tau: float = 0.0, make_triangles: bool = True, high_is_inside: bool = True, dual_connectivity: bool = False) -> Manifold
Creates a polygonal mesh from volumetric data by isocontouring. The dimensions are given by dims, bbox_min (defaults to [0,0,0] ) and bbox_max (defaults to dims) are the corners of the bounding box in R^3 that corresponds to the volumetric grid, tau is the iso value (defaults to 0). If make_triangles is True (default), we turn the quads into triangles. Finally, high_is_inside=True (default) means that values greater than tau are interior and smaller values are exterior. If dual_connectivity is False (default) the function produces marching cubes connectivity and if True it produces dual contouring connectivity. MC connectivity tends to produce less nice triangle shapes but since the vertices always lie on edges, the geometry is arguably better defined for MC.
Source code in pygel3d/hmesh.py
triangulate ¶
Turn a general polygonal mesh, m, into a triangle mesh by repeatedly splitting a polygon into smaller polygons.
Source code in pygel3d/hmesh.py
extrude_faces ¶
Inserts a new face loop around a set of faces given by fset.
Source code in pygel3d/hmesh.py
kill_face_loop ¶
Removes the face loop surrounding the patch of smallest area. This function has undefined effecto on a mesh that is not a pure quad mesh.
kill_degenerate_face_loops ¶
Removes face loops which contain very poorly shaped faces. Must be called on a pure quad mesh.
graph_to_feq ¶
graph_to_feq(g: Graph, node_radii: ArrayLike | float | None = None, symmetrize: bool = True) -> Manifold
Turn a skeleton graph g into a Face Extrusion Quad Mesh m with given node_radii for each graph node. If symmetrize is True (default) the graph is made symmetrical. If node_radii are supplied then they are used in the reconstruction. Otherwise, the radii are obtained from the skeleton. They are stored in the green channel of the vertex color during skeletonization, so for a skeletonized shape that is how the radius of each node is obtained. This is a questionable design decision and will probably change in the future.
Source code in pygel3d/hmesh.py
graph_to_cylinders ¶
Creates a Manifold mesh from the graph. The first argument, g, is the graph we want converted, and fudge is a constant that is used to increase the radius of every node. This is useful if the radii are 0.
Source code in pygel3d/hmesh.py
graph_to_isosurface ¶
Creates a Manifold mesh from the graph. The first argument, g, is the graph we want converted, and fudge is a constant that is used to increase the radius of every node. This is useful if the radii are 0.
Source code in pygel3d/hmesh.py
fit_mesh_to_ref ¶
fit_mesh_to_ref(m: Manifold, ref_mesh: Manifold, dist_wt: float = 0.5, lap_wt: float = 1.0, iter: int = 10)
Fits a skeletal mesh m to a reference mesh ref_mesh.
Source code in pygel3d/hmesh.py
rsr_recon ¶
rsr_recon(vertices: ArrayLike, normals: ArrayLike = None, use_Euclid_dist: bool = False, genus: int = -1, num_neighbors: int = 70, max_neighbor_dist: float = 20, max_normal_ang: float = 60, max_handle_dist: int = 50) -> Manifold
RsR Reconstruction. The first argument, vertices, is the point cloud. The next argument, normals, are the normals associated with the vertices or empty list (default) if normals need to be estimated during reconstruction. use_Euclid_dist should be true if we can use the Euclidean rather than projected distance. Set to true only for noise free point clouds. genus controls handle insertion: -1 lets the algorithm detect genus, 0 disables handle insertion, and values > 0 request that many handles. num_neighbors is the number of nearest neighbors for each point, max_neighbor_dist is the maximum distance to farthest neighbor measured in multiples of average distance, max_normal_ang is the threshold on angles between normals: two points are only connected if the angle between their normals is less than max_normal_ang. Finally, max_handle_dist is the threshold on the distance between vertices that are connected by handle edges (check paper). For large max_handle_dist, it is harder for the algorithm to add handles.
Source code in pygel3d/hmesh.py
hrsr_recon ¶
hrsr_recon(vertices: ArrayLike, normals: ArrayLike = None, collapse_iters: int = 1, use_Euclid_dist: bool = False, genus: int = -1, num_neighbors: int = 70, max_neighbor_dist: float = 20, max_normal_ang: float = 60, max_handle_dist: int = 50, skip_reexpansion: bool = False) -> Manifold
Hierarchical RsR reconstruction. The arguments match rsr_recon, with two additions: collapse_iters controls how many collapse iterations to run, and skip_reexpansion disables the final reexpansion stage when set to True.
Genus semantics are intentionally the same as rsr_recon: - genus = -1: auto-detect - genus = 0: do not add handles - genus > 0: request genus handle insertions
Source code in pygel3d/hmesh.py
connected_components ¶
Returns a list of Manifolds that form the connected components of the mesh m.
Source code in pygel3d/hmesh.py
count_boundary_curves ¶
analyze_topology ¶
Returns a list of dictionaries with information about the connected components of the mesh m. Each dictionary contains the Manifold ('m'), number of vertices ('V'), edges ('E'), faces ('F'), boundary curves ('b'), and the genus ('g') of the component. The genus is calculated using the Euler-Poincaré formula:
Source code in pygel3d/hmesh.py
sphere_delaunay ¶
Given a set of points on the unit sphere, compute the spherical Delaunay triangulation and return it as a Manifold mesh. The points should be given as an array-like of shape (N,3).
Source code in pygel3d/hmesh.py
The hmesh module provides the core halfedge mesh data structure and associated operations for polygonal mesh processing.
Manifold Class¶
The Manifold class represents a polygonal mesh using the halfedge data structure, which enables efficient traversal and manipulation of mesh topology.
Creating Meshes¶
You can create meshes in several ways:
import pygel3d.hmesh as hmesh
# Create an empty mesh
m = hmesh.Manifold()
# Load from file
m = hmesh.load("model.obj")
m = hmesh.obj_load("model.obj")
m = hmesh.off_load("model.off")
m = hmesh.ply_load("model.ply")
m = hmesh.x3d_load("model.x3d")
Mesh I/O Functions¶
Loading Meshes¶
load(filename, mesh)- Load mesh from file (auto-detect format)obj_load(filename, mesh)- Load Wavefront OBJ fileoff_load(filename, mesh)- Load Object File Formatply_load(filename, mesh)- Load PLY filex3d_load(filename, mesh)- Load X3D file
Saving Meshes¶
obj_save(filename, mesh)- Save as Wavefront OBJoff_save(filename, mesh)- Save as Object File Formatx3d_save(filename, mesh)- Save as X3D
Mesh Information¶
Basic Queries¶
valid(mesh)- Check if mesh is validclosed(mesh)- Check if mesh is closed (no boundary)bbox(mesh)- Get bounding box (returns min, max)bsphere(mesh)- Get bounding sphere (returns center, radius)
Counts¶
mesh.no_vertices()- Number of verticesmesh.no_faces()- Number of facesmesh.no_halfedges()- Number of halfedgescount_boundary_curves(mesh)- Count boundary loops
Mesh Processing¶
Cleaning and Repair¶
stitch_mesh(mesh, threshold)- Merge nearby verticesclose_holes(mesh, max_size)- Fill holes up to max_size edgesremove_caps(mesh, threshold)- Remove cap-like featuresremove_needles(mesh, threshold, avg_pos)- Remove needle-like featuresmerge_coincident_boundary_vertices(mesh, threshold)- Merge boundary vertices
Smoothing¶
cc_smooth(mesh)- Catmull-Clark smoothingloop_smooth(mesh)- Loop smoothingtaubin_smooth(mesh, iterations)- Taubin smoothinglaplacian_smooth(mesh, weight, iterations)- Laplacian smoothinganisotropic_smooth(mesh, sharpness, iterations)- Anisotropic smoothing
Subdivision¶
cc_split(mesh)- Catmull-Clark subdivisionloop_split(mesh)- Loop subdivision (for triangles)root3_subdivide(mesh)- Root-3 subdivisionbutterfly_subdivide(mesh)- Butterfly subdivision
Simplification¶
quadric_simplify(mesh, keep_fraction, singular_threshold, error_threshold)- Quadric error metric simplification
Refinement¶
refine_edges(mesh, threshold)- Refine long edges
Triangulation¶
shortest_edge_triangulate(mesh)- Triangulate using shortest diagonalear_clip_triangulate(mesh)- Triangulate using ear clipping
Optimization¶
minimize_curvature(mesh, anneal)- Minimize mesh curvatureminimize_dihedral_angle(mesh, max_iter, anneal, alpha, gamma)- Minimize dihedral anglesmaximize_min_angle(mesh, threshold, anneal)- Maximize minimum angleoptimize_valency(mesh, anneal)- Optimize vertex valencyrandomize_mesh(mesh, max_iter)- Random edge flips
Topology Operations¶
flip_orientation(mesh)- Reverse face orientationcc_split(mesh)- Catmull-Clark split
Mesh Measurements¶
Geometric Measurements¶
area(mesh, face_id)- Area of a faceperimeter(mesh, face_id)- Perimeter of a facelength(mesh, halfedge_id)- Length of an edgetotal_area(mesh)- Total surface areavolume(mesh)- Mesh volume
Vertex Measurements¶
valency(mesh, vertex_id)- Vertex valency (degree)one_ring_area(mesh, vertex_id)- Area of one-ring neighborhoodmixed_area(mesh, vertex_id)- Mixed Voronoi/barycentric area
Curvature¶
gaussian_curvature(mesh, vertex_id)- Gaussian curvature at vertexmean_curvature(mesh, vertex_id)- Mean curvature at vertexprincipal_curvatures(mesh, vertex_id)- Principal curvatures
Normals¶
vertex_normal(mesh, vertex_id)- Vertex normalface_normal(mesh, face_id)- Face normal
Mesh Traversal¶
Vertex Operations¶
mesh.vertices()- Iterator over all verticesmesh.circulate_vertex(vertex_id, mode)- Circulate around vertex
Face Operations¶
mesh.faces()- Iterator over all facesmesh.circulate_face(face_id, mode)- Circulate around faceno_edges(mesh, face_id)- Number of edges in facecentre(mesh, face_id)- Face center
Halfedge Operations¶
mesh.halfedges()- Iterator over all halfedges
Mesh Editing¶
Adding Elements¶
mesh.add_vertex(position)- Add a vertexmesh.add_face(positions)- Add a face
Removing Elements¶
mesh.remove_vertex(vertex_id)- Remove a vertexmesh.remove_face(face_id)- Remove a facemesh.remove_edge(halfedge_id)- Remove an edge
Modifying Topology¶
mesh.flip_edge(halfedge_id)- Flip an edgemesh.collapse_edge(halfedge_id, avg_vertices)- Collapse an edgemesh.split_edge(halfedge_id)- Split an edgemesh.split_face_by_edge(face_id, v0, v1)- Split face with new edgemesh.split_face_by_vertex(face_id)- Split face from centermesh.merge_faces(face_id, halfedge_id)- Merge two facesmesh.stitch_boundary_edges(h0, h1)- Stitch two boundary edgesmesh.close_hole(halfedge_id)- Close a boundary loop
Status Queries¶
mesh.vertex_in_use(vertex_id)- Check if vertex existsmesh.face_in_use(face_id)- Check if face existsmesh.halfedge_in_use(halfedge_id)- Check if halfedge exists
Boundary Queries¶
is_vertex_at_boundary(mesh, vertex_id)- Check if vertex is on boundaryis_halfedge_at_boundary(mesh, halfedge_id)- Check if halfedge is on boundaryboundary_edge(mesh, vertex_id, halfedge_id)- Check if edge is on boundary
Connectivity¶
connected(mesh, v0, v1)- Check if two vertices are connected
Walker Functions¶
Walker functions provide low-level halfedge traversal:
mesh.walker.next_halfedge(h)- Get next halfedge in facemesh.walker.prev_halfedge(h)- Get previous halfedge in facemesh.walker.opposite_halfedge(h)- Get opposite halfedgemesh.walker.incident_face(h)- Get incident facemesh.walker.incident_vertex(h)- Get incident vertex
Advanced Operations¶
Volumetric Operations¶
volumetric_isocontour(mesh, x_dim, y_dim, z_dim, data, pmin, pmax, tau, make_triangles, high_is_inside, dual_connectivity)- Extract isosurface from volume
Registration¶
non_rigid_registration(mesh, reference_mesh)- Non-rigid registrationstable_marriage_registration(mesh, reference_mesh)- Stable marriage registration
Reconstruction¶
rsr_recon(mesh, vertices, normals, v_num, n_num, isEuclidean, genus, k, r, theta, n)- Rotation system reconstruction
Face Operations¶
extrude_faces(mesh, face_list, output_face_list)- Extrude faceskill_face_loop(mesh)- Remove face loopkill_degenerate_face_loops(mesh, threshold)- Remove degenerate loops
Connected Components¶
connected_components(mesh)- Split into connected components
MeshDistance Class¶
The MeshDistance class provides efficient distance queries to triangle meshes.
from pygel3d import MeshDistance
import pygel3d.hmesh as hmesh
# Load mesh
m = hmesh.load("model.obj")
# Create distance object
dist = MeshDistance(m)
# Query signed distance
point = [0, 0, 0]
distance = dist.signed_distance(point)
# Query unsigned distance
distance = dist.distance(point)
Example Usage¶
Complete Mesh Processing Pipeline¶
import pygel3d.hmesh as hmesh
# Load mesh
m = hmesh.load("input.obj")
# Clean mesh
hmesh.stitch_mesh(m, 1e-6)
hmesh.close_holes(m, 100)
hmesh.remove_caps(m, 0.1)
# Smooth
hmesh.cc_smooth(m)
# Triangulate
hmesh.shortest_edge_triangulate(m)
# Simplify
hmesh.quadric_simplify(m, keep_fraction=0.5)
# Optimize
hmesh.minimize_curvature(m, anneal=True)
# Save
hmesh.obj_save("output.obj", m)