diff --git a/src/axom/klee/docs/sphinx/specifying_shapes.rst b/src/axom/klee/docs/sphinx/specifying_shapes.rst
index c5cfd704d6..ae7e281c12 100644
--- a/src/axom/klee/docs/sphinx/specifying_shapes.rst
+++ b/src/axom/klee/docs/sphinx/specifying_shapes.rst
@@ -114,6 +114,8 @@ will match that of the (global or per-shape) `dimensions`.
x: 10
+.. _klee-overlay-rules:
+
Overlay Rules
-------------
Shapes are added to meshes in the order in which they appear in the YAML
diff --git a/src/axom/quest/docs/scripts/generate_shaping_pipeline_figure.py b/src/axom/quest/docs/scripts/generate_shaping_pipeline_figure.py
new file mode 100644
index 0000000000..fe46c62979
--- /dev/null
+++ b/src/axom/quest/docs/scripts/generate_shaping_pipeline_figure.py
@@ -0,0 +1,323 @@
+#!/usr/bin/env python3
+"""Generate an SVG figure for Quest's shaping pipeline documentation."""
+
+from __future__ import annotations
+
+import argparse
+import html
+from pathlib import Path
+
+
+WIDTH = 1600
+HEIGHT = 900
+
+
+def esc(text: str) -> str:
+ return html.escape(text, quote=True)
+
+
+def rect(x: int, y: int, w: int, h: int, cls: str, rx: int = 28) -> str:
+ return (
+ f""
+ )
+
+
+def line(x1: int, y1: int, x2: int, y2: int, cls: str, marker: bool = True) -> str:
+ marker_end = " marker-end='url(#arrow)'" if marker else ""
+ return (
+ f""
+ )
+
+
+def text_block(x: int, y: int, lines: list[str], cls: str, line_gap: int = 34) -> str:
+ parts = [f""]
+ for idx, item in enumerate(lines):
+ dy = 0 if idx == 0 else line_gap
+ parts.append(f"{esc(item)}")
+ parts.append("")
+ return "".join(parts)
+
+
+def bullet_list(x: int, y: int, items: list[str], cls: str, line_gap: int = 30) -> str:
+ parts: list[str] = []
+ for idx, item in enumerate(items):
+ yy = y + idx * line_gap
+ parts.append(f"")
+ parts.append(f"{esc(item)}")
+ return "".join(parts)
+
+
+def stage_box(x: int, y: int, w: int, h: int, label: str, items: list[str], kind: str) -> str:
+ return "".join(
+ [
+ rect(x, y, w, h, f"panel {kind}"),
+ text_block(x + 28, y + 52, [label], "stage-title"),
+ bullet_list(x + 32, y + 110, items, "body-text"),
+ ]
+ )
+
+
+def step_box(
+ x: int, y: int, w: int, h: int, title: list[str], subtitle: list[str]
+) -> str:
+ return "".join(
+ [
+ rect(x, y, w, h, "step"),
+ text_block(x + 20, y + 36, title, "step-title", line_gap=22),
+ text_block(x + 20, y + 74, subtitle, "step-text", line_gap=22),
+ ]
+ )
+
+
+def dashed_loop(x: int, y: int, w: int, h: int) -> str:
+ return (
+ f""
+ )
+
+
+def build_svg() -> str:
+ out: list[str] = []
+ out.append(
+ f"")
+ return "".join(out)
+
+
+def parse_args() -> argparse.Namespace:
+ default_output = (
+ Path(__file__).resolve().parent.parent / "sphinx" / "figs" / "shaping_pipeline.svg"
+ )
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ "-o",
+ "--output",
+ type=Path,
+ default=default_output,
+ help=f"Path to the output SVG file (default: {default_output})",
+ )
+ return parser.parse_args()
+
+
+def main() -> None:
+ args = parse_args()
+ args.output.parent.mkdir(parents=True, exist_ok=True)
+ args.output.write_text(build_svg(), encoding="utf-8")
+ print(f"Wrote {args.output}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/axom/quest/docs/sphinx/delaunay.rst b/src/axom/quest/docs/sphinx/delaunay.rst
new file mode 100644
index 0000000000..ace44beda7
--- /dev/null
+++ b/src/axom/quest/docs/sphinx/delaunay.rst
@@ -0,0 +1,80 @@
+.. ## Copyright (c) Lawrence Livermore National Security, LLC and other
+.. ## Axom Project Contributors. See top-level LICENSE and COPYRIGHT
+.. ## files for dates and other details.
+.. ##
+.. ## SPDX-License-Identifier: (BSD-3-Clause)
+
+***********************
+Delaunay Triangulation
+***********************
+
+Quest provides a ``quest::Delaunay`` class for incremental construction of a
+2D or 3D Delaunay complex from a point set. The class currently builds the
+triangulation by inserting points one at a time into an initial bounding mesh,
+retriangulating the local cavity after each insertion.
+
+At a high level, the workflow is:
+
+#. define a bounding box that contains the points to be inserted
+#. initialize the Delaunay object with that boundary
+#. insert points incrementally
+#. remove the artificial boundary elements
+#. optionally validate the resulting complex and write it to VTK
+
+The current implementation is useful for applications that need a simplicial
+mesh over an unstructured point set. Quest also uses this capability in higher
+level algorithms such as scattered interpolation.
+
+The example application
+``/src/axom/quest/examples/delaunay_triangulation.cpp`` demonstrates the
+basic usage pattern.
+
+The Delaunay class is templated on the dimension:
+
+.. literalinclude:: ../../examples/delaunay_triangulation.cpp
+ :start-after: _quest_delaunay_include_start
+ :end-before: _quest_delaunay_include_end
+ :language: C++
+
+Creating the triangulation
+--------------------------
+
+The example creates a bounding box, initializes the Delaunay object, inserts
+points, and then removes the artificial boundary:
+
+.. literalinclude:: ../../examples/delaunay_triangulation.cpp
+ :start-after: _quest_delaunay_basic_start
+ :end-before: _quest_delaunay_basic_end
+ :language: C++
+
+The call to ``initializeBoundary()`` is required before inserting points. The
+inserted points must lie inside that bounding box.
+
+Validation
+----------
+
+Quest provides validation helpers for both the underlying mesh structure and
+the Delaunay property itself:
+
+.. literalinclude:: ../../examples/delaunay_triangulation.cpp
+ :start-after: _quest_delaunay_validate_start
+ :end-before: _quest_delaunay_validate_end
+ :language: C++
+
+Output
+------
+
+The resulting triangulation can be written to a VTK file for inspection:
+
+.. literalinclude:: ../../examples/delaunay_triangulation.cpp
+ :start-after: _quest_delaunay_output_start
+ :end-before: _quest_delaunay_output_end
+ :language: C++
+
+Current scope
+-------------
+
+This page is intentionally conservative. It documents the current user-visible
+workflow of Quest's Delaunay triangulation example without trying to freeze the
+interface or fully characterize future use cases while the implementation is
+still evolving.
diff --git a/src/axom/quest/docs/sphinx/figs/intersection.png b/src/axom/quest/docs/sphinx/figs/intersection.png
new file mode 100644
index 0000000000..0f14e09a98
Binary files /dev/null and b/src/axom/quest/docs/sphinx/figs/intersection.png differ
diff --git a/src/axom/quest/docs/sphinx/figs/sampling.png b/src/axom/quest/docs/sphinx/figs/sampling.png
new file mode 100644
index 0000000000..22d6f1c0c9
Binary files /dev/null and b/src/axom/quest/docs/sphinx/figs/sampling.png differ
diff --git a/src/axom/quest/docs/sphinx/figs/shaping_overview.png b/src/axom/quest/docs/sphinx/figs/shaping_overview.png
new file mode 100644
index 0000000000..bb000d12a4
Binary files /dev/null and b/src/axom/quest/docs/sphinx/figs/shaping_overview.png differ
diff --git a/src/axom/quest/docs/sphinx/figs/shaping_pipeline.svg b/src/axom/quest/docs/sphinx/figs/shaping_pipeline.svg
new file mode 100644
index 0000000000..04c0b0f168
--- /dev/null
+++ b/src/axom/quest/docs/sphinx/figs/shaping_pipeline.svg
@@ -0,0 +1,40 @@
+
\ No newline at end of file
diff --git a/src/axom/quest/docs/sphinx/index.rst b/src/axom/quest/docs/sphinx/index.rst
index 01e2606598..67cbe0eda7 100644
--- a/src/axom/quest/docs/sphinx/index.rst
+++ b/src/axom/quest/docs/sphinx/index.rst
@@ -7,37 +7,31 @@
Quest User Guide
================
-The Quest component of Axom provides several spatial operations and queries
-on a ``mint::Mesh``.
-
- - Operations
-
- - :ref:`Read a surface mesh` from an STL file
- - :ref:`Check for some common mesh errors; deduplicate vertices`
-
- - vertex welding: merge vertices closer than a specified distance
- "epsilon"
- - find self-intersections and degenerate triangles in a surface mesh
- - watertightness test: is a surface mesh a watertight manifold?
-
- - Point queries
-
- - Surface mesh point queries :ref:`in C` or
- :ref:`in C++`
-
- - in/out query: is a point inside or outside a surface mesh?
- - signed distance query: find the minimum distance from a query point
- to a surface mesh
-
- - :ref:`Point in cell query`: for a query point, find the
- cell of the mesh that holds the point and the point's isoparametric
- coordinates within that cell
- - :ref:`All nearest neighbors`: given a list of point
- locations and regions, find all neighbors of each point in a different
- region
- - :ref:`Isosurface detection`: generate an
- isosurface mesh from a nodal scalar field and an isovalue.
-
+Axom's Quest component provides spatial queries, geometry readers, contouring,
+and shaping algorithms for simulation workflows. Quest works with several Axom
+mesh representations, including ``mint::Mesh`` objects, Conduit Blueprint
+meshes, and MFEM meshes.
+
+This guide focuses on the most common Quest workflows:
+
+* :ref:`Read geometry and meshes ` from STL, Pro/E, STEP,
+ C2C, and MFEM-based inputs.
+* :ref:`Check and repair surface meshes ` before using
+ algorithms that require watertight geometry.
+* Run surface and mesh queries, including :ref:`surface containment and signed
+ distance `, :ref:`point-in-cell `, and
+ :ref:`all nearest neighbors `.
+* Generate :ref:`isocontours and isosurfaces ` from
+ nodal scalar fields on Blueprint meshes.
+* Build point-set simplicial meshes with :doc:`Quest's Delaunay triangulation `.
+* Query curved and linearized shapes with :doc:`Winding Numbers `.
+* Approximate curved contour geometry with :doc:`Linearize Curves `.
+* Build :ref:`shaping pipelines ` that convert Klee shape
+ descriptions into material volume fractions on target meshes.
+
+The Sphinx pages describe the user-facing workflows and show representative
+examples from Quest's sources and tests. For a full API reference, use the
+generated Doxygen documentation below.
API Documentation
-----------------
@@ -47,7 +41,7 @@ Doxygen generated API documentation can be found here: `API documentation <../..
.. toctree::
:caption: Contents
- :maxdepth: 2
+ :maxdepth: 1
read_mesh
check_and_repair
@@ -56,4 +50,7 @@ Doxygen generated API documentation can be found here: `API documentation <../..
point_in_cell
all_nearest_neighbors
isosurface_detection
-
+ delaunay
+ winding_number
+ linearize_curves
+ shaping
diff --git a/src/axom/quest/docs/sphinx/isosurface_detection.rst b/src/axom/quest/docs/sphinx/isosurface_detection.rst
index 00a6218fde..5608bd8977 100644
--- a/src/axom/quest/docs/sphinx/isosurface_detection.rst
+++ b/src/axom/quest/docs/sphinx/isosurface_detection.rst
@@ -10,15 +10,15 @@
Isosurface Detection
********************
-Quest can generate isosurface meshes for node-centered scalar fields.
-This feature takes a structured mesh with some scalar nodal field and
-generates an ``UnstructuredMesh`` at a user-specified isovalue. The
-isosurface mesh contains information on which elements of the field
-mesh it crosses. The output may be useful for material surface
-reconstruction and visualization, among other things.
+Quest provides the ``quest::MarchingCubes`` class for generating contours from
+node-centered scalar fields on Conduit Blueprint meshes. In 2D, the algorithm
+produces line segments; in 3D, it produces triangles. The output contour mesh
+also records which input cell and domain generated each contour facet, which is
+useful for analysis, debugging, and downstream reconstruction workflows.
-We support 2D and 3D configurations. The isosurface mesh is a
-composed of line segments in 2D and triangles in 3D.
+``MarchingCubes`` operates on Blueprint meshes in *multi-domain* form. The
+parent topology must be structured and the scalar field must be node-centered.
+The class supports both 2D and 3D inputs.
.. Note::
@@ -40,98 +40,91 @@ composed of line segments in 2D and triangles in 3D.
:width: 400px
Planar isocontour generated using the field :math:`f(\mathbf{r}) =
- f_0 + \mathbf{r} \cdot \mathbf{n}` and spherical contour generated
- using the field field :math:`g(\mathbf{r}) = |\textbf{r} -
- \textbf{r}_0|`. Colors denote the domain index in the multi-domain
- cubic mesh.
-
-The algorithm is implemented in the class ``quest::MarchingCubes``.
-
-The inputs are:
-
-#. The mesh containing the scalar field. This mesh should be in
- Conduit's blueprint format. See
- https://llnl-conduit.readthedocs.io/en/latest/blueprint_mesh.html
-#. The name of the blueprint coordinates data for the input mesh.
-#. The name of the scalar field data within the input mesh.
-#. The contour value.
-
-The following example shows usage of the ``MarchingCubes`` class.
-(A complete example is provided in
-``src/axom/quest/examples/quest_marching_cubes_example.cpp``.)
-
-Relevant header files:
-
-.. sourcecode:: C++
-
- #include "conduit_relay_io_blueprint.hpp"
- #include "axom/quest/MarchingCubes.hpp"
- #include "axom/mint/mesh/UnstructuredMesh.hpp"
-
-Set up the user's blueprint mesh and the ``MarchingCubes`` object:
-
-The blueprint mesh must be a structured mesh in multi-domain format.
-A domain is a part of a global mesh that has been subdivided for
-reasons including parallel partitioning, geometric constraints and
-size constraints. Any number of domains is allowed, including zero.
-(For single-domain format, see the similar
-``MarchingCubesSingleDomain`` class in the ``axom::quest`` namespace.)
-
-Blueprint convention allows for named coordinate sets and scalar
-fields. Here, we tell the ``MarchingCubes`` constructor that the
-topology is "mesh", and the name of the nodal scalar
-field is "scalarFieldName".
-
-The constructor's ``quest::MarchingCubesRuntimePolicy::seq`` argument
-tells ``mc`` to run sequentially on the host. ``MarchingCubes``
-currently also supports OpenMP and GPU device executions using CUDA
-and HIP.
-
-.. sourcecode:: C++
-
- conduit::Node blueprintMesh = blueprint_mesh_from_user();
- quest::MarchingCubes mc(quest::MarchingCubesRuntimePolicy::seq,
- blueprintMesh,
- "mesh",
- "scalarFieldName");
-
-Run the algorithm:
-
-.. sourcecode:: C++
-
- double contourValue = 0.5;
- mc.computeIsocontour(contourValue);
-
-Place the isocontour in an output ``mint::UnstructuredMesh`` object:
-
-``MarchingCubes`` generates the isocontour mesh in an internal format.
-Use ``populateContourMesh`` to put it in a ``mint::UnstructuredMesh``
-object. In the future, we will support outputs in blueprint format.
-
-``populateContourMesh`` provides two scalar fields for the generated
-mesh:
-
-#. the ID of the cell from the input mesh that generated the
- isocontour cell.
-#. the ID of the domain from the input mesh that generated the
- isocontour cell.
-
-The names of these fields are user-specified. Use empty strings if
-you don't need these fields. This example puts cell IDs in
-"cellIds" and domain IDs in "domainIds".
-
-.. sourcecode:: C++
-
- mint::UnstructuredMesh contourMesh;
- mc.populateContourMesh(contourMesh, "cellIds", "domainIds");
-
-After putting the isosurface in the ``UnstructuredMesh`` object,
-the ``MarchingCubes`` object is no longer needed.
-
-MPI-parallel runs:
-
-For MPI-parallel runs, the input mesh may have local and remote
-domains. The algorithm is local in that no data communication is
-required to run. The output isosurface mesh uses node and cell
-numbers that are locally unique. Users requiring these numbers to be
-globally unique should renumber them.
+ f_0 + \mathbf{r} \cdot \mathbf{n}` and spherical contour generated using
+ the field :math:`g(\mathbf{r}) = |\textbf{r} - \textbf{r}_0|`. Colors
+ denote the domain index in the multi-domain mesh.
+
+Basic workflow
+--------------
+
+The workflow is:
+
+#. Construct a ``quest::MarchingCubes`` object with a runtime policy,
+ allocator, and data-parallel implementation choice.
+#. Call ``setMesh()`` with the Blueprint mesh and topology name. A cell mask
+ field name may also be supplied.
+#. Select the node-centered scalar field with ``setFunctionField()``.
+#. Call ``computeIsocontour()`` for each isovalue of interest.
+#. Export the contour to either a ``mint::UnstructuredMesh`` or the raw output
+ arrays.
+
+The example application in
+``/src/axom/quest/examples/quest_marching_cubes_example.cpp`` uses the
+following setup:
+
+.. literalinclude:: ../../examples/quest_marching_cubes_example.cpp
+ :start-after: _quest_marching_cubes_init_start
+ :end-before: _quest_marching_cubes_init_end
+ :language: C++
+
+After the object is configured, the caller selects a scalar field and computes
+the contour:
+
+.. literalinclude:: ../../examples/quest_marching_cubes_example.cpp
+ :start-after: _quest_marching_cubes_usage_start
+ :end-before: _quest_marching_cubes_usage_end
+ :language: C++
+
+Output
+------
+
+``MarchingCubes`` stores its output internally until it is exported or cleared.
+The simplest output path is to populate a ``mint::UnstructuredMesh``:
+
+.. literalinclude:: ../../examples/quest_marching_cubes_example.cpp
+ :start-after: _quest_marching_cubes_output_start
+ :end-before: _quest_marching_cubes_output_end
+ :language: C++
+
+The generated mesh can optionally contain:
+
+* a field with the parent cell ID for each contour facet
+* a field with the parent domain ID for each contour facet
+
+If host-side ``mint`` output is not desired, the class also exposes array-based
+accessors for connectivity, node coordinates, parent cell IDs, and parent
+domain IDs. Those arrays remain in the allocator space associated with the
+``MarchingCubes`` object.
+
+Runtime policies and implementation choices
+-------------------------------------------
+
+``MarchingCubes`` accepts an Axom runtime policy, so the contour generation can
+run on the CPU or on supported GPU backends. The
+``MarchingCubesDataParallelism`` enum controls which implementation is used:
+
+* ``byPolicy`` chooses the implementation that best matches the runtime policy.
+* ``hybridParallel`` uses a partially parallel implementation that performs
+ well on CPUs.
+* ``fullParallel`` uses a more fully parallel implementation that is intended
+ for highly parallel devices.
+
+Masking and repeated use
+------------------------
+
+The optional mask argument to ``setMesh()`` names a cell-centered integer field
+used to restrict contour generation. After a mask field is supplied, the caller
+can select which mask value to process by calling ``setMaskValue()`` before
+``computeIsocontour()``.
+
+The same ``MarchingCubes`` object can be reused for multiple fields, masks, or
+isovalues. ``computeIsocontour()`` appends new facets to the existing output,
+while ``clearOutput()`` discards the accumulated contour data.
+
+MPI-parallel runs
+-----------------
+
+For MPI-parallel runs, the input mesh may contain local and remote domains. The
+algorithm itself is local to each rank and does not require communication. The
+generated contour mesh uses locally unique node and cell numbering, so callers
+that need globally unique IDs must renumber the output.
diff --git a/src/axom/quest/docs/sphinx/linearize_curves.rst b/src/axom/quest/docs/sphinx/linearize_curves.rst
new file mode 100644
index 0000000000..e74aa95fe8
--- /dev/null
+++ b/src/axom/quest/docs/sphinx/linearize_curves.rst
@@ -0,0 +1,50 @@
+.. ## Copyright (c) Lawrence Livermore National Security, LLC and other
+.. ## Axom Project Contributors. See top-level LICENSE and COPYRIGHT
+.. ## files for dates and other details.
+.. ##
+.. ## SPDX-License-Identifier: (BSD-3-Clause)
+
+.. _linearize-curves:
+
+*****************
+Linearize Curves
+*****************
+
+``quest::LinearizeCurves`` converts 2D NURBS contour geometry into a
+``mint::SEGMENT`` mesh. This is useful when a downstream workflow wants a
+discrete polyline approximation instead of evaluating directly on the original
+curved representation.
+
+Quest provides two linearization strategies:
+
+* ``getLinearMeshUniform()`` samples each knot span with a fixed number of
+ segments. This gives predictable resolution and is often a good default when
+ the caller wants simple control over the approximation density.
+* ``getLinearMeshNonUniform()`` refines adaptively until the polyline length is
+ within a requested percent error of a higher-resolution arc-length estimate.
+ This tends to place more segments where the curve needs them most.
+
+The winding-number example uses both modes:
+
+.. literalinclude:: ../../examples/quest_winding_number_2d.cpp
+ :start-after: _linearize_curves_start
+ :end-before: _linearize_curves_end
+ :language: C++
+
+In practice, the uniform method is a good fit when a workflow already uses a
+``segments per knot span`` control, while the adaptive method is more natural
+when the caller wants to bound approximation error rather than choose a fixed
+sampling density.
+
+Revolved Volume
+---------------
+
+The same utility also provides a revolved-volume helper for contour-based
+shapes. This computes the volume generated by revolving the original curves
+about the axis of revolution using quadrature on the curve representation,
+rather than estimating the volume from an already linearized mesh.
+
+.. literalinclude:: ../../tests/quest_linearize_curves.cpp
+ :start-after: _revolved_volume_start
+ :end-before: _revolved_volume_end
+ :language: C++
diff --git a/src/axom/quest/docs/sphinx/read_mesh.rst b/src/axom/quest/docs/sphinx/read_mesh.rst
index aa8dc0d3f3..d31aa4d135 100644
--- a/src/axom/quest/docs/sphinx/read_mesh.rst
+++ b/src/axom/quest/docs/sphinx/read_mesh.rst
@@ -6,26 +6,43 @@
.. _reading-mesh:
-*****************
-Reading in a mesh
-*****************
-
-Applications commonly need to read a mesh file from disk. Quest provides the
-``STLReader`` class, which can read binary or ASCII `STL`_ files, as well as the
-``PSTLReader`` class for use in parallel codes. STL (stereolithography)
-is a common file format for triangle surface meshes. The STL reader classes
-will read the file from disk and build a ``mint::Mesh`` object. Quest also
-provides the ``ProEReader`` class, for ASCII Pro/E files containing tetrahedra,
-and the ``PProEReader`` class for use in parallel codes. PTC Creo is a modeling
-application formerly known as Pro/ENGINEER, and its file format is in use among
-Axom's users.
+****************
+Reading Geometry
+****************
+
+Quest contains several readers that translate geometry files into Axom data
+structures. The most common cases are STL surface meshes, Pro/E tetrahedral
+meshes, and geometry inputs used by shaping workflows such as STEP, C2C, and
+MFEM contour files.
+
+The STL and Pro/E readers produce ``mint::Mesh`` objects directly. Other
+readers expose geometry in forms that are better suited to downstream Quest
+algorithms, such as NURBS patches, NURBS curves, or curved polygons.
+
+Quest currently provides the following reader families:
+
+* ``STLReader`` and ``PSTLReader`` for ASCII or binary STL triangle meshes.
+* ``ProEReader`` and ``PProEReader`` for ASCII Pro/E tetrahedral meshes.
+* ``STEPReader`` and ``PSTEPReader`` for STEP B-Rep geometry represented as
+ trimmed NURBS patches, with optional triangulated output.
+* ``C2CReader`` and ``PC2CReader`` for C2C contour files represented as
+ NURBS curves, when Axom is built with the C2C dependency.
+* ``MFEMReader`` for MFEM contour files represented as curves or curved
+ polygons, when Axom is built with MFEM support.
.. _STL: https://en.wikipedia.org/wiki/STL_(file_format)
Reading an STL file
-------------------
-The code examples are excerpts from the file ``/src/tools/mesh_tester.cpp``.
+STL (stereolithography) is a common file format for triangle surface meshes.
+Quest's STL readers load the file from disk and populate an
+``mint::UnstructuredMesh`` containing triangles. STL stores triangles as a
+"triangle soup", so downstream algorithms often need a cleanup pass to weld
+duplicate vertices and check for defects. The next page describes that
+workflow.
+
+The following example is excerpted from ``/src/tools/mesh_tester.cpp``.
We include the STL reader header
@@ -48,19 +65,22 @@ For convenience, we use typedefs in the axom namespace.
:end-before: _read_stl_typedefs_end
:language: C++
-The following example shows usage of the STLReader class:
+The following example shows how to use ``STLReader``:
.. literalinclude:: ../../../../tools/mesh_tester.cpp
:start-after: _read_stl_file_start
:end-before: _read_stl_file_end
:language: C++
-After reading the STL file, the ``STLReader::getMesh`` method gives access to the
-underlying mesh data. The reader may then be deleted.
+After reading the STL file, ``STLReader::getMesh()`` gives access to the
+loaded mesh data.
Reading a Pro/E file
--------------------
+Quest's ``ProEReader`` reads ASCII Pro/E tetrahedral meshes and can optionally
+filter the tetrahedra during input.
+
As read by Axom, an ASCII Pro/E tet file contains:
- Zero or more comment lines starting with a ``#`` character
@@ -71,12 +91,11 @@ As read by Axom, an ASCII Pro/E tet file contains:
- ``t`` lines, one for each tetrahedron; each line contains a contiguous
integer ID starting at 1 and four integers specifying the tet's nodes
-Reading an ASCII Pro/E tet file is similar to reading an STL file. The code
-examples are excerpts from the file ``/src/axom/quest/examples/quest_proe_bbox.cpp``.
-The Pro/E reader has the ability to read a subset of the mesh in the file,
-defined by a user-supplied predicate function. The example code shows how
-to use a convenience function to specify a predicate that keeps only tets
-fully included in a user-supplied bounding box.
+Reading an ASCII Pro/E tet file is similar to reading an STL file. The code
+examples are excerpts from ``/src/axom/quest/examples/quest_proe_bbox.cpp``.
+The example demonstrates one of the reader's useful features: selecting a
+subset of the input tetrahedra using a predicate. In this case, the predicate
+keeps only tetrahedra whose nodes fall inside a user-supplied bounding box.
We include the ProEReader header
@@ -99,11 +118,11 @@ For convenience, we specify some type aliases.
:end-before: _read_proe_typealiases_end
:language: C++
-The following example shows how to use the ProEReader class.
+The following example shows how to use ``ProEReader``.
Calling ``reader.setTetPredFromBoundingBox(bbox, false)``, as shown in the
code, makes a tetrahedron predicate that accepts tets with all four nodes
-falling in ``bbox`` and rejects others. Alternately, the user can specify
-an arbitrary predicate function with ``setTetPred()``. If the user specifies
+falling in ``bbox`` and rejects others. Alternately, the user can specify
+an arbitrary predicate function with ``setTetPred()``. If the user specifies
no tetrahedron predicate, the reader reads all tets in the file.
.. literalinclude:: ../../examples/quest_proe_bbox.cpp
@@ -111,5 +130,70 @@ no tetrahedron predicate, the reader reads all tets in the file.
:end-before: _read_proe_file_end
:language: C++
-After reading the Pro/E file, the ``ProEReader::getMesh`` method gives access
-to the underlying mesh data. The reader may then be deleted.
+After reading the Pro/E file, ``ProEReader::getMesh()`` gives access to the
+loaded mesh data.
+
+Other Quest readers
+-------------------
+
+Quest includes several other readers that are commonly used with shaping and
+CAD-oriented workflows.
+
+``STEPReader``
+^^^^^^^^^^^^^^
+
+``STEPReader`` reads trimmed STEP surfaces using Open Cascade. It can expose
+the model as NURBS patches and trimming curves, query metadata such as patch
+IDs and bounding boxes, and generate a triangulated ``mint::UnstructuredMesh``
+approximation of the model when a surface mesh is needed.
+
+This reader is available when Axom is configured with Open Cascade support.
+The following example, excerpted from
+``/src/axom/quest/examples/quest_winding_number_3d.cpp``, reads a STEP
+file, loads its trimmed NURBS patches, and queries the model bounding box.
+
+.. literalinclude:: ../../examples/quest_winding_number_3d.cpp
+ :start-after: _read_step_file_start
+ :end-before: _read_step_file_end
+ :language: C++
+
+When a triangle mesh approximation is needed, ``STEPReader`` can also
+triangulate the loaded B-Rep:
+
+.. literalinclude:: ../../examples/quest_winding_number_3d.cpp
+ :start-after: _read_step_triangulate_start
+ :end-before: _read_step_triangulate_end
+ :language: C++
+
+``C2CReader``
+^^^^^^^^^^^^^
+
+``C2CReader`` reads contour files and stores the result as NURBS curves. This
+reader is available when Axom is configured with the C2C dependency and is
+primarily used by shaping workflows that revolve or sample contour geometry.
+
+The following example, excerpted from
+``/src/axom/quest/examples/containment_driver.cpp``, reads a contour file
+and linearizes the resulting curves into a segment mesh:
+
+.. literalinclude:: ../../examples/containment_driver.cpp
+ :start-after: _read_c2c_file_start
+ :end-before: _read_c2c_file_end
+ :language: C++
+
+``MFEMReader``
+^^^^^^^^^^^^^^
+
+``MFEMReader`` reads MFEM contour files and can return either individual curves
+or grouped curved polygons. It is available when Axom is configured with MFEM
+support. Quest uses this representation in workflows such as sampling-based
+shaping with winding-number containment tests.
+
+The following example, excerpted from
+``/src/axom/quest/examples/quest_winding_number_2d.cpp``, reads an MFEM
+contour file into an array of NURBS curves:
+
+.. literalinclude:: ../../examples/quest_winding_number_2d.cpp
+ :start-after: _read_mfem_file_start
+ :end-before: _read_mfem_file_end
+ :language: C++
diff --git a/src/axom/quest/docs/sphinx/shaping.rst b/src/axom/quest/docs/sphinx/shaping.rst
new file mode 100644
index 0000000000..b218a2e695
--- /dev/null
+++ b/src/axom/quest/docs/sphinx/shaping.rst
@@ -0,0 +1,341 @@
+.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and
+.. ## other Axom Project Developers. See the top-level LICENSE file for details.
+.. ##
+.. ## SPDX-License-Identifier: (BSD-3-Clause)
+
+.. _shaping-overview:
+
+Shaping Overview
+================
+
+Shaping is the process of overlaying additional detail into a mesh by converting
+shape geometry into materials described as volume fractions within each mesh zone.
+Shaping is used when it is not feasible or practical to directly build features
+into the mesh itself.
+
+.. figure:: figs/shaping_overview.png
+ :width: 800px
+
+ Shaping permits details to be added into meshes.
+
+Axom's Klee component describes the models used for shaping. A Klee shape set
+contains the shape geometry reference, its material name, replacement rules, and
+any transforms that should be applied before shaping. Quest provides the
+algorithms that read those shapes, compare them against a target mesh, and
+generate the material volume fractions.
+
+Quest provides two shaping implementations:
+
+* ``SamplingShaper`` estimates overlap by sampling points in each target zone
+ and evaluating in/out tests against the shape.
+* ``IntersectionShaper`` computes overlap geometrically by intersecting the
+ target mesh with discretized shape geometry.
+
+Both shapers share the same high-level pipeline and both write volume fractions
+as fields named ``vol_frac_`` on the target mesh.
+
+For MFEM-based shaping workflows, those material fields are typically
+``mfem::GridFunction`` objects registered in the target
+``MFEMSidreDataCollection``. The caller chooses the output field order. In the
+sampling workflow, that order is set explicitly with
+``SamplingShaper::setVolumeFractionOrder()``; in the example driver, it is
+controlled by the selected output order. For Blueprint-based shaping workflows,
+the same material information is written as fields on the Blueprint mesh.
+
+
+.. _shaping-pipeline:
+
+Shaping Pipeline
+----------------
+
+Shaping involves creating a target mesh and data collection, reading a shape
+set, creating a shaper, and then iterating over the shapes in the set. For each
+shape, the shaper loads the geometry, prepares a spatial query structure, runs
+the query against the target mesh, applies replacement rules, and cleans up any
+temporary state before moving to the next shape.
+
+.. figure:: figs/shaping_pipeline.svg
+ :width: 800px
+
+ Quest shaping pipeline from Klee shape descriptions to material volume fraction fields.
+
+First, we include relevant Axom headers:
+
+.. code-block:: c++
+
+ #include
+ #include
+ #include
+ #include
+
+ using quest = axom::quest;
+ using klee = axom::klee;
+ using slic = axom::slic;
+ using sidre = axom::sidre;
+
+Quest shaping APIs operate on either:
+
+* MFEM meshes stored in ``sidre::MFEMSidreDataCollection`` objects
+* Blueprint meshes stored in a ``sidre::Group`` or ``conduit::Node``
+
+The example below uses an MFEM mesh and an ``MFEMSidreDataCollection`` to store
+the volume fraction fields.
+
+More information on MFEM is covered at the `MFEM examples page `_.
+
+The MFEM mesh also needs an associated data collection, ``shapingDC``, to
+contain the grid functions. Axom provides ``MFEMSidreDataCollection``, a
+derived class of MFEM's ``DataCollection`` that interoperates with Sidre.
+
+.. literalinclude:: ../../examples/shaping_driver.cpp
+ :start-after: _load_mesh_start
+ :end-before: _load_mesh_end
+ :language: C++
+
+
+Next, create the desired shaper and configure its parameters. Some settings live
+in the common ``Shaper`` base class and apply to both shaping methods. Examples
+include the runtime policy, the linearization density for curved geometry, the
+vertex welding threshold for surface meshes, verbosity, and the controls for
+dynamic refinement based on a percent error target.
+
+The shaper will operate on shapes from a Klee shape set:
+
+.. code-block:: c++
+
+ auto shapeSet = klee::readShapeSet("/path/to/klee/file");
+
+
+The shaper can also be pre-initialized with volume fractions supplied by the
+calling code. This is optional, but it is useful when a simulation mesh already
+contains background or previously shaped materials. Volume fraction fields use
+the ``vol_frac_`` prefix followed by the material name.
+
+.. literalinclude:: ../../examples/shaping_driver.cpp
+ :start-after: _import_volume_fractions_start
+ :end-before: _import_volume_fractions_end
+ :language: C++
+
+
+After the target mesh, shaper, and shape set are ready, the shaping pipeline is
+the same for both ``SamplingShaper`` and ``IntersectionShaper``. Each shape is:
+
+#. loaded from its geometry file
+#. prepared for querying
+#. queried against the target mesh
+#. applied through its replacement rules
+#. finalized so the shaper is ready for the next shape
+
+.. literalinclude:: ../../examples/shaping_driver.cpp
+ :start-after: _shaping_pipeline_begin
+ :end-before: _shaping_pipeline_end
+ :language: C++
+
+After all shapes have been processed, the shaper adjusts the accumulated volume
+fractions so the material fields on the target mesh are ready for output. For
+MFEM targets, the result is usually a set of ``vol_frac_``
+``mfem::GridFunction`` fields, one per material, that can be saved with the
+data collection or used directly by downstream code.
+
+
+.. _sampling-shaper:
+
+Sampling Shaper
+---------------
+
+``SamplingShaper`` creates volume fractions by sampling points inside each
+target zone and determining whether those points are inside the shape. The
+sampled results are then converted into zone-centered or higher-order volume
+fraction fields.
+
+On MFEM target meshes, these outputs are material-specific
+``mfem::GridFunction`` objects. The field order is chosen by the caller, so the
+result can be piecewise constant or higher order depending on the needs of the
+simulation or analysis workflow.
+
+``SamplingShaper`` supports:
+
+* 2D and 3D target meshes
+* STL and Pro/E geometry loaded as discrete surfaces
+* contour-based workflows, including MFEM contours and revolved shaping through
+ point projection
+* higher-order output volume fraction fields
+* CPU execution, with additional runtime-policy-dependent samplers for some
+ 3D primitive workflows
+
+.. figure:: figs/sampling.png
+ :width: 300px
+
+ Sampling shaper tests whether points in a zone are in/out for a shape.
+
+Sampler selection
+^^^^^^^^^^^^^^^^^
+
+``SamplingShaper`` uses one of three internal sampler types to answer the
+point-vs-shape queries that drive the volume fraction calculation. The sampler
+is currently chosen primarily from the shape representation that Quest is
+working with and, in one case, from the user-selected sampling method. The
+examples below describe the current implementation without implying that the
+samplers are inherently tied to specific readers.
+
+``InOutSampler``
+""""""""""""""""
+
+The in/out sampler uses Quest's spatial-index-based containment queries on
+discrete geometry. It is the default sampler for shapes that Quest has turned
+into discrete curves or surfaces. In the current implementation, this includes:
+
+* 2D contour geometry loaded from C2C files
+* MFEM contour geometry when ``SamplingMethod::InOut`` is selected
+* 3D surface geometry loaded from STL files
+
+In practice, choose the in/out sampler when the shape is being queried through
+discrete line or surface geometry and the standard containment query is
+appropriate. In the example driver, this is the behavior selected by
+``samplingShaper->setSamplingMethod(quest::SamplingShaper::SamplingMethod::InOut);``
+or by leaving the method at its default.
+
+``PrimitiveSampler``
+""""""""""""""""""""
+
+The primitive sampler operates directly on primitive volumetric elements rather
+than a surface-based in/out query. In the current implementation, this path is
+used when Quest is sampling tetrahedral shape geometry, with a backend chosen
+from the active runtime policy.
+
+Use this path when the shape geometry is already volumetric and Quest can avoid
+going through a surface containment query. This is also the sampler family that
+maps naturally onto the supported CPU and GPU execution policies for these
+primitive-based cases.
+
+``WindingNumberSampler``
+""""""""""""""""""""""""
+
+The winding-number sampler evaluates winding numbers on a curved contour
+representation instead of using the standard in/out spatial index. In the
+current implementation, this option is available for MFEM contour geometry.
+
+Select it explicitly with
+``samplingShaper->setSamplingMethod(quest::SamplingShaper::SamplingMethod::WindingNumber);``.
+Quest will use it when the shape format is MFEM and the sampling method is set
+to ``WindingNumber``.
+
+This sampler is useful when the curved MFEM representation itself is the
+important source of truth and you want the containment query to operate on that
+curve-based model. It is currently a CPU-oriented path.
+
+For the broader direct, linearized, and fast approximate generalized
+winding-number workflows, see :doc:`Winding Numbers `.
+
+Curve-based workflows that need a discrete segment representation can use
+:doc:`Linearize Curves ` to convert NURBS contours into a
+polyline mesh before sampling or winding-number evaluation.
+
+Summary
+"""""""
+
+In short:
+
+* choose ``InOut`` for the standard default behavior on shapes queried through
+ discrete curves or surfaces
+* choose ``WindingNumber`` when curved contour geometry should be queried
+ through winding numbers rather than the standard in/out path
+* expect ``PrimitiveSampler`` to be chosen automatically when Quest is sampling
+ supported volumetric primitive geometry
+
+
+Accuracy
+^^^^^^^^
+
+The main accuracy controls are:
+
+* ``setSamplesPerKnotSpan()`` controls how finely curved input geometry is
+ linearized before sampling.
+* ``setQuadratureOrder()`` controls how many sample points are used in each
+ target zone.
+* ``setVolumeFractionOrder()`` controls the polynomial order of the output
+ volume fraction field.
+
+For curved inputs, increasing the number of samples per knot span improves the
+geometric approximation of the shape. Increasing the quadrature order improves
+the overlap estimate within each target zone.
+
+.. code-block:: c++
+
+ shaper->setSamplesPerKnotSpan(25);
+ shaper->setQuadratureOrder(5);
+ shaper->setVolumeFractionOrder(2);
+
+
+Point Projection
+^^^^^^^^^^^^^^^^
+
+``SamplingShaper`` can use point projectors to map points from the target mesh
+into the coordinate system used by the shape query. One common use case is
+projecting 3D mesh points into a 2D RZ space so that a 2D contour can define a
+revolved 3D shape.
+
+.. literalinclude:: ../../examples/shaping_driver.cpp
+ :start-after: _point_projection_obj_begin
+ :end-before: _point_projection_obj_end
+ :language: C++
+
+.. literalinclude:: ../../examples/shaping_driver.cpp
+ :start-after: _point_projection_begin
+ :end-before: _point_projection_end
+ :language: C++
+
+.. _intersection-shaper:
+
+Intersection Shaper
+--------------------
+
+``IntersectionShaper`` computes overlap geometrically instead of sampling. It
+discretizes the shape into intersection-friendly primitives and intersects those
+primitives with the target mesh zones to compute volume fractions.
+
+Depending on the input, the generated intersection geometry differs:
+
+* 2D C2C contours can be refined into segments and intersected against 2D meshes.
+* 3D shaping from 2D contours revolves the refined contour into truncated-cone
+ geometry that is approximated using octahedra.
+* Pro/E tetrahedral meshes and analytical 3D shapes are converted into
+ tetrahedral intersection geometry.
+* STL input can be used for 2D triangle-based workflows and other discrete
+ surface cases handled by the implementation.
+
+``IntersectionShaper`` supports 3D workflows on CPUs and supported GPU
+backends, and it can operate on MFEM or Blueprint target meshes.
+
+.. figure:: figs/intersection.png
+ :width: 400px
+
+ Intersection shaper creates revolved geometry for a shape and determines volume intersection with target mesh zones.
+
+Accuracy
+^^^^^^^^^
+
+The main accuracy controls for ``IntersectionShaper`` are the refinement level
+and the optional percent-error target. The refinement level controls how finely
+the internal intersection geometry is subdivided. When a percent error is
+provided, Quest can switch to dynamic refinement and keep refining until the
+estimated volume error falls below the requested tolerance.
+
+.. code-block:: c++
+
+ shaper->setPercentError(0.02);
+ shaper->setRefinementType(quest::Shaper::RefinementDynamic);
+
+Materials and replacement rules
+-------------------------------
+
+Both shapers write their results as material volume fractions on the target
+mesh. ``IntersectionShaper`` also maintains a free-material field, named
+``free`` by default, to represent any volume that has not yet been claimed by a
+user-defined material. This field is especially useful when shapes overwrite
+existing materials through replacement rules.
+
+Quest applies replacement rules from the Klee shape description when it merges
+each shape's volume fractions into the existing material state. For more on the
+``replaces`` and ``does_not_replace`` properties, see Klee's
+:ref:`Overlay Rules ` documentation.
diff --git a/src/axom/quest/docs/sphinx/winding_number.rst b/src/axom/quest/docs/sphinx/winding_number.rst
new file mode 100644
index 0000000000..e4d7faa936
--- /dev/null
+++ b/src/axom/quest/docs/sphinx/winding_number.rst
@@ -0,0 +1,71 @@
+.. ## Copyright (c) Lawrence Livermore National Security, LLC and other
+.. ## Axom Project Contributors. See top-level LICENSE and COPYRIGHT
+.. ## files for dates and other details.
+.. ##
+.. ## SPDX-License-Identifier: (BSD-3-Clause)
+
+.. _winding-number:
+
+****************
+Winding Numbers
+****************
+
+Quest provides generalized winding number workflows for querying curved and
+linearized geometry on MFEM query meshes. These workflows are useful when the
+goal is not just a binary in/out test at quadrature points, but a
+winding-number field and its derived in/out classification over a
+user-defined query mesh.
+
+At a high level, there are two paths:
+
+* A direct path that evaluates winding number on curved geometry, such as NURBS
+ curves in 2D or NURBS patches in 3D. This is the most natural choice when
+ the curved representation is the source of truth and preserving that geometry
+ in the query is more important than maximizing throughput.
+* A linearized path that first replaces the curved shape with segments or
+ triangles and then evaluates the same winding-number quantity on that
+ discretized geometry. This path is useful when the input is already discrete
+ or when a linearized representation is acceptable for the query.
+
+Basic Workflow
+--------------
+
+The 2D and 3D winding-number examples follow the same basic pattern:
+
+* choose a direct curved or linearized query method
+* generate an MFEM query mesh and its associated ``winding`` and ``inout`` fields
+* preprocess the input geometry for the selected method
+* evaluate the query on the mesh
+
+The 2D example shows this structure directly:
+
+.. literalinclude:: ../../examples/quest_winding_number_2d.cpp
+ :start-after: _gwn_query_workflow_start
+ :end-before: _gwn_query_workflow_end
+ :language: C++
+
+Fast Approximate Methods
+------------------------
+
+The "fast" winding-number methods build on the linearized path. They keep the
+same basic query quantity, but add preprocessing over the segment or triangle
+representation so that large batches of queries can be evaluated more
+efficiently. In the current implementation, that acceleration uses a hierarchy
+over the linearized geometry together with precomputed moment data to
+approximate the contribution of well-separated clusters.
+
+In practice:
+
+* use the direct workflow when curved geometry fidelity matters most
+* use the linearized workflow when a segment or triangle approximation is
+ already available or acceptable
+* use the fast linearized workflow when query counts are high enough that extra
+ preprocessing is worth the reduction in per-query cost
+
+Query Outputs
+-------------
+
+The example programs ``quest_winding_number_2d.cpp`` and
+``quest_winding_number_3d.cpp`` show these workflows end to end, including
+query-mesh setup, preprocessing, winding-number evaluation, and creation of the
+derived ``inout`` field by rounding the winding-number result.
diff --git a/src/axom/quest/examples/containment_driver.cpp b/src/axom/quest/examples/containment_driver.cpp
index 7d6c5399a6..53b3e59486 100644
--- a/src/axom/quest/examples/containment_driver.cpp
+++ b/src/axom/quest/examples/containment_driver.cpp
@@ -72,6 +72,7 @@ class ContainmentDriver
void loadContourMesh(const std::string& inputFile, int segmentsPerKnotSpan)
{
AXOM_ANNOTATE_SCOPE("load c2c");
+ // _read_c2c_file_start
quest::C2CReader reader;
reader.setFileName(inputFile);
reader.read();
@@ -82,6 +83,7 @@ class ContainmentDriver
lin.getLinearMeshUniform(reader.getCurvesView(),
static_cast(m_surfaceMesh.get()),
segmentsPerKnotSpan);
+ // _read_c2c_file_end
}
#else
void loadContourMesh(const std::string& inputFile, int segmentsPerKnotSpan)
diff --git a/src/axom/quest/examples/delaunay_triangulation.cpp b/src/axom/quest/examples/delaunay_triangulation.cpp
index 8b4486d288..23dd598c08 100644
--- a/src/axom/quest/examples/delaunay_triangulation.cpp
+++ b/src/axom/quest/examples/delaunay_triangulation.cpp
@@ -21,6 +21,11 @@
#include "axom/fmt.hpp"
#include "axom/CLI11.hpp"
+// _quest_delaunay_include_start
+using Delaunay2D = axom::quest::Delaunay<2>;
+using Delaunay3D = axom::quest::Delaunay<3>;
+// _quest_delaunay_include_end
+
/// Struct to parse and contain command line arguments
struct Input
{
@@ -133,6 +138,7 @@ void run_delaunay(const Input& params)
axom::utilities::Timer timer(true);
// Create initial Delaunay triangulation over bounding box
+ // _quest_delaunay_basic_start
Delaunay dt;
dt.initializeBoundary(bbox);
@@ -158,6 +164,7 @@ void run_delaunay(const Input& params)
//Remove the starting rectangular box
dt.removeBoundary();
+ // _quest_delaunay_basic_end
timer.stop();
@@ -175,8 +182,10 @@ void run_delaunay(const Input& params)
{
timer.reset();
timer.start();
+ // _quest_delaunay_validate_start
dt.getMeshData()->isValid(true);
dt.isValid(true);
+ // _quest_delaunay_validate_end
timer.stop();
SLIC_INFO(axom::fmt::format("Validation took {} seconds", timer.elapsedTimeInSec()));
}
@@ -185,7 +194,9 @@ void run_delaunay(const Input& params)
{
std::string fname = axom::fmt::format("{}.vtk", outputVTKFile);
SLIC_INFO(axom::fmt::format("Writing out final Delaunay complex to file '{}'", fname));
+ // _quest_delaunay_output_start
dt.writeToVTKFile(fname);
+ // _quest_delaunay_output_end
}
SLIC_INFO("Done!");
diff --git a/src/axom/quest/examples/quest_marching_cubes_example.cpp b/src/axom/quest/examples/quest_marching_cubes_example.cpp
index 3c19f9734f..b5e0ab9ff6 100644
--- a/src/axom/quest/examples/quest_marching_cubes_example.cpp
+++ b/src/axom/quest/examples/quest_marching_cubes_example.cpp
@@ -841,9 +841,11 @@ struct ContourTestBase
{
AXOM_ANNOTATE_SCOPE("MCInit");
initializationTimer.start();
+ // _quest_marching_cubes_init_start
mcPtr =
std::make_unique(params.policy, s_allocatorId, params.dataParallelism);
mcPtr->setMesh(computationalMesh.asConduitNode(), "mesh", "mask");
+ // _quest_marching_cubes_init_end
initializationTimer.stop();
}
auto& mc = *mcPtr;
@@ -865,6 +867,7 @@ struct ContourTestBase
params.objectRepCount,
i,
params.contourGenCount));
+ // _quest_marching_cubes_usage_start
mc.clearOutput();
m_strategyFacetPrefixSum.clear();
m_strategyFacetPrefixSum.push_back(0);
@@ -894,6 +897,7 @@ struct ContourTestBase
}
m_strategyFacetPrefixSum.push_back(mc.getContourFacetCount());
}
+ // _quest_marching_cubes_usage_end
}
contourGenLoopTimer.stop();
}
@@ -956,7 +960,9 @@ struct ContourTestBase
#endif
axom::utilities::Timer extractTimer(false);
extractTimer.start();
+ // _quest_marching_cubes_output_start
mc.populateContourMesh(contourMesh, m_parentCellIdField, m_domainIdField);
+ // _quest_marching_cubes_output_end
extractTimer.stop();
printTimingStats(extractTimer, "extract");
diff --git a/src/axom/quest/examples/quest_winding_number_2d.cpp b/src/axom/quest/examples/quest_winding_number_2d.cpp
index 5e29371646..164082082e 100644
--- a/src/axom/quest/examples/quest_winding_number_2d.cpp
+++ b/src/axom/quest/examples/quest_winding_number_2d.cpp
@@ -497,6 +497,7 @@ int main(int argc, char** argv)
AXOM_ANNOTATE_SCOPE("read_mesh");
axom::quest::MFEMReader mfem_reader;
+ // _read_mfem_file_start
mfem_reader.setFileName(input.inputFile);
const int ret = mfem_reader.read(curves);
@@ -505,6 +506,7 @@ int main(int argc, char** argv)
SLIC_ERROR("Failed to read MFEM file.");
return 1;
}
+ // _read_mfem_file_end
}
// Linearize the input curves if asked for
@@ -514,6 +516,7 @@ int main(int argc, char** argv)
AXOM_ANNOTATE_SCOPE("linearization");
axom::utilities::Timer timer(true);
+ // _linearize_curves_start
axom::quest::LinearizeCurves lc;
if(input.useUniformLinearization)
{
@@ -523,6 +526,7 @@ int main(int argc, char** argv)
{
lc.getLinearMeshNonUniform(curves.view(), &poly_mesh, input.percentError);
}
+ // _linearize_curves_end
timer.stop();
SLIC_INFO(axom::fmt::format(
@@ -553,6 +557,7 @@ int main(int argc, char** argv)
// if user did not provide a bounding box, user input bounding box scaled by 10%
mfem::DataCollection dc("winding_query");
{
+ // _gwn_query_workflow_start
// Create the desired winding number query instance
auto wn_query =
make_gwn_query(input.policy, app.got_subcommand("linearize_curves"), input.approximation_order);
@@ -582,6 +587,7 @@ int main(int argc, char** argv)
// Run the query
std::visit([&](auto& wn) { wn.query(dc, input.tol); }, wn_query);
+ // _gwn_query_workflow_end
}
// Postprocess query results: norms, ranges, and integral statistics
diff --git a/src/axom/quest/examples/quest_winding_number_3d.cpp b/src/axom/quest/examples/quest_winding_number_3d.cpp
index e043e0c2a5..faa82d8b15 100644
--- a/src/axom/quest/examples/quest_winding_number_3d.cpp
+++ b/src/axom/quest/examples/quest_winding_number_3d.cpp
@@ -361,6 +361,7 @@ int main(int argc, char** argv)
AXOM_ANNOTATE_SCOPE("read_step");
axom::quest::STEPReader step_reader;
+ // _read_step_file_start
step_reader.setFileName(input.inputFile);
step_reader.setVerbosity(input.verbose);
@@ -374,6 +375,7 @@ int main(int argc, char** argv)
read_timer.stop();
shape_bbox = step_reader.getBRepBoundingBox();
+ // _read_step_file_end
int num_trimming_curves = 0;
for(const auto& patch : step_reader.getPatchArray())
@@ -395,6 +397,7 @@ int main(int argc, char** argv)
read_timer.reset();
read_timer.start();
AXOM_ANNOTATE_SCOPE("triangulation");
+ // _read_step_triangulate_start
const int tc = step_reader.getTriangleMesh(&tri_mesh,
input.linear_deflection,
input.angular_deflection,
@@ -405,6 +408,7 @@ int main(int argc, char** argv)
SLIC_ERROR("Failed to triangulate STEP geometry.");
return 1;
}
+ // _read_step_triangulate_end
read_timer.stop();
SLIC_INFO(
diff --git a/src/axom/quest/examples/shaping_driver.cpp b/src/axom/quest/examples/shaping_driver.cpp
index 8210d30d06..ee4d6b2f2c 100644
--- a/src/axom/quest/examples/shaping_driver.cpp
+++ b/src/axom/quest/examples/shaping_driver.cpp
@@ -51,6 +51,7 @@ namespace
using Point2D = primal::Point;
using Point3D = primal::Point;
+// _point_projection_obj_begin
struct AxisymmetricProjector32
{
AXOM_HOST_DEVICE Point2D operator()(Point3D pt) const
@@ -61,6 +62,7 @@ struct AxisymmetricProjector32
return Point2D {z, sqrt(x * x + y * y)};
}
};
+// _point_projection_obj_end
struct Projector23
{
@@ -535,6 +537,7 @@ int main(int argc, char** argv)
//---------------------------------------------------------------------------
// Set up DataCollection for shaping
//---------------------------------------------------------------------------
+ // _load_mesh_start
mfem::Mesh* shapingMesh = nullptr;
constexpr bool dc_owns_data = true;
sidre::MFEMSidreDataCollection shapingDC("shaping", shapingMesh, dc_owns_data);
@@ -546,6 +549,7 @@ int main(int argc, char** argv)
(pmesh != nullptr) ? new mfem::ParMesh(*pmesh) : new mfem::Mesh(*originalMeshDC->GetMesh());
shapingDC.SetMesh(shapingMesh);
}
+ // _load_mesh_end
AXOM_ANNOTATE_END("load mesh");
printMeshInfo(shapingDC.GetMesh(), "After loading");
@@ -596,7 +600,9 @@ int main(int argc, char** argv)
// register point projectors
if(shapingDC.GetMesh()->Dimension() == 3)
{
+ // _point_projection_begin
samplingShaper->setPointProjector32(AxisymmetricProjector32 {});
+ // _point_projection_end
}
else if(shapingDC.GetMesh()->Dimension() == 2)
{
@@ -618,6 +624,7 @@ int main(int argc, char** argv)
//---------------------------------------------------------------------------
// Project initial volume fractions, if applicable
//---------------------------------------------------------------------------
+ // _import_volume_fractions_start
if(auto* samplingShaper = dynamic_cast(shaper))
{
AXOM_ANNOTATE_SCOPE("import initial volume fractions");
@@ -651,6 +658,7 @@ int main(int argc, char** argv)
// Project provided volume fraction grid functions as quadrature point data
samplingShaper->importInitialVolumeFractions(initial_grid_functions);
}
+ // _import_volume_fractions_end
AXOM_ANNOTATE_END("setup shaping problem");
AXOM_ANNOTATE_END("init");
@@ -659,6 +667,7 @@ int main(int argc, char** argv)
//---------------------------------------------------------------------------
SLIC_INFO(axom::fmt::format("{:=^80}", "Sampling InOut fields for shapes"));
AXOM_ANNOTATE_BEGIN("shaping");
+ // _shaping_pipeline_begin
for(const auto& shape : params.shapeSet.getShapes())
{
const std::string shapeFormat = shape.getGeometry().getFormat();
@@ -698,6 +707,7 @@ int main(int argc, char** argv)
shaper->finalizeShapeQuery();
slic::flushStreams();
}
+ // _shaping_pipeline_end
AXOM_ANNOTATE_END("shaping");
//---------------------------------------------------------------------------
diff --git a/src/axom/quest/tests/quest_linearize_curves.cpp b/src/axom/quest/tests/quest_linearize_curves.cpp
index fa0e5ad5a0..c224c964a7 100644
--- a/src/axom/quest/tests/quest_linearize_curves.cpp
+++ b/src/axom/quest/tests/quest_linearize_curves.cpp
@@ -130,8 +130,10 @@ TEST(quest_linearize_curves, revolved_volume)
// Compute the revolved volume
const double expectedVolume = 4.15330715158103;
axom::quest::LinearizeCurves lin;
+ // _revolved_volume_start
const auto transform = axom::numerics::Matrix::identity(4);
const double revolvedVolume = lin.getRevolvedVolume(curves.view(), transform);
+ // _revolved_volume_end
EXPECT_NEAR(revolvedVolume, expectedVolume, 3.e-3);
}
diff --git a/src/tools/mesh_tester.cpp b/src/tools/mesh_tester.cpp
index 976b34d940..6ff1a676f6 100644
--- a/src/tools/mesh_tester.cpp
+++ b/src/tools/mesh_tester.cpp
@@ -22,8 +22,8 @@
// _read_stl_include1_start
#include "axom/quest/io/STLReader.hpp"
-#include "axom/quest/io/STLWriter.hpp"
// _read_stl_include1_end
+#include "axom/quest/io/STLWriter.hpp"
// _check_repair_include_start
#include "axom/quest/MeshTester.hpp"
// _check_repair_include_end