Wildmeshing Toolkit
Loading...
Searching...
No Matches
Functions
visualize_error_field Namespace Reference

Functions

 load_field_module ()
 
 eval_selection (expr, names)
 
 read_mesh (path)
 
 selection_mask (tris, groups, expr)
 
 input_segments (pts, tris, sel, expr)
 
 in_selected_region (q, pts, tris, sel)
 
 dist_to_segments (q, a, b, chunk=20000)
 
 complex_arrays (seg_a, seg_b)
 
 grid (pts, delta, res, pad_rel, pad_abs=None, zoom=1.0)
 
 main ()
 

Detailed Description

Pixel image of the offset's quadratic error E = (Phi - c)^2 over a 2D input mesh.

    ./visualize_error_field.py two_circles.msh                     # defaults
    ./visualize_error_field.py two_circles.msh config.json         # delta + selection from it
    ./visualize_error_field.py two_circles.msh --cap 0.05 --res 800 --save out.png

Needs matplotlib, meshio and numpy -- the same environment visualize_offset.py wants:

    /Volumes/Seagate_Drive/offsets_optimization_testing/.venv/bin/python

WHAT IT DOES. Evaluates the potential on a regular grid and draws one pixel per sample,
coloured by value. Nothing else -- no contours, no overlays unless asked for.

  --cap V     values above V are clamped to V before colouring, so the colour range is
              [min, V]. Phi is a BARRIER: it diverges ON the input complex, so a handful of
              pixels next to it are orders of magnitude above everything else and take the
              whole range uncapped. Default is c^2, which is what E equals where Phi = 0.
  --plot      `error` (default) draws E = (Phi - c)^2; `phi` draws Phi itself.
  --res       pixels on the long axis.

WHICH Phi. Both fields, selected with --field (default: the config's offset_field, else
`smooth`, which is the component's own default).

  euclidean   exact distance to the input complex. Level c = target_distance.
  smooth      the ipc-toolkit high_order_contact (OGC) potential -- the SAME object the run
              uses, reached through the wmtk_offset_field extension module rather than
              reimplemented, because a Python approximation of it would be a different field
              wearing the same name. Build it with:
                  cmake --build build --target wmtk_offset_field
              and this script finds it in <repo>/build/bin automatically. It is IDENTICALLY
              ZERO past dhat = offset_dhat_factor x delta; the exact distance to the complex
              (cheap in numpy) is used to skip calling it on samples out there, which is not an
              approximation and is most of the grid.

WHICH SEGMENTS. The input complex is derived from the mesh the way the C++ derives it: an edge
whose two incident triangles have different group membership under the selection. With a config,
`offset_selection` is used; without one, the default is every non-ambient group against the rest.

Function Documentation

◆ complex_arrays()

visualize_error_field.complex_arrays (   seg_a,
  seg_b 
)
Segment endpoint pairs -> (V, E, P) as the potentials want them: welded vertices, an
index pair per segment, and no isolated points (every vertex here is on a segment).

◆ dist_to_segments()

visualize_error_field.dist_to_segments (   q,
  a,
  b,
  chunk = 20000 
)
(nq,) exact distance from each query point to the nearest segment.

Chunked over the query points: the intermediate is (chunk, ns, 2), and at 600^2 samples
against a few hundred segments the unchunked version is a multi-GB allocation.

◆ eval_selection()

visualize_error_field.eval_selection (   expr,
  names 
)
The offset_selection boolean expression, on one cell's group memberships.

Same language the C++ parses: tag names, & | !, parentheses; 'ambient' (spelled '_' in the
spec) means "in no tag group". Names are substituted with their membership truth value, so
eval only ever sees booleans and the three operators.

◆ grid()

visualize_error_field.grid (   pts,
  delta,
  res,
  pad_rel,
  pad_abs = None,
  zoom = 1.0 
)
A regular grid over the mesh bbox -> (xs, ys, query points).

pad_abs overrides the relative padding: the smooth field is identically zero past dhat, so
for it there is nothing to see further out than that.

zoom > 1 keeps the same pixel COUNT over a window 1/zoom as wide, centred on the mesh
bounding box -- so it is a real increase in sampling density, not a crop of an image already
computed. The padding is symmetric, so the padded box has the same centre as the mesh.

◆ in_selected_region()

visualize_error_field.in_selected_region (   q,
  pts,
  tris,
  sel 
)
(nq,) bool: which query points land inside a SELECTED triangle.

Exact point location through matplotlib's trapezoid-map trifinder -- O(log m) per query and
vectorised -- rather than a point-in-triangle sweep, which at 10^6 pixels against 10^4
triangles would be 10^10 tests. Points outside the mesh entirely come back as -1.

◆ input_segments()

visualize_error_field.input_segments (   pts,
  tris,
  sel,
  expr 
)
The input complex: edges whose two incident triangles differ under the selection.

Returns (a, b) as two (ns, 2) arrays of segment endpoints. A boundary edge of the domain
counts only if its one triangle is selected -- the outside is 'not selected' by definition.

◆ load_field_module()

visualize_error_field.load_field_module ( )
The wmtk_offset_field extension, from the repo's build/bin. -> module or None.

Located relative to this file rather than requiring PYTHONPATH, since the build tree is
always at a fixed depth above scripts/.

◆ read_mesh()

visualize_error_field.read_mesh (   path)
-> (points (n,2), triangles (m,3), {group name: bool mask over triangles}).

◆ selection_mask()

visualize_error_field.selection_mask (   tris,
  groups,
  expr 
)
(m,) bool: which triangles the offset_selection picks out. This is the region being
offset -- the offset grows OUT of it, so it is also the region there is nothing to plot in.