|
Wildmeshing Toolkit
|
The smooth offset potential Phi, and the offset defined as its level set Phi = c. More...
#include <OffsetPotential.hpp>
Classes | |
| struct | Impl |
| Everything that mentions ipc-toolkit. More... | |
Public Types | |
| using | VecD = typename OffsetPotential< DIM >::VecD |
| using | MatD = typename OffsetPotential< DIM >::MatD |
Public Types inherited from wmtk::components::topological_offset::OffsetPotential< DIM > | |
| using | VecD = Eigen::Matrix< double, DIM, 1 > |
| using | MatD = Eigen::Matrix< double, DIM, DIM > |
Public Member Functions | |
| SmoothOffsetPotential (const MatrixXd &V, const MatrixXi &E, const MatrixXi &F, const std::vector< int > &P, double delta, double dhat_factor) | |
| Build the potential of a fixed complex. | |
| double | value (const VecD &p) const override |
| VecD | gradient (const VecD &p) const override |
| MatD | hessian (const VecD &p) const override |
| double | residual_length (const VecD &p) const override |
The distance from p to the level set Phi = c ALONG THE FIELD, in length units. | |
| double | relative_residual (const VecD &p) const override |
| The same root, signed and over delta: t / delta, > 0 outside the offset region. NaN where residual_length() is infinite. | |
| bool | within_support (const VecD &p) const override |
Whether p is inside the support at all, i.e. Phi(p) > 0. | |
| bool | is_inside_offset (const VecD &p) const override |
| Phi decreases with distance, so the offset region is where it is still above the level. | |
| std::string | describe_active (const VecD &p) const override |
Public Member Functions inherited from wmtk::components::topological_offset::OffsetPotential< DIM > | |
| virtual | ~OffsetPotential () |
| double | target_level () const |
| The level value the offset boundary is placed on. | |
| double | delta () const |
| The offset distance the field is calibrated to. | |
| double | dhat () const |
| double | level_set_slope () const |
| |d(field)/d(distance)| at the level set on a flat stretch of input. | |
| virtual bool | is_euclidean () const |
| The Euclidean field: value() is the distance, so a distance residual is the plain one. | |
Private Member Functions | |
| SmoothOffsetPotential (double delta, double dhat_factor, int) | |
| void | build (const MatrixXd &V, const MatrixXi &E, const MatrixXi &F, const std::vector< int > &P) |
| bool | level_set_distance (const VecD &p, double &t) const |
| void | value_gradient (const VecD &p, double &v, VecD &g) const |
| value() and gradient() at p from one collision build per part; see the definition. | |
Private Attributes | |
| std::unique_ptr< Impl > | m_impl |
Additional Inherited Members | |
Protected Member Functions inherited from wmtk::components::topological_offset::OffsetPotential< DIM > | |
| OffsetPotential (const double delta, const double dhat) | |
Protected Attributes inherited from wmtk::components::topological_offset::OffsetPotential< DIM > | |
| double | m_delta = 0. |
| double | m_dhat = 0. |
| double | m_c = 0. |
| double | m_grad_ref = 1. |
The smooth offset potential Phi, and the offset defined as its level set Phi = c.
Phi is C^2 with an analytic gradient and Hessian, so placing a vertex on the offset is an ordinary term in the smoothing objective and the front is smoothed by the same code path as every other vertex.
What Phi is: the offset geometric contact potential of ipc-toolkit's high_order_contact subtree, evaluated at a point q against the input complex,
Phi(q) = sum over active primitives P of b( dist(q, P), dhat ) b(d, dhat) = -(d/dhat - 1)^2 * log(d/dhat) for d < dhat, 0 otherwise
(ipc::NormalizedClampedLogBarrier). "Active" is the OGC feasible-region rule: a triangle is active at q when q projects into its interior, an edge when q projects into its interior and lies outside the wedges its incident triangles claim, a vertex when q lies in its Voronoi region. Away from features exactly one primitive contributes and Phi is a monotone function of the Euclidean distance alone; at a reentrant feature several contribute, their barriers add, and the level set bulges outward. So Phi = c is a smoothed offset, not the Euclidean one, and that difference is deliberate; the Euclidean distance is still reported as a diagnostic.
Calibration: c is not a free parameter. It is Phi at perpendicular distance delta from one large flat primitive – one active pair, no feature interaction – computed at construction through this same class, so it cannot drift from a hand-kept analytic formula. Both dimensions therefore calibrate to the same c for the same delta and dhat_factor, which tests/test_offset_potential.cpp asserts.
dhat, the support radius beyond which Phi and every derivative are identically zero, is dhat_factor * delta. delta must sit strictly inside the support (at exactly dhat the potential and its gradient are both 0, so a vertex there gets no direction to move in) and the support must not be so wide that distant parts of the complex reach the level set. A vertex beyond dhat is a hard error – see TopoOffsetTriMesh::check_offset_within_support() and its 3D twin.
Threading: an evaluation writes the query point into a scratch vertex matrix and builds a collision set around it, so it holds per-thread state; value, gradient and hessian are const and safe to call concurrently from the smoothing pass.
| wmtk::components::topological_offset::SmoothOffsetPotential< DIM >::SmoothOffsetPotential | ( | const MatrixXd & | V, |
| const MatrixXi & | E, | ||
| const MatrixXi & | F, | ||
| const std::vector< int > & | P, | ||
| double | delta, | ||
| double | dhat_factor | ||
| ) |
Build the potential of a fixed complex.
The complex is given as ipc gives a collision mesh – vertices, edges, triangles – plus the indices of its isolated points. Phi has no area primitive in 2D and no volume primitive in 3D, so a solid input region must enter as its boundary; outside the region, the only place an offset exists, the two descriptions agree exactly.
| V | #V x DIM complex vertices. |
| E | #E x 2 segments. In 3D this must contain every edge of every triangle in F as well as the complex's own isolated edges: ipc derives faces_to_edges from it and throws if an edge of a face is missing, and the OGC feasible-region test for a vertex reads its edge neighbours. |
| F | #F x 3 triangles. Must be empty when DIM == 2. |
| P | indices into V of the isolated complex vertices (in no segment/triangle). |
| delta | the offset distance the level set is calibrated to. |
| dhat_factor | support radius as a multiple of delta. Must be > 1. |
|
private |
Calibration constructor: builds the single-flat-primitive reference complex without recursing into calibration itself.
|
overridevirtual |
Diagnostic: the active pairs at p, one per line, with each one's contribution – the only way to see why Phi has the value it has.
Implements wmtk::components::topological_offset::OffsetPotential< DIM >.
|
overridevirtual |
|
overridevirtual |
|
inlineoverridevirtual |
Phi decreases with distance, so the offset region is where it is still above the level.
Implements wmtk::components::topological_offset::OffsetPotential< DIM >.
|
private |
The signed distance t from p to the level set along the field (> 0 outside the offset region), or false where there is none to measure. residual_length() is |t|, relative_residual() t / delta.
|
overridevirtual |
The same root, signed and over delta: t / delta, > 0 outside the offset region. NaN where residual_length() is infinite.
NOT the relative field error (Phi - c)/c, which the 3D loop measured before 2026-09-27 and which is a length only for a field linear in the distance. For this one it is the length error times the logarithmic slope delta |dPhi/dd| / c at the level set, which for b(d) = -(d/dhat - 1)^2 ln(d/dhat) is 2/(k - 1) + 1/ln k with k = dhat/delta: 3.4427 at the default k = 2, 6.47 at k = 1.5, 1.91 at k = 3, unbounded as k -> 1. Measured on the cube (target_distance_rel 1e-2, front_conv_rel 1e-4, 10 threads): (Phi - c)/c over the true relative distance error was 3.443 on every flat side, rounded edge and corner at convergence (the regional 3.4 / 3.3 / 3.1 of turns 1 to 4 are the same factor at vertices several bars off the level set, where b is visibly curved), and since the sag goes as h^2 that cost one extra halving: 9 turns and 80054 front faces against the Euclidean field's 7 and 25006 on the same offset surface.
Implements wmtk::components::topological_offset::OffsetPotential< DIM >.
|
overridevirtual |
The distance from p to the level set Phi = c ALONG THE FIELD, in length units.
|t| for the root t of Phi(p + t n) = c nearest p along n = grad Phi(p) / |grad Phi(p)|, on p's own side of the complex. No calibration constant and no reference geometry enter: it is a distance in the field's own geometry, and where Phi is a function of the Euclidean distance alone (one active pair) it is exactly |d - delta|. +infinity where it cannot be measured – outside the support (Phi = 0, no direction), on the complex (Phi infinite), where grad Phi vanishes, or where no root lies along n within dhat. The runaway guard turns the first of those into a hard error before this number decides anything. See the definition for the search and its tolerance.
Implements wmtk::components::topological_offset::OffsetPotential< DIM >.
|
overridevirtual |
|
inlineoverridevirtual |
Whether p is inside the support at all, i.e. Phi(p) > 0.
Implements wmtk::components::topological_offset::OffsetPotential< DIM >.