One host class + one kernel-facing struct replace two parallel reps (dict-based
MeshAdjacency and the VBD solver's ParticleForceElementAdjacencyInfo),
joined by a single host→device boundary.
| Array | Shape | Meaning |
|---|---|---|
edge_indices | [E,4] | [o0,o1,v0,v1]: edge endpoints + opposite verts (-1 = boundary) |
edge_tri_indices | [E,2] | [f0,f1]: the two triangles on each edge |
tri_edge_indices | [T,3] | each triangle's 3 edges (-1 if unregistered) |
v_adj_{edges,tris,springs,tets} (+_offsets) | CSR | per-vertex adjacency; built on demand |
__init__; vertex CSR lazy via init_vertex_adjacency() (idempotent — a flag prevents recompute).MeshAdjacency(tri_indices, edge_indices=None): None → compute edges; given → keep numbering, derive maps (vectorized _build_maps).to(device) is the only NumPy→Warp boundary; .edges dict is the lone deprecation.@wp.struct (kernel-facing)Built by to(); pure data. The v_adj_* CSR (read by VBD kernels via the
get_vertex_* accessors) + edge_tri_indices/tri_edge_indices
(wp.array2d, future-proofing).
edge_indices/tri_indices.model.soft_mesh_adjacency = MeshAdjacency(tri_indices, edge_indices).init_vertex_adjacency (memoized) → .to(device) → kernels; collision: host copy via _as_numpy; Style3D: builds its own.edge_indices accumulated, never rebuilt from tris (material alignment); only the order-independent maps are derived.edge (not hinge) naming; accessors co-located with the struct.