Skip to content

Writing documentation

The site is MkDocs with the Material theme, built from docs/ by mkdocs.yml. Everything on it is either written in docs/ or generated from the code on every build; nothing generated is committed.

Building the site

pip install -r docs/requirements.txt
mkdocs serve            # http://127.0.0.1:8000, rebuilt on every change
mkdocs build --strict   # as CI does: a broken link or anchor fails the build

CI (.github/workflows/docs.yml) builds the site with --strict on every pull request and deploys it from main, with the Doxygen C++ reference under /api/.

What is generated

docs/tools/gen_reference.py reads every components/<name>/wmtk/components/<name>/<name>_spec.json and docs/components.yml, and writes, at build time:

  • reference/<name>.md: every option of the component, with its type, default and doc. The options in key_options (components.yml) come first; DEBUG_, EXPERIMENTAL_ and TESTING_ options are folded away. Each option has an anchor: link to one as ../reference/tetwild.md#eps_rel.
  • reference/<name>_defaults.json: the options that have a default, with it.
  • schemas/<name>.json and schemas/wmtk_app.json: JSON Schemas for editors (see JSON Schemas).
  • The reference's navigation: a new component appears on its own.

So the spec is the documentation of an option: improve an option's description by editing its doc in the spec, and help(wildmeshing.<name>), wmtk_app --options <name> and the reference change with it. gen_reference.py --check (run in CI) fails when a rule has no doc, when a spec has no entry in components.yml, or when an entry names an option its spec does not have.

Examples

Every example on the site is a file under docs/examples/, included into pages, built and run by the tests. A page never contains example code of its own that is not in one of these files.

Directory What How it is tested
docs/examples/json/<name>.json a wmtk_app input file copied into a directory with the meshes it names, run with wmtk_app -j
docs/examples/python/<name>.py a script run with python <name>.py in a directory of its own
docs/examples/cpp/<name>.cpp a program built as wmtk_doc_example_<name>, linking every component, and run

All three are registered in ctest when CMake runs with -DWMTK_BUILD_DOC_EXAMPLES=ON (and BUILD_TESTING), in Release builds only: doc_example_cpp_<name> for each program, and doc_examples_python_json for the scripts and JSON files together (tests/python/test_docs_examples.py).

Conventions, so that the files run the same from the repository root, in the tests and on a reader's machine:

  • Input meshes come from data/models (the data repository, cloned by CMake when tests are enabled). Python and C++ read the directory from the environment variable WMTK_DATA, defaulting to data/models, so they run from the repository root as they are:

    DATA = Path(os.environ.get("WMTK_DATA", "data/models"))
    
    const char* env = std::getenv("WMTK_DATA");
    const std::filesystem::path data = env ? env : "data/models";
    

    A JSON file names its meshes by file name alone ("input": "bunny.obj"): wmtk_app resolves input paths against the JSON file's directory (and writes outputs to the working directory), and the test copies the meshes there and runs in it. - Outputs go to the working directory, under names that say what they are (bunny_remeshed.msh). - Small and fast: an example takes seconds, a minute at most in Release. Pick a small mesh and options that keep the run short; a tutorial explains what to change for quality. - Sections: mark the part a page shows with snippet markers, and include it by name:

    outputs, report = wm.qslim(...)
    
    octocat = wm.read_mesh(str(DATA / "Octocat.obj"))
    outputs, report = wm.qslim(
        {"input": "octocat", "target_rel": 0.1, "eps_rel": 0.002, "num_threads": 4},
        {"octocat": octocat},
    )
    simplified = outputs["mesh"]
    print(len(octocat["V"]), "->", len(simplified["V"]), "vertices")
    

    A section name may not start with a digit ([start:2d] fails the build; [start:two_d] works). Inside a fenced block on the page, with the language set (```python). Paths are relative to the repository root. Including a whole file (--8<-- "docs/examples/json/qslim.json") is right for JSON. - An example with a test of its own starts with # docs-example: tested by <test file>, and test_docs_examples.py skips it (the tracking tutorial's pipeline is one: tests/python/test_tracking_correspondences.py).

Run them while writing:

cmake -B build -DWMTK_BUILD_DOC_EXAMPLES=ON ...
cmake --build build --target wmtk_doc_example_qslim wmtk_app wildmeshing
ctest --test-dir build -R 'doc_example' --output-on-failure

Renderings

Pages show meshes in an interactive viewer, <wmtk-viewer> (docs/viewer, three.js, loaded on every page): the input on the left, the output on the right, one camera; tet meshes with a cut plane; cells colored by quality or by region. A page shows a gallery example by its id:

<div>
<wmtk-viewer example="qslim"></wmtk-viewer>
</div>

The examples are listed in docs/gallery/examples.yaml. Each runs a tutorial's JSON example (json: qslim), a component on meshes of data/models (component, inputs, options), or a pipeline, a function of docs/gallery/scenes.py that runs a workflow's script and returns the meshes to show. docs/tools/build_gallery.py runs them through the Python bindings and writes their assets to docs/gallery/assets/<id>/ (not committed: CI builds them, cached by the code they depend on); --posters also takes a still of each, used before the viewer starts and in the README. The gallery page is generated from the same list. To preview locally:

PYTHONPATH=build/bin python docs/tools/build_gallery.py qslim --posters
mkdocs serve

build_gallery.py --check (in CI) fails when a page names an example the list does not have. The viewer's source is docs/viewer/src/wmtk-viewer.js; after changing it, rebuild the bundle (npm install && npm run build in docs/viewer) and commit docs/assets/viewer/wmtk-viewer.min.js. Keep the input on the left and the output on the right in every figure.

A tutorial page

One page per component, docs/tutorials/<name>.md, in this order:

  1. What it does, in a paragraph, and when to use it (and when another component fits better).
  2. A first run, in three tabs over the same run, with the same options:

    === "Command line"
    
        ```json
        {
          "application": "qslim",
          "input": "Octocat.obj",
          "output": "octocat_qslim.obj",
          "target_rel": 0.1,
          "eps_rel": 0.002,
          "num_threads": 4
        }
    
        ```
    
        ```bash
        wmtk_app -j qslim.json
        ```
    
    === "Python"
    
        ```python
        octocat = wm.read_mesh(str(DATA / "Octocat.obj"))
        outputs, report = wm.qslim(
            {"input": "octocat", "target_rel": 0.1, "eps_rel": 0.002, "num_threads": 4},
            {"octocat": octocat},
        )
        simplified = outputs["mesh"]
        print(len(octocat["V"]), "->", len(simplified["V"]), "vertices")
        ```
    
    === "C++"
    
        ```cpp
        wmtk::MeshMap meshes{{"octocat", wmtk::io::read_mesh(data / "Octocat.obj")}};
        auto [outputs, report] = wmtk::components::qslim::qslim(
            {{"input", "octocat"}, {"target_rel", 0.1}, {"eps_rel", 0.002}, {"num_threads", 4}},
            meshes);
        const wmtk::MeshData& simplified = outputs.at("mesh");
        std::cout << meshes.at("octocat").V.rows() << " -> " << simplified.V.rows() << " vertices\n";
        wmtk::io::write_mesh("octocat_qslim.msh", simplified);
        ```
    

    The tabs are linked across the site (content.tabs.link): a reader who picks Python sees Python everywhere. 3. Key settings: the options in the component's key_options, each with what it changes and when to change it, a link to its reference entry, and the numbers a reader needs (what the default does on the example, what a coarser or finer value gives). Measure them: run the example and say what came out. 4. Outputs: the files written, the fields on the mesh, what the report holds. 5. Recipes and pitfalls: what goes wrong in practice and what to do. 6. See also: the reference page, related tutorials, the paper.

Write in plain declarative sentences, present tense, without marketing. Numbers come from runs, not from memory: if a page says "about 1,000 faces", an example produced it.