Expand description
Adaptive splitting: triangles a deformation stretched split on their own, and nothing else changes.
capture_rest_sizes records each vertex’s rest size, the longest edge
on it, in the REST_SIZE_CHANNEL channel, before the deformation starts.
split_stretched then splits an edge only when it has grown past
StretchOptions::limit times the smaller rest size of its two endpoints.
An edge’s rest length is at most either endpoint’s rest size, so an edge that did not stretch can never reach the limit, however long it is next to the rest of the mesh. A mesh with fine and coarse areas keeps both: each area is measured against itself, not against a mesh-wide mean.
§What changes
- Existing vertices keep their ids and positions, so anything that stores vertex ids stays valid.
- The triangles on a split edge are bisected. A triangle no split edge touches keeps its slot and its corners: the only unstretched triangles that change are neighbours of a stretched edge, to stay conforming.
- Nothing collapses and nothing flips.
The longest edge splits first, which keeps the bisected triangles from degrading into slivers.
§Where new vertices go
Existing vertices cannot move, which rules out Loop’s edge rule: its
3/8 (a + b) + 1/8 (c + d) relies on the vertex rule moving a and b
too. Alone it pulls a new vertex below the midpoint on a convex surface and
measures no better than a midpoint against the true normal. A new vertex
takes the middle of the cubic Hermite curve between the edge’s endpoints
and their normals instead, which needs only the edge
and is exact to second order on a smooth surface:
(a + b) / 2 - (((b - a) . na) na + ((a - b) . nb) nb) / 8. An endpoint’s
normal averages only the triangles around it that face like the edge’s own
(within 45 degrees), so a flat face stays flat next to a hard edge. A
gentler crease reads as a curve and rounds off: a flat face beside one bulges
by up to about 5% of its edge.
An exact midpoint is used on a boundary or non-manifold edge, on a sharp edge (owners more than 45 degrees apart), and wherever the curved point would fold a child triangle. The boundary rule is what keeps a UV seam closed: its two sides are separate vertices at one position, and both must compute the same new point.
§Seams
Two vertices at exactly one position take the larger of their rest sizes,
so both sides of a seam use one limit and split together. Boundary edges
also split before any interior edge and are exempt from
StretchOptions::max_splits, so a seam cannot open at the cap.
§Cost
One pass tests the vertices against the region and then touches only the
edges around vertices inside it, using the mesh’s vertex index
(Mesh::ensure_incidence). It never copies or sorts the whole mesh.
Without a cached index (inside a transaction that has already changed
topology, for example) it builds a private one first, which costs a pass
over every triangle.
Structs§
- Stretch
Options - Options for
split_stretched. - Stretch
Report - Outcome of
split_stretched.
Constants§
- DEFAULT_
MAX_ SPLITS - Default for
StretchOptions::max_splits. - DEFAULT_
STRETCH_ LIMIT - Default for
StretchOptions::limit. - REST_
SIZE_ CHANNEL - The source channel
capture_rest_sizeswrites: one value per vertex slot, interpolated for new vertices. Zero means no rest size is known.
Functions§
- capture_
rest_ sizes - Records every vertex’s rest size for
split_stretched: the longest edge on it, or on any vertex at exactly its position. Replaces an earlier capture, and builds the mesh’s vertex index if needed. - ensure_
rest_ sizes capture_rest_sizesunless the mesh holds valid rest sizes. Returns whether it captured.- split_
stretched - Splits the edges under
options.regionthat have stretched pastStretchOptions::limit, until none is left orStretchOptions::max_splitsruns out. See the module docs for what changes and where new vertices go. New vertices interpolate source channels, so UVs follow. The plan is computed first and committed throughtxat the end: either every split lands or nothing does.