Isotropic remeshing (isotropic_remeshing)¶
Remeshes a triangle mesh towards a uniform target edge length with splits, collapses, swaps and tangential smoothing (Botsch and Kobbelt 2004). With a positive eps_rel, the surface stays inside an envelope around the input; features and feature_fields name curves and vertices the remeshing keeps.
Tutorial · Spec source · Defaults · JSON Schema
Cite: Botsch and Kobbelt 2004, A Remeshing Approach to Multiresolution Modeling
Required¶
application¶
string · required · one of isotropic_remeshing
Application name must be isotropic_remeshing.
input¶
string · required
Triangular input mesh.
Key options¶
output¶
string · default "out.obj"
Output file name. With feature_fields or features, a .vtu output holds what the remeshing keeps: the triangles, with a cell field per per-face field of feature_fields (its value; per-corner fields are not written) and the point field vertex_id (the id features gives a fixed vertex, -1 elsewhere); and, in <stem>_edge.vtu next to it (named as simwild's edges), the same points with one line cell per feature edge, its cell field edge_id (the id features gives it, -1 for none or for a boundary of feature_fields), and vertex_id again. The ids are Float64 there, as every .vtu field. Any other format holds the triangles only, and the remeshing warns that the rest is not written.
length_rel¶
float · default -1
Target edge length relative to the bounding box. Only used when absolute length is set negative.
length_abs¶
float · default -1
Absolute target edge length. If negative, relative length is used to compute the absolute. If this is negative as well, the average edge length of the input is used as target.
max_iterations¶
int · default 3
Number of iterations, each a pass of splits, collapses, swaps and smoothing. All of them run: there is no convergence test.
eps_rel¶
float · default -1
Envelope thickness relative to the bounding box diagonal. Negative (the default): no envelope.
boundary_eps_rel¶
float · default -1
Thickness of a tube around the open boundary of the input, relative to the bounding box diagonal. When positive, the boundary is not frozen (freeze_boundary then only freezes the vertices the caller lists, of which the application has none): splits, collapses, swaps and smoothing may move it, as long as every boundary edge that descends from the input's boundary stays within this distance of the input's boundary edges. A collapse between a boundary vertex and an interior one keeps the boundary vertex where it is, and a vertex splitting a boundary edge carries the lineage on. Holes and slits narrower than the tube can close when use_link_condition is false. Boundary that an operation opens away from the input's is not judged. Negative (the default): freeze_boundary decides.
freeze_boundary¶
bool · default true
Freeze the vertices of the input's open boundary. Ignored for the boundary when boundary_eps_rel is positive, which replaces the freezing with a tube.
features¶
string · default ""
File route: a JSON file, its path resolved like input, of curves and vertices to keep: {"edges": [[a, b], ...], "edge_ids": [id, ...], "vertices": [[v, id], ...]}, every key optional. Indices are vertices of the input file in file order, as written: the features follow them through the reader's merging of duplicates (remove_duplicate_eps), which renumbers them, so an edge whose two ends it merges goes, and entries it merges into one -- the copies of a curve that a mesh written face by face has on either side -- are kept once and have to agree on the id. Each edge has to be an edge of the input, and becomes a feature edge (see IsotropicRemeshing::set_feature_edges), with the boundaries of feature_fields; an entry [a, a] (a B-rep edge of no length, say) is skipped. edge_ids, as long as edges, gives each one an id that the remeshing carries: both halves of a split edge keep it, a collapse along a curve leaves the curve its id, and a boundary of feature_fields has -1. A vertex between two feature edges of different ids stays put, like a junction. The vertices are fixed: never moved and never removed -- a collapse of one of their edges removes the other end, where that may go -- and their ids are carried to the output; one may sit inside a curve, between two regions. With a .vtu output, the ids are written there (see output). On the memory route features names a mesh of the memory input instead, on the vertex rows of input (its V has as many rows), as the edges result is on mesh's: its edges are the curves, with an Int64 cell field edge_id or without; its points, with an Int64 cell field vertex_id, and the vertices whose Int64 vertex field vertex_id is not -1, are the fixed vertices. The result then holds what the .vtu output would, its ids Int64: mesh, with the vertex field vertex_id, and edges, its feature edges on the same vertex rows -- an edge block of none if there are none -- with the cell field edge_id and vertex_id again. So a result goes back in as it is: mesh as input (its vertex_id is accepted there and ignored) and edges as features. One made with feature_fields goes back in with feature_fields naming the per-face fields that mesh carries: the input's triangles may carry no field feature_fields does not name.
feature_fields¶
list of string · default []
Triangle fields of a .msh input, as io::read_mesh reads it, whose boundaries the remeshing keeps as curves (see IsotropicRemeshing::set_feature_edges): per-face fields (labels), where two faces differ, and per-corner fields (uv and normal, known by their names: two and three values per corner, corner after corner, so 6 and 9 columns), where the two faces at an edge disagree at either end -- a seam. A field carried through the remeshing then holds on every output face: a label exactly, texture coordinates within one chart. The values are those of the faces of the input as given: a degenerate face, which the reader drops (remove_duplicate_eps can make one), takes its values with it. The remeshing carries them as Float64, which holds every integer up to 2^53: an Int64 value beyond 2^53 in magnitude is refused, as a uv or normal of another width. On the memory route they are triangle attributes of the input mesh, whose triangles may carry no other attributes but uv and normal; the result then holds what the .vtu output would (see features): mesh, with each per-face field of feature_fields as a triangle attribute, in the type it was given in (Float64, Int64 or UInt8) and with its column labels, and the vertex field vertex_id; and edges, its feature edges on the same vertex rows, with edge_id (-1 for a boundary of feature_fields). The remeshing itself carries the per-face fields alone: the result holds no uv or normal, only their seams, among the edges. So mesh goes back in with feature_fields naming its per-face fields again, and edges as features.
Each entry: The name of a per-face or per-corner field of the input.
swap_quality¶
bool · default false
Swap an edge where that raises the worse shape quality (4 sqrt(3) area / sum of squared edge lengths) of its two triangles, as the remeshing app of the topology-first B-rep pipeline does, instead of where it brings the valences of its four vertices closer to 6 (4 on the boundary). Valence leaves flat triangles and needles alone when the swap would not change the valences much -- next to feature curves, say, where the curves themselves cannot be swapped. check_inversions still refuses a swap that folds the surface.
use_link_condition¶
bool · default true
Require the link condition before collapsing an edge. When true a collapse cannot change the topology, which also means it cannot create non-manifold edges or vertices. When false those collapses are allowed -- TriMesh represents the result, and any duplicate triangles the collapse produces are merged. Swaps are refused on non-manifold edges either way.
use_sample_envelope¶
bool · default false
Use sample envelope instead of exact one.
Other options¶
history_output¶
string · default ""
Record every committed operation and save the history here (see wmtk/history): the input surface and its edits, from which the map between input and output is rebuilt (the tracking application). On the memory route no file is written: any non-empty value records the history, and the bytes of the file it would be are returned in the report as history (bytes in Python; history::write_history or wm.write_history saves them). Empty: no recording.
check_inversions¶
bool · default true
Refuse a collapse, swap or smoothing that folds the surface over: one that leaves more triangles facing against, or flat to, the surface it replaces (the triangles it changes or removes, their area vectors summed) or the surface it leaves than there were, or more such triangles in the stars of the vertices it touches, each star against its own normal. Exact (orient3d). A fold the input has can be undone, but none is made that the triangles around it would see. Splits are not checked: they move no vertex. With feature curves (features or feature_fields), each region the curves bound is judged on its own, against the normal of its own triangles, in the operation and in each star: across a curve the surface may turn by any angle, and where it turns by more than 90 degrees -- at a sharp edge of a B-rep -- triangles of the smaller side, oriented as they should be, face away from a normal summed over both sides (at 90 degrees or less they face along it). A curve that does not close has the same region on both sides, which are judged together. Where the surface turns by more than 90 degrees, a side can also turn onto the other, and through it, by less than 90, still facing along its own normal; so at a feature edge between two regions whose sides (each one's triangles round the edge's ends, their area vectors summed) face more than 90 degrees apart, and whose two triangles open the wedge between them as the sides do, an operation may not leave the two opening it the other way, or flat (orient3d again). Two triangles that do not -- a needle at the curve, or a wedge the input has closed through itself -- may change. Where the input's triangles are not consistently oriented, every orientation flip inside a region counts as a fold, so most operations there are refused; one along a curve between two regions does not.
limit_collapse_length¶
bool · default false
Refuse a collapse that leaves an edge longer than 4/3 of the target length, the length above which an edge is split, as Botsch and Kobbelt's isotropic remeshing does. Without it a collapse is judged by the edge it removes alone, and those of the last iteration leave long edges that no split comes after to cut.
cleanup_angle¶
float · default 0
Repair, at the end of every iteration, the triangles whose smallest angle is under this many degrees; 0 repairs none. Worst first, each gets the first of these that is allowed: a collapse of its shortest edge, either way; a swap of one of its edges, longest first; a move of one of its vertices, or else of one of the triangles around it -- off the curves, within the tangent plane; inside a curve, along it -- to where the smallest angle around it is largest. Allowed means that every other rule holds (curves, fixed vertices, envelopes), that it folds nothing by the rule of check_inversions (on or off: the repairs keep it), that a collapse leaves no edge longer than 4/3 of the target length (with limit_collapse_length or without: the repairs are not to coarsen the mesh, and those after the last iteration have no split after them), and that the smallest angle of the triangles it leaves beats that around the triangle repaired. Where what it replaces folds already -- a fold the input brings, which confuses that rule -- one that leaves fewer triangles facing against the input triangles next to them, of the same feature_fields values, than there were is allowed too, even where no angle improves; only there, since next to a wall thinner than a triangle the input's two sides cancel and its orientation says nothing. Up to 10 rounds, serially. Meant for what the passes leave: thin triangles next to curves, where no vertex may leave its curve and no swap or collapse may put two curve edges in one triangle, and the folds of the input that the splits copy.
remove_duplicate_eps¶
float · default 1e-05
Merge input vertices closer than this fraction of the bounding box diagonal. 0 merges only exactly coincident vertices; a negative value merges nothing.
preallocation_factor¶
float · default 6.0 · min 1.0
Mesh storage (connectivity + attributes) is preallocated to this factor times the live element count at init and consolidation. Operations take fresh slots from that headroom and are retried once it is exhausted. Lower it for pure-decimation runs, raise it for aggressive refinement.
Run control¶
input_dir¶
string · default ""
Directory where the input files are located. This is injected by the application and should not be set by the user.
num_threads¶
int · default 0
Number of threads. 0 runs sequentially and gives the same result on every run; with more threads the order of the operations depends on the schedule, and the result can change slightly from run to run.
log_file¶
string · default ""
Logs are not just printed on the terminal but also saved in this file.
report¶
string · default ""
A JSON file that stores information about the result and the method execution, e.g., runtime.