π§© Module ReferenceΒΆ
Every class, method, and property exported by the frontend package.
Import surface:
from frontend import App, Scene, FixedSession, ParamManager, ...
Entry point for the frontend package.
Import App from this package to start the application; the other
names listed in __all__ expose the scene, session, mesh, plotting,
asset, and utility APIs.
- class frontend.App(name: str, renew: bool, cache_dir: str = '')[source]ΒΆ
Bases:
objectHigh-level entry point for the simulation frontend.
An
Appbundles together the mesh, asset, scene, and session managers used to build and run a simulation. It is the canonical starting point for notebooks and scripts, typically constructed viaApp.create()(new) orApp.load()(resume).Example
Build a trivial scene and run it to completion:
from frontend import App app = App.create("intro") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) scene = app.scene.create().add("sheet").build() session = app.session.create(scene).build() session.start(blocking=True)
- property asset: AssetManagerΒΆ
Get the asset manager.
- Returns:
The asset manager.
- Return type:
Example
Use
app.assetto register mesh data and list what exists:app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) print(app.asset.list())
- static busy() bool[source]ΒΆ
Return whether a simulation is currently running.
- Returns:
True if a simulation is running, False otherwise.
- Return type:
bool
Example
Guard against starting a second simulation while one is still in progress:
from frontend import App if App.busy(): print("solver is still running; skipping") else: print("ready to start a new session")
- property cache_dir: strΒΆ
Get the path to the cache directory.
- Returns:
The path to the cache directory.
- Return type:
str
Example
Inspect where downloaded preset meshes and other cached files are stored on disk:
app = App.create("demo") print(app.cache_dir)
- property ci: boolΒΆ
Check if the code is running in a CI environment.
- Returns:
True if the code is running in a CI environment, False otherwise.
- Return type:
bool
Example
Skip long-running steps when executing in CI:
app = App.create("demo") if not app.ci: session.start(blocking=True)
- property ci_dir: str | NoneΒΆ
Get the path to the CI directory, if running under CI.
- Returns:
The CI directory path, or None when not running in CI.
- Return type:
Optional[str]
Example
Resolve a path under the CI workspace only when the app is running inside CI:
app = App.create("demo") if app.ci_dir is not None: print("CI workspace:", app.ci_dir)
- clear() App[source]ΒΆ
Clear the application state and return a fresh App instance.
Example
Discard existing assets, scenes, and sessions before rebuilding a workflow from scratch:
from frontend import App app = App.create("drape") app = app.clear() print(app.asset.list())
- clear_cache() App[source]ΒΆ
Remove all files and subdirectories under the cache directory.
Example
Force downloaded meshes and other cached artifacts to be regenerated on the next run:
from frontend import App app = App.create("drape") app.clear_cache()
- static create(name: str, cache_dir: str = '') App[source]ΒΆ
Start a new application.
- Parameters:
name (str) β The name of the application.
cache_dir (str) β The directory used to store cached files. If empty, defaults to
~/.cache/ppf-cts(or a project-relativecache/ppf-ctson Windows).
- Returns:
A new instance of the App class.
- Return type:
Example
Register a mesh as an asset, drop it into a scene, and run a short simulation:
from frontend import App app = App.create("hello") V, F = app.mesh.square(res=64, ex=[1, 0, 0], ey=[0, 0, 1]) app.asset.add.tri("sheet", V, F) scene = app.scene.create() scene.add("sheet").at(0, 0.6, 0) scene = scene.build() session = app.session.create(scene) session.param.set("frames", 60).set("dt", 0.01) session = session.build() session.start(blocking=True) session.export.animation().zip()
- property extra: ExtraΒΆ
Get the extra manager.
- Returns:
The extra manager.
- Return type:
Example
Use
app.extrafor auxiliary helpers that do not belong to the core asset/scene/session flow:app = App.create("demo") helpers = app.extra
- static get_default_param() ParamManager[source]ΒΆ
Get default parameters for the application.
- Returns:
The default parameters.
- Return type:
Example
Inspect the default value of a solver parameter before building a session:
from frontend import App param = App.get_default_param() print(param.get("gravity"))
- static get_proj_root() str[source]ΒΆ
Find the root directory of the project.
- Returns:
Path to the root directory of the project (parent of frontend).
- Return type:
str
Example
Resolve paths relative to the project root (for example, the bundled
srcdirectory):import os from frontend import App src_dir = os.path.join(App.get_proj_root(), "src") print(os.path.isdir(src_dir))
- static is_fast_check() bool[source]ΒΆ
Check if fast check mode is enabled.
Fast check mode forces simulations to run for only 1 frame, enabling quick validation of all examples.
- Returns:
True if fast check mode is enabled.
- Return type:
bool
Example
Branch on fast-check mode to shorten a CI run:
from frontend import App frames = 1 if App.is_fast_check() else 200 print("frames:", frames)
- static load(name: str, cache_dir: str = '') App[source]ΒΆ
Load the saved state of the application if it exists.
If no saved state is found on disk, a fresh App is initialized instead.
- Parameters:
name (str) β The name of the application.
cache_dir (str) β The directory used to store cached files. If empty, defaults to
~/.cache/ppf-cts(or a project-relativecache/ppf-ctson Windows).
- Returns:
A new instance of the App class.
- Return type:
Example
Resume a named app, reusing the previously saved assets and scene when available:
from frontend import App app = App.load("drape") print(app.asset.list())
- property mesh: MeshManagerΒΆ
Get the mesh manager.
- Returns:
The mesh manager.
- Return type:
Example
Use
app.meshto build primitives or load presets:app = App.create("demo") V, F = app.mesh.square(res=64) V2, F2, T = app.mesh.preset("armadillo").tetrahedralize()
- property name: strΒΆ
Get the name of the application.
- Returns:
The name of the application.
- Return type:
str
Example
Read back the name assigned at construction time:
app = App.create("hello") assert app.name == "hello"
- property plot: PlotManagerΒΆ
Get the plot manager.
- Returns:
The plot manager.
- Return type:
Example
Use
app.plotto create interactive viewers from mesh data:app = App.create("demo") V, F = app.mesh.square(res=32) plot = app.plot.create().tri(V, F).build()
- static recover(name: str) FixedSession[source]ΒΆ
Recover the fixed session previously saved under
name.The session is located via a symlink in the data directory (or, on Windows, a
.txtfallback file holding the target path), and otherwise via a fallback{data_dir}/{name}/sessiondirectory.- Parameters:
name (str) β The name used to identify the session.
- Returns:
The recovered fixed session.
- Return type:
- Raises:
Exception β If no recoverable session can be found for
name.
Example
Resume a long-running session after restarting the kernel and inspect its current status:
from frontend import App session = App.recover("drape") print(session.finished())
- static run_tests() bool[source]ΒΆ
Run all frontend tests.
- Returns:
True if all tests pass, False otherwise.
- Return type:
bool
Example
Run the bundled frontend self-tests and exit non-zero on failure:
import sys from frontend import App if not App.run_tests(): sys.exit(1)
- save() App[source]ΒΆ
Save the application state to disk and return self.
Example
Persist the current assets and scene so a later call to
App.load()can resume them:from frontend import App app = App.create("drape") V, F = app.mesh.square(res=64) app.asset.add.tri("sheet", V, F) app.save()
- property scene: SceneManagerΒΆ
Get the scene manager.
- Returns:
The scene manager.
- Return type:
Example
Use
app.sceneto assemble a scene from registered assets:app = App.create("demo") scene = app.scene.create().add("sheet").at(0, 0.5, 0).build()
- property session: SessionManagerΒΆ
Get the session manager.
- Returns:
The session manager.
- Return type:
Example
Use
app.sessionto build and start a solver run from a finalized scene:app = App.create("demo") scene = app.scene.create().add("sheet").build() session = app.session.create(scene).build() session.start(blocking=True)
- static set_fast_check(enabled: bool = True)[source]ΒΆ
Set fast check mode.
When enabled, simulations are forced to run for only 1 frame, enabling quick validation of examples.
- Parameters:
enabled β Whether to enable fast check mode. Defaults to True.
Example
Enable fast-check mode before building a session to run a single-frame smoke test:
from frontend import App App.set_fast_check(True) assert App.is_fast_check()
- class frontend.AssetFetcher(manager: AssetManager)[source]ΒΆ
Bases:
objectFetcher used to retrieve mesh assets from an
AssetManager.Example
Access the fetcher via
App.asset.fetchto pull arrays for a registered mesh:from frontend import App app = App.create("demo") V, F = app.mesh.square(res=64) app.asset.add.tri("sheet", V, F) V2, F2 = app.asset.fetch.tri("sheet")
- get(name: str) dict[str, ndarray][source]ΒΆ
Return the raw arrays stored for an asset.
The keys present in the returned dictionary depend on the asset type:
"tri":V,F, and optionallyUV."tet":V,F,T."rod":V,E."stitch":Ind,W.
- Parameters:
name (str) β The name of the asset.
- Returns:
Mapping from array name to array.
- Return type:
dict[str, np.ndarray]
- Raises:
Exception β If no asset is registered under
name.
Example
Retrieve the raw arrays of a registered mesh as a dictionary and inspect their shapes:
from frontend import App app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) data = app.asset.fetch.get("sheet") print(data["V"].shape, data["F"].shape)
- get_type(name: str) str[source]ΒΆ
Return the type tag of a registered asset.
- Parameters:
name (str) β The name of the asset.
- Returns:
The type of the asset: one of
"tri","tet","rod", or"stitch".- Return type:
str
- Raises:
Exception β If no asset is registered under
name.
Example
Branch on the type tag before retrieving arrays of the correct shape:
from frontend import App app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) assert app.asset.fetch.get_type("sheet") == "tri"
- rod(name: str) tuple[ndarray, ndarray][source]ΒΆ
Return the vertex and edge arrays of a rod mesh asset.
- Parameters:
name (str) β The name of the asset.
- Returns:
The vertices (#x3) and the edges (#x2) of the rod.
- Return type:
tuple[np.ndarray, np.ndarray]
- Raises:
Exception β If no asset is registered under
name, or the asset exists but is not a rod mesh.
Example
Retrieve the vertex and edge arrays of a registered rod asset:
import numpy as np from frontend import App app = App.create("demo") V = np.linspace([0, 0, 0], [1, 0, 0], 10) E = np.array([[i, i + 1] for i in range(len(V) - 1)], dtype=np.uint32) app.asset.add.rod("strand", V, E) V, E = app.asset.fetch.rod("strand")
- stitch(name: str) tuple[ndarray, ndarray][source]ΒΆ
Return the index and weight arrays of a stitch asset.
- Parameters:
name (str) β The name of the asset.
- Returns:
The index array
Ind(#x4) and the weight arrayW(#x4) of the stitch, as uploaded viaAssetUploader.stitch().- Return type:
tuple[np.ndarray, np.ndarray]
- Raises:
Exception β If no asset is registered under
name, or the asset exists but is not a stitch.
Example
Fetch the index and weight arrays for a registered stitch asset:
from frontend import App app = App.create("demo") V, F, S = app.extra.load_CIPC_stitch_mesh("dress_stage.obj") app.asset.add.stitch("glue", S) Ind, W = app.asset.fetch.stitch("glue")
- tet(name: str) tuple[ndarray, ndarray, ndarray][source]ΒΆ
Return the arrays of a tetrahedral mesh asset.
- Parameters:
name (str) β The name of the asset.
- Returns:
The vertices (#x3), the surface triangle elements (#x3), and the tetrahedral elements (#x4) of the mesh.
- Return type:
tuple[np.ndarray, np.ndarray, np.ndarray]
- Raises:
Exception β If no asset is registered under
name, or the asset exists but is not a tetrahedral mesh.
Example
Fetch a tet asset and plot its volumetric connectivity:
from frontend import App app = App.create("demo") V, F, T = app.mesh.icosphere(r=0.25, subdiv_count=3).tetrahedralize() app.asset.add.tet("ball", V, F, T) V, F, T = app.asset.fetch.tet("ball") app.plot.create().tet(V, T)
- tri(name: str) tuple[ndarray, ndarray][source]ΒΆ
Return the vertex and face arrays of a triangle mesh asset.
- Parameters:
name (str) β The name of the asset.
- Returns:
The vertices (#x3) and the triangle elements (#x3) of the mesh.
- Return type:
tuple[np.ndarray, np.ndarray]
- Raises:
Exception β If no asset is registered under
name, or the asset exists but is not a triangle mesh.
Example
Plot a previously registered triangle asset after fetching its arrays:
from frontend import App app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) V, F = app.asset.fetch.tri("sheet") app.plot.create().tri(V, F)
- class frontend.AssetManager[source]ΒΆ
Bases:
objectRegistry for mesh data assets.
Holds uploaded meshes keyed by name and exposes an
AssetUploaderfor registering new assets and anAssetFetcherfor retrieving them.Example
Access the manager via
App.assetto register a mesh, list the currently known names, and fetch arrays back out:from frontend import App app = App.create("demo") V, F = app.mesh.square(res=64) app.asset.add.tri("sheet", V, F) print(app.asset.list()) V2, F2 = app.asset.fetch.tri("sheet")
- property add: AssetUploaderΒΆ
The uploader used to register new assets.
Example
Use
app.asset.addto register triangle, tetrahedral, or rod meshes by name:app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F)
- clear()[source]ΒΆ
Remove all assets from the manager.
Example
Reset the asset registry before reloading a different set of meshes:
from frontend import App app = App.create("demo") app.asset.clear() V, F, T = app.mesh.preset("armadillo").tetrahedralize() app.asset.add.tet("body", V, F, T)
- property fetch: AssetFetcherΒΆ
The fetcher used to retrieve registered assets.
Example
Use
app.asset.fetchto pull mesh arrays back out by name:app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) V2, F2 = app.asset.fetch.tri("sheet")
- list() list[str][source]ΒΆ
List the names of all assets currently registered.
- Returns:
The registered asset names.
- Return type:
list[str]
Example
Check whether an asset has already been uploaded before re-registering it:
from frontend import App app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) assert "sheet" in app.asset.list()
- property mesh: dict[str, tuple]ΒΆ
The underlying name-to-mesh-tuple dictionary.
Example
Peek at the raw registry to inspect what has been uploaded:
app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) print(list(app.asset.mesh.keys()))
- remove(name: str) bool[source]ΒΆ
Remove an asset from the manager.
- Parameters:
name (str) β The name of the asset to remove.
- Returns:
True if the asset existed and was removed, False if no asset with that name was registered.
- Return type:
bool
Example
Drop an asset by name and re-upload a fresh version:
from frontend import App app = App.create("demo") V, F = app.mesh.square(res=32) app.asset.add.tri("sheet", V, F) app.asset.remove("sheet") app.asset.add.tri("sheet", V, F)
- class frontend.AssetUploader(manager: AssetManager)[source]ΒΆ
Bases:
objectUploader used to register mesh assets with an
AssetManager.Example
Access the uploader via
App.asset.addand register triangle, tetrahedral, and rod assets:from frontend import App app = App.create("demo") V, F = app.mesh.square(res=64) app.asset.add.tri("sheet", V, F) V, F, T = app.mesh.icosphere(r=0.25, subdiv_count=4).tetrahedralize() app.asset.add.tet("ball", V, F, T)
- check_bounds(V: ndarray, E: ndarray)[source]ΒΆ
Check that every index in
Erefers to a valid row ofV.- Parameters:
V (np.ndarray) β Vertex array whose row count defines the upper bound.
E (np.ndarray) β Element/index array to validate.
- Raises:
Exception β If any index in
Eis greater than or equal toV.shape[0].
Example
Validate a hand-built mesh before uploading it:
import numpy as np from frontend import App app = App.create("demo") V = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]], dtype=float) F = np.array([[0, 1, 2]], dtype=np.uint32) app.asset.add.check_bounds(V, F)
- rod(name: str, V: ndarray, E: ndarray)[source]ΒΆ
Upload a rod mesh to the asset manager.
- Parameters:
name (str) β The name of the asset. Must not already exist.
V (np.ndarray) β The vertices (#x3) of the rod.
E (np.ndarray) β The edges (#x2) of the rod.
- Raises:
Exception β If
namealready exists or any index inEis out of bounds forV.
Example
Build a polyline from a numpy array of points and register it as a rod asset:
import numpy as np from frontend import App app = App.create("demo") V = np.linspace([0, 0, 0], [1, 0, 0], 20) E = np.array([[i, i + 1] for i in range(len(V) - 1)], dtype=np.uint32) app.asset.add.rod("strand", V, E)
- stitch(name: str, stitch: tuple[ndarray, ndarray])[source]ΒΆ
Upload a stitch asset to the asset manager.
- Parameters:
name (str) β The name of the asset. Must not already exist.
stitch (tuple[np.ndarray, np.ndarray]) β A pair
(Ind, W)whereIndis the index array (#x4) andWis the weight array (#x4) of the stitch.
- Raises:
Exception β If
IndorWdoes not have 4 columns, or ifnamealready exists.
Example
Load a CIPC-format stitch mesh and register its stitch information alongside the dress geometry:
from frontend import App app = App.create("demo") V, F, S = app.extra.load_CIPC_stitch_mesh("dress_stage.obj") app.asset.add.tri("dress", V, F) app.asset.add.stitch("glue", S)
- tet(name: str, V: ndarray, F: ndarray, T: ndarray)[source]ΒΆ
Upload a tetrahedral mesh to the asset manager.
- Parameters:
name (str) β The name of the asset. Must not already exist.
V (np.ndarray) β The vertices (#x3) of the mesh.
F (np.ndarray) β The surface triangle elements (#x3) of the mesh.
T (np.ndarray) β The tetrahedral elements (#x4) of the mesh.
- Raises:
Exception β If the column counts of
V,F, orTare wrong,namealready exists, or any index inForTis out of bounds forV.
Example
Tetrahedralize an icosphere and register it as a volumetric asset:
from frontend import App app = App.create("demo") V, F, T = app.mesh.icosphere(r=0.25, subdiv_count=4).tetrahedralize() app.asset.add.tet("sphere", V, F, T)
- tri(name: str, V: ndarray, F: ndarray)[source]ΒΆ
Upload a triangle mesh to the asset manager.
- Parameters:
name (str) β The name of the asset. Must not already exist.
V (np.ndarray) β The vertices (#x3 or #x5) of the mesh. If #x5, columns 0-2 are xyz coordinates and columns 3-4 are UV coordinates.
F (np.ndarray) β The triangle elements (#x3) of the mesh.
- Raises:
Exception β If
Vdoes not have 3 or 5 columns,Fdoes not have 3 columns,namealready exists, or any index inFis out of bounds forV.
Example
Register a square sheet and then an icosphere as triangle assets:
from frontend import App app = App.create("demo") V, F = app.mesh.square(res=128, ex=[1, 0, 0], ey=[0, 0, 1]) app.asset.add.tri("sheet", V, F) V, F = app.mesh.icosphere(r=0.5, subdiv_count=4) app.asset.add.tri("sphere", V, F)
- class frontend.BlenderApp(name: str, verbose: bool = False, progress_callback=None)[source]ΒΆ
Bases:
objectAttach to a project exported by the Blender addon and build a runnable session.
The addon writes
data.pickleandparam.pickleinto a per-project directory under~/.local/share/ppf-cts/git-<branch>/<name>/.open()is the canonical entry point: it decodes those pickles, populates the scene, applies parameters, and builds aFixedSessionaccessible viasceneandsession.Example
Canonical notebook cell (as generated by the Blender addon), attach to a transferred project and run it:
from frontend import BlenderApp app = BlenderApp.open("rod-example-3") app.scene.report() app.scene.preview() app.session.run() app.session.preview()
- make() BlenderApp[source]ΒΆ
Apply
param.pickleto the populated scene and build a runnable session.Applies per-object parameters, pin configuration, cross-stitch constraints, explicit merge pairs, and invisible colliders to the scene, then builds the fixed scene and session. The resulting state is persisted to
app_state.pickleon a best-effort basis.Example
Finish the build after
populate(), then run the session:from frontend import BlenderApp app = BlenderApp("my-project").populate().make() app.session.run() app.session.preview()
- classmethod open(name: str, verbose: bool = False, progress_callback=None) BlenderApp[source]ΒΆ
Open a Blender project and build its scene and session.
Behavior:
If the Blender addon has uploaded both
data.pickleandparam.pickleunder the project root,populate().make()is invoked to build a fresh scene and session in this process.Otherwise,
FileNotFoundErroris raised β the user must click βTransferβ in the Blender addon first.
After
open(),app.sceneandapp.sessionare the builtFixedScene/FixedSessionobjects, ready for report, preview, run, and stream.Example
Notebook cell generated by the Blender addon β attach to the transferred project and run it:
from frontend import BlenderApp app = BlenderApp.open("<project>") app.scene.preview() app.session.run() app.session.preview()
- populate() BlenderApp[source]ΒΆ
Populate the scene with objects decoded from
data.pickle.Also peeks at
param.picklefor per-group fTetWild overrides so that tetrahedralization can use them at build time; full parameter application is deferred tomake().Example
Two-stage construction (useful when you want to inspect the mutable scene before parameters are applied):
from frontend import BlenderApp app = BlenderApp("my-project") app.populate() app.make() app.session.run()
- property scene: FixedSceneΒΆ
The built fixed scene β use this for
.report(),.preview(), etc. The mutable pre-buildSceneremains accessible as_scenefor advanced callers.Example
Open a Blender-authored project and inspect the built scene:
from frontend import BlenderApp app = BlenderApp.open("rod-example-3") app.scene.report() app.scene.preview()
- property session: FixedSessionΒΆ
The built fixed session β use this for
.run(),.preview(),.stream(), etc.Example
Run a Blender-authored session and stream live output:
from frontend import BlenderApp app = BlenderApp.open("rod-example-3") app.session.run() app.session.preview() app.session.stream()
- class frontend.CppRustDocStringParser[source]ΒΆ
Bases:
objectParser for logging-related docstrings in the C++/Rust sources.
Example
Harvest the log-name table from the bundled C++/Rust sources so a sessionβs recorded log keys can be annotated:
import os from frontend import App, CppRustDocStringParser src_dir = os.path.join(App.get_proj_root(), "src") entries = CppRustDocStringParser.get_logging_docstrings(src_dir) print(sorted(entries)[:5])
- static get_logging_docstrings(root: str) dict[str, dict[str, str]][source]ΒΆ
Scan
rootfor logging docstrings in.cuand.rsfiles.Walks
rootrecursively (skippingargs.rs) and parses//comment blocks describingSimpleLog,logging.push, andlogging.markcall sites. Each block contributes aLabel: valuedictionary plus a free-formDescriptionand a derivedfilename.- Parameters:
root (str) β Directory to search.
- Returns:
Mapping from entry name (with underscores replaced by hyphens) to the parsed docstring fields, sorted by key.
- Return type:
dict[str, dict[str, str]]
Example
Look up the metadata associated with a specific log name under the bundled solver sources:
import os from frontend import App, CppRustDocStringParser src_dir = os.path.join(App.get_proj_root(), "src") entries = CppRustDocStringParser.get_logging_docstrings(src_dir) if "time-per-frame" in entries: print(entries["time-per-frame"].get("Description"))
- class frontend.CreateManager(cache_dir: str)[source]ΒΆ
Bases:
objectA manager that provides mesh creation functions.
This manager provides a set of functions to create various types of meshes, such as rods, triangles, and tetrahedra.
Example
Wrap raw numpy arrays into the mesh types expected by the solver:
V = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]]) E = np.array([[0, 1], [1, 2]]) rod = app.mesh.create.rod(V, E) pts = np.array([[0, 0], [1, 0], [1, 1], [0, 1]]) tri_mesh = app.mesh.create.tri(pts).triangulate(1024)
- rod(vert: ndarray, edge: ndarray) Rod[source]ΒΆ
Create a rod mesh.
- Parameters:
vert (np.ndarray) β array of vertex positions
edge (np.ndarray) β array of edges
- Returns:
a rod mesh, a pair of vertices and edges
- Return type:
Example
Build a custom rod from hand-specified vertices and edges:
V = np.array([[0, 0, 0], [0.5, 0.2, 0], [1, 0, 0]]) E = np.array([[0, 1], [1, 2]], dtype=np.uint32) rod = app.mesh.create.rod(V, E) app.asset.add.rod("strand", *rod)
- tet(vert: ndarray, elm: ndarray, tet: ndarray) TetMesh[source]ΒΆ
Create a tetrahedral mesh.
- Parameters:
vert (np.ndarray) β array of vertex positions
elm (np.ndarray) β array of surface triangle elements
tet (np.ndarray) β array of tetrahedral elements
- Returns:
a tetrahedral mesh containing vertices, surface triangles, and tetrahedra
- Return type:
Example
Wrap precomputed tet arrays into a TetMesh and register it:
V, F, T = app.mesh.tet_box(1, 1, 1) tet = app.mesh.create.tet(V, F, T) app.asset.add.tet("block", *tet)
- tri(vert: ndarray, elm: ndarray = None) TriMesh[source]ΒΆ
Create a triangle mesh.
When
elmisNoneor empty, a closed edge loop over all vertices is auto-generated instead of triangle faces. The resulting meshβs element array then stores edges rather than triangles.- Parameters:
vert (np.ndarray) β array of vertex positions
elm (np.ndarray) β array of elements (
Noneor empty to auto-generate an edge loop)
- Returns:
a triangle (or line-loop) mesh bound to the managerβs cache directory
- Return type:
Example
Make a closed 2D curve and triangulate it into a filled patch:
pts = np.array( [[np.cos(t), np.sin(t)] for t in np.linspace(0, 2 * np.pi, 64, endpoint=False)] ) V, F = app.mesh.create.tri(pts).triangulate(target=1024) app.plot.create().tri(V, F)
- class frontend.Extra[source]ΒΆ
Bases:
objectCollection of auxiliary helpers that do not belong to any manager.
Example
Access the helpers via
App.extrato sparse-clone an external dataset and load one of its stitch meshes:import os from frontend import App, get_cache_dir app = App.create("fitting") dest = os.path.join(get_cache_dir(), "Codim-IPC") app.extra.sparse_clone( "https://github.com/ipc-sim/Codim-IPC", dest, ["Projects/FEMShell/input/dress_knife"], ) stage = os.path.join(dest, "Projects/FEMShell/input/dress_knife/stage.obj") V, F, S = app.extra.load_CIPC_stitch_mesh(stage)
- load_CIPC_stitch_mesh(path: str) tuple[ndarray, ndarray, tuple[ndarray, ndarray]][source]ΒΆ
Load a stitch mesh in the format used by the CIPC paper repository.
- Parameters:
path (str) β Path to the stitch mesh file.
- Returns:
A tuple
(vertices, faces, (stitch_index, stitch_weight))where vertices have shape(#, 3), faces have shape(#, 3)with zero-based indices, and each stitch entry has shape(#, 4). The weight row[1.0, 1 - w, w, 0.0]encodes linear interpolation between the second and third stitch vertices.- Return type:
tuple[np.ndarray, np.ndarray, tuple[np.ndarray, np.ndarray]]
Example
Load a CIPC-format stitch mesh and register the pieces as triangle and stitch assets:
from frontend import App app = App.create("fitting") V, F, S = app.extra.load_CIPC_stitch_mesh("dress_stage.obj") app.asset.add.tri("dress", V, F) app.asset.add.stitch("glue", S)
- sparse_clone(url: str, dest: str, paths: list[str], delete_exist: bool = False)[source]ΒΆ
Fetch a git repository using sparse-checkout.
Clones
urlintodestif needed, then adds each entry inpathsto the sparse-checkout set and checks it out. Already present paths are left untouched.- Parameters:
url (str) β URL of the git repository.
dest (str) β Destination directory for the clone.
paths (list[str]) β Repository-relative paths to fetch.
delete_exist (bool) β If True, delete
destbefore cloning.
- Raises:
FileNotFoundError β If
gitcannot be found onPATH.
Example
Fetch only the FEMShell input subdirectories from the upstream CIPC repository into the ppf-cts cache:
import os from frontend import App, get_cache_dir app = App.create("fitting") dest = os.path.join(get_cache_dir(), "Codim-IPC") app.extra.sparse_clone( "https://github.com/ipc-sim/Codim-IPC", dest, ["Projects/FEMShell/input/dress_knife"], )
- class frontend.FixedScene(plot: PlotManager | None, name: str, map_by_name: dict[str, list[int]], displacement: ndarray, vert: tuple[ndarray, ndarray], color: ndarray, dyn_face_color: list[EnumColor], dyn_face_intensity: list[float], vel: ndarray, uv: list[ndarray], rod: ndarray, tri: ndarray, tet: ndarray, rod_param: dict[str, list[Any]], tri_param: dict[str, list[Any]], tet_param: dict[str, list[Any]], wall: list[Wall], sphere: list[Sphere], rod_vert_range: tuple[int, int], shell_vert_range: tuple[int, int], rod_count: int, shell_count: int, tri_is_collider: ndarray, rod_is_collider: ndarray, pinned_vertices: set[int] | None = None, static_vert_for_check: ndarray | None = None, static_tri_for_check: ndarray | None = None, surface_map_by_name: dict[str, tuple] | None = None, concat_rest_vert: ndarray | None = None, rest_vert_mask: ndarray | None = None)[source]ΒΆ
Bases:
objectA fixed scene class.
FixedSceneis the immutable, validated result ofScene.build(). Hand it toSessionManager.create()to drive a solver run.Example
Build a scene, inspect it, then pass it into a session:
scene = app.scene.create() scene.add("sheet").at(0, 0.6, 0) fixed = scene.build().report() fixed.preview() session = app.session.create(fixed).build()
- bbox() tuple[ndarray, ndarray][source]ΒΆ
Compute the bounding box of the scene.
- Returns:
The maximum and minimum coordinates of the bounding box.
- Return type:
tuple[np.ndarray, np.ndarray]
Example
Print the extents of the built scene:
hi, lo = fixed.bbox() print("size:", hi - lo)
- center() ndarray[source]ΒΆ
Compute the area-weighted center of the scene.
- Returns:
The area-weighted center of the scene.
- Return type:
np.ndarray
Example
Aim a camera at the sceneβs area-weighted center:
target = fixed.center() fixed.preview(options={"lookat": target.tolist()})
- color(vert: ndarray, hint: dict | None = None) ndarray[source]ΒΆ
Compute the per-vertex color for the scene given a vertex array.
- Parameters:
vert (np.ndarray) β The current vertex positions.
hint (dict, optional) β Optional hints for the color computation (e.g.
"max-area"). Defaults to None.
- Returns:
The per-vertex colors of the scene.
- Return type:
np.ndarray
Example
Compute colors for the current vertex positions before previewing:
fixed = scene.build() V = fixed.vertex() colors = fixed.color(V, hint={"max-area": 2.0}) fixed.preview(V)
- export(vert: ndarray, color: ndarray, path: str, include_static: bool = True, args: dict | None = None, delete_exist: bool = False) FixedScene[source]ΒΆ
Export the scene to a mesh file.
The vertices and vertex colors must be supplied explicitly so callers can pass time-evaluated positions.
- Parameters:
vert (np.ndarray) β The vertices of the scene.
color (np.ndarray) β The colors of the vertices.
path (str) β The path to the mesh file. Supported formats include
.plyand.obj.include_static (bool, optional) β Whether to include the static mesh. Defaults to True.
args (dict, optional) β Additional arguments passed to the renderer.
delete_exist (bool, optional) β Whether to delete any existing file at the path. Defaults to False.
- Returns:
The fixed scene.
- Return type:
Example
Write out the scene as a .ply at the initial time:
vert = fixed.vertex() color = fixed.color(vert) fixed.export(vert, color, "/tmp/scene.ply", delete_exist=True)
- export_fixed(path: str, delete_exist: bool) FixedScene[source]ΒΆ
Export the fixed scene into a set of data files that are read by the simulator.
- Parameters:
path (str) β The path to the output directory.
delete_exist (bool) β Whether to delete the existing directory.
- Returns:
The fixed scene.
- Return type:
Example
Typically invoked internally by
Session.build(), but can be called directly to materialize the solverβs data directory:fixed = scene.build() fixed.export_fixed("/tmp/my_scene_data", delete_exist=True)
- get_violation_messages() list[str][source]ΒΆ
Get a list of violation messages for the scene.
Example
Print any validation violations before running the solver:
for msg in fixed.get_violation_messages(): print(msg)
- property has_violations: boolΒΆ
Check if the scene has any violations that prevent simulation.
Example
Guard the solver launch behind a validation check:
fixed = scene.build() if fixed.has_violations: print(fixed.get_violation_messages())
- preview(vert: ndarray | None = None, options: dict | None = None, show_slider: bool = True, engine: str = 'threejs') Plot | None[source]ΒΆ
Preview the scene.
- Parameters:
vert (Optional[np.ndarray], optional) β The vertices to preview. Defaults to None, in which case
self.vertex()is used.options (dict, optional) β The options for the plot. Defaults to None.
show_slider (bool, optional) β Whether to show the time slider. Defaults to True.
engine (str, optional) β The rendering engine. Defaults to
"threejs".
- Returns:
The plot object if in a Jupyter notebook, otherwise None.
- Return type:
Optional[Plot]
Example
Preview the initial scene with a custom camera and no pin markers:
opts = {"eye": [0, 1.4, 2.5], "pin": False, "wireframe": True} fixed.preview(options=opts)
- report() FixedScene[source]ΒΆ
Print a summary of the scene.
- Returns:
The fixed scene (for chaining).
- Return type:
Example
Chain a summary printout into the build step:
fixed = scene.build().report() fixed.preview()
- set_pin(pin: list[PinData])[source]ΒΆ
Set the pinning data of all the objects.
- Parameters:
pin (list[PinData]) β A list of pinning data.
Example
Typically invoked internally by
Scene.build(), but can be called directly to inject pinning data into a fixed scene:fixed = scene.build() fixed.set_pin(list_of_pin_data)
- set_spin(spin: list[SpinData])[source]ΒΆ
Set the spinning data of all the objects.
- Parameters:
spin (list[SpinData]) β A list of spinning data.
Example
Typically invoked internally by
Scene.build(), but can be called directly to inject spin data:fixed = scene.build() fixed.set_spin(list_of_spin_data)
- set_static(vert: tuple[ndarray, ndarray], tri: ndarray, color: ndarray, param: dict[str, list[Any]], transform_animations: list[tuple[int, TransformAnimation]] | None = None)[source]ΒΆ
Set the static mesh data.
- Parameters:
vert (tuple[np.ndarray, np.ndarray]) β The vertices of the static mesh. The first array is the displacement map reference; the second is local positions.
tri (np.ndarray) β The triangle elements of the static mesh.
color (np.ndarray) β The colors of the static mesh.
param (dict[str, list[Any]]) β Parameters for the static mesh elements.
transform_animations β Optional list of
(vert_offset, TransformAnimation)for animated static objects.
Example
Typically invoked internally by
Scene.build(), but can be called directly to attach a static collider mesh:fixed = scene.build() fixed.set_static((ref, V), F, colors, {"area": areas})
- set_stitch(ind: ndarray, w: ndarray)[source]ΒΆ
Set the stitch data.
- Parameters:
ind (np.ndarray) β The stitch indices.
w (np.ndarray) β The stitch weights.
Example
Typically invoked internally by
Scene.build(), but can be called directly to inject stitch constraints:fixed = scene.build() fixed.set_stitch(stitch_indices, stitch_weights)
- static_time(time: float) ndarray[source]ΒΆ
Compute animated static vertex positions at a specific time.
- Parameters:
time (float) β The time to evaluate.
- Returns:
The static vertex positions at the specified time.
- Return type:
np.ndarray
Example
Sample the animated collider mesh halfway through the animation:
fixed = scene.build() V_static = fixed.static_time(0.5) print(V_static.shape)
- time(time: float) ndarray[source]ΒΆ
Compute the vertex positions at a specific time.
- Parameters:
time (float) β The time to compute the vertex positions.
- Returns:
The vertex positions at the specified time.
- Return type:
np.ndarray
Example
Evaluate the scene midway through a pin animation:
vert_mid = fixed.time(0.5) print(vert_mid.shape)
- property tri_param: dict[str, list[Any]]ΒΆ
Get the triangle parameters.
Example
Inspect per-triangle parameter arrays captured at build time:
fixed = scene.build() for key, values in fixed.tri_param.items(): print(key, len(values))
- vertex(transform: bool = True) ndarray[source]ΒΆ
Get the vertices of the scene.
- Parameters:
transform (bool, optional) β Whether to transform the vertices. Defaults to True.
- Returns:
The vertices of the scene.
- Return type:
np.ndarray
Example
Fetch the initial world-space vertex positions:
vert = fixed.vertex() print(vert.shape)
- class frontend.FixedSession(session: Session)[source]ΒΆ
Bases:
objectClass to manage a fixed simulation session.
Returned by
Session.build(). Use it to launch the solver, monitor it, pull results, and export.Example
Build a session from a configured
Sessionand run it to completion, then export:fixed_session = session.build() fixed_session.start(blocking=True) fixed_session.export.animation().zip()
- animate(options: dict | None = None, engine: str = 'threejs') FixedSession[source]ΒΆ
Show the animation inside a Jupyter notebook.
Outside Jupyter this is a no-op. Loads all available frames from disk and exposes a slider plus a reload button to pull in frames produced after the initial load.
- Parameters:
options (dict, optional) β The render options.
engine (str, optional) β The rendering engine. Defaults to
"threejs".
- Returns:
This session.
- Return type:
Example
Replay frames once the run has finished (or has enough frames on disk):
session.animate()
- delete()[source]ΒΆ
Delete the session.
Example
Remove the on-disk session directory before a clean rerun:
session.delete()
- property export: SessionExportΒΆ
Get the session export object.
Example
Export the session shell command for reproducible replays:
session = app.session.create(fixed_scene).build() cmd_path = session.export.shell_command(session.session.param)
- finished() bool[source]ΒΆ
Check if the session has finished.
Any stderr lines present are printed as a side effect.
- Returns:
Trueif afinished.txtmarker exists in the output directory,Falseotherwise.- Return type:
bool
Example
Assert the run completed cleanly in CI:
if app.ci: assert session.finished()
- property fixed_scene: FixedScene | NoneΒΆ
Get the fixed scene.
Example
Retrieve the bound scene from a fixed session:
fixed = app.session.create(scene).build() scene_ref = fixed.fixed_scene
- property get: SessionGetΒΆ
Get the session get object.
Example
Retrieve solver-emitted log channel names:
session = session.build().start(blocking=True) channels = session.get.log.names()
- property info: SessionInfoΒΆ
Get the session information.
Example
Read the session name and directory path:
session = app.session.create(fixed_scene).build() print(session.info.name, session.info.path)
- initialize_finished() bool[source]ΒΆ
Check if the session initialization has finished.
Any stderr lines present are printed as a side effect.
- Returns:
Trueif aninitialize_finish.txtmarker exists in the output directory,Falseotherwise.- Return type:
bool
Example
Wait for solver initialization to complete before continuing:
while not session.initialize_finished(): time.sleep(1)
- is_running() bool[source]ΒΆ
Check if the solver process is running.
This method first checks the stored process handle (most reliable), then falls back to Utils.busy() for broader detection.
- Returns:
True if the solver is running, False otherwise.
- Return type:
bool
Example
Poll the solver state after launching non-blocking:
session.start() while session.is_running(): time.sleep(1)
- property output: SessionOutputΒΆ
Get the session output object.
Example
Locate the solver output directory:
session = app.session.create(fixed_scene).build() print(session.output.path)
- preview(options: dict | None = None, live_update: bool = True, engine: str = 'threejs') Plot | None[source]ΒΆ
Live-view the session inside a Jupyter notebook.
Outside Jupyter this is a no-op and returns
None.- Parameters:
options (dict, optional) β The render options.
live_update (bool, optional) β Whether to enable live updates. Defaults to
True.engine (str, optional) β The rendering engine. Defaults to
"threejs".
- Returns:
The plot object, or
Nonewhen not running inside a Jupyter notebook.- Return type:
Optional[Plot]
Example
Kick off a run and watch it in-notebook while tailing stdout:
session.start().preview() # live frame playback session.stream() # tail solver stdout
- print(message)[source]ΒΆ
Print a message.
- Parameters:
message (str) β The message to print.
Example
Emit a status line that renders nicely in Jupyter or stdout:
session.print("Launching solver...")
- resume(frame: int = -1, force: bool = True, blocking: bool | None = None) FixedSession[source]ΒΆ
Resume the solver from a saved state.
- Parameters:
- Returns:
This session.
- Return type:
Example
Pick up where a previous run left off:
session.resume() # latest saved frame session.resume(frame=120) # specific saved frame
- run(blocking: bool | None = None) FixedSession[source]ΒΆ
Idempotent launcher: ensure the solver is running.
If the solver is already running (this process or another host process detected via
Utils.busy()), return without relaunching.Otherwise, start a fresh simulation from frame 0.
To pick up a previously-saved state, use
resume()explicitly, sincerun()never auto-resumes.- Parameters:
blocking (Optional[bool]) β If
True, block until the solver finishes. IfNone, defaults to blocking outside Jupyter and non-blocking inside Jupyter.- Returns:
This session.
- Return type:
Example
Ensure the solver is running without restarting it if it already is:
session.run(blocking=True)
- running() bool[source]ΒΆ
Alias for
is_running().Example
Shorthand form for polling solver state:
session.start() while session.running(): time.sleep(1)
- save_and_quit()[source]ΒΆ
Save the session and quit the solver.
Example
Ask a running solver to checkpoint and exit gracefully:
session.save_and_quit() while session.is_running(): time.sleep(1)
- save_and_quit_file_path() str[source]ΒΆ
Get the flag-file path that signals the solver to save and quit.
If this file exists, the solver will save the session and quit. After the session is saved, the file is removed.
Example
Check for the save-and-quit sentinel file:
path = session.save_and_quit_file_path() print(os.path.exists(path))
- property session: SessionΒΆ
Get the session object.
Example
Reach back to the parent
Sessionfor its parameters:fixed = app.session.create(fixed_scene).build() params = fixed.session.param
- start(force: bool = False, blocking: bool | None = None, load: int = 0) FixedSession[source]ΒΆ
Start the session.
Inside a Jupyter notebook the function returns immediately by default and the solver runs in the background; outside Jupyter it blocks until the solver finishes. Pass
blockingexplicitly to override this behavior.If saved states exist and
forceisFalse, this delegates toresume()from the latest saved frame.- Parameters:
force (bool, optional) β If
True, terminate any running solver and skip the auto-resume branch. Defaults toFalse.blocking (Optional[bool], optional) β Whether to block until the solver finishes. Defaults to
None(auto-detect based on Jupyter).load (int, optional) β The frame number to load from saved states. Defaults to
0(start fresh).
- Returns:
The started session.
- Return type:
Example
Launch the solver and return immediately for notebook-style monitoring:
session.start().preview() session.stream()
Or block until finished when running as a script:
session.start(blocking=True)
- stream(n_lines=40) FixedSession[source]ΒΆ
Stream the tail of the session stdout log inside a Jupyter notebook.
Outside Jupyter this is a no-op.
- Parameters:
n_lines (int, optional) β The number of trailing lines to display. Defaults to
40.- Returns:
This session.
- Return type:
Example
Kick off a run and watch both the live preview and stdout tail in a notebook cell:
session.start().preview() session.stream()
- update_options(options: dict) dict[source]ΒΆ
Return a copy of
optionswith missing defaults filled in.- Parameters:
options (dict) β User-supplied render options.
- Returns:
A new dictionary combining
optionswith the sessionβs default option values.- Return type:
dict
Example
Add the sessionβs default render flags to a user-provided options dict:
opts = session.update_options({"flat_shading": True})
- class frontend.InvisibleAdder(scene: Scene)[source]ΒΆ
Bases:
objectHelper for attaching invisible colliders (walls and spheres) to a scene.
Obtained via
scene.add.invisible.Example
Add a ground wall and a ball collider together:
scene.add.invisible.wall([0, 0, 0], [0, 1, 0]) scene.add.invisible.sphere([0, 0.5, 0], 0.25)
- sphere(position: list[float], radius: float) Sphere[source]ΒΆ
Add an invisible sphere to the scene.
- Parameters:
position (list[float]) β The position of the sphere.
radius (float) β The radius of the sphere.
- Returns:
The invisible sphere.
- Return type:
Example
Place an inverted hemispherical bowl under the cloth:
scene.add.invisible.sphere([0, 1, 0], 1.0).invert().hemisphere()
- wall(position: list[float], normal: list[float]) Wall[source]ΒΆ
Add an invisible wall to the scene.
- Parameters:
position (list[float]) β The position of the wall.
normal (list[float]) β The outer normal of the wall.
- Returns:
The invisible wall.
- Return type:
Example
Seal a simulation box with four side walls:
scene.add.invisible.wall([1, 0, 0], [-1, 0, 0]) scene.add.invisible.wall([-1, 0, 0], [1, 0, 0]) scene.add.invisible.wall([0, 0, 1], [0, 0, -1]) scene.add.invisible.wall([0, 0, -1], [0, 0, 1])
- class frontend.MeshManager(cache_dir: str)[source]ΒΆ
Bases:
objectMesh manager for accessing mesh creation functions.
Example
Reach into the mesh manager via the top-level app object:
app = App.create("demo") V, F = app.mesh.square(res=64) V, F = app.mesh.icosphere(r=0.5, subdiv_count=4) V, E = app.mesh.line([0, 0, 0], [1, 0, 0], n=32)
- box(width: float = 1, height: float = 1, depth: float = 1) TriMesh[source]ΒΆ
Create a box mesh.
- Parameters:
width (float) β the width of the box
height (float) β the height of the box
depth (float) β the depth of the box
- Returns:
a box mesh, a pair of vertices and triangles
- Return type:
Example
Build a unit box and subdivide it once:
V, F = app.mesh.box(width=1, height=1, depth=1).subdivide(n=1) app.asset.add.tri("box", V, F)
- circle(n: int = 32, r: float = 1, ntri: int = 1024) TriMesh[source]ΒΆ
Create a circle mesh.
- Parameters:
n (int) β number of boundary segments
r (float) β radius of the circle
ntri (int) β approximate number of triangles filling the circle
- Returns:
a circle mesh, a pair of 2D vertices and triangles
- Return type:
Example
Produce a circular patch ready for plotting:
V, F = app.mesh.circle(n=64, r=1, ntri=2048) app.plot.create().tri(V, F)
- cone(Nr: int = 16, Ny: int = 16, Nb: int = 4, radius: float = 0.5, height: float = 2, sharpen: float = 1.0) TriMesh[source]ΒΆ
Create a cone mesh with the given radial, vertical, and bottom resolution, radius, and height.
- Parameters:
Nr (int) β radial resolution
Ny (int) β vertical resolution
Nb (int) β bottom resolution
radius (float) β radius of the cone
height (float) β height of the cone
sharpen (float) β exponent applied to the radial parameter, sharpening the tip
- Returns:
a cone mesh, a pair of vertices and triangles
- Return type:
Example
Generate a pointed cone and register it:
V, F = app.mesh.cone(radius=0.5, height=2, sharpen=1.5) app.asset.add.tri("cone", V, F)
- property create: CreateManagerΒΆ
Get the mesh creation manager.
Example
Access the creation manager to build a parametric mesh:
V, F = app.mesh.create.square(res=64) app.asset.add.tri("sheet", V, F)
- cylinder(r: float, min_x: float, max_x: float, n: int)[source]ΒΆ
Create a cylinder along the x-axis.
- Parameters:
r (float) β radius of the cylinder
min_x (float) β minimum x coordinate
max_x (float) β maximum x coordinate
n (int) β number of divisions along the x-axis
- Returns:
(V, F)where:V: ndarray of shape (N, 3) containing vertex positionsF: ndarray of shape (M, 3) containing triangle indices
- Return type:
tuple
Example
Create a cylinder and plot it directly:
V, F = app.mesh.cylinder(r=0.5, min_x=-1, max_x=1, n=32) app.plot.create().tri(V, F)
- export(V: ndarray, F: ndarray, path: str)[source]ΒΆ
Export a mesh given by vertices
Vand facesFto a file.Example
Save a generated square as an OBJ on disk:
V, F = app.mesh.square(res=32) app.mesh.export(V, F, "sheet.obj")
- icosphere(r: float = 1, subdiv_count: int = 3) TriMesh[source]ΒΆ
Create an icosphere mesh with the given radius and subdivision count.
- Parameters:
r (float) β radius of the icosphere
subdiv_count (int) β number of subdivision iterations applied to the base icosahedron
- Returns:
an icosphere mesh, a pair of vertices and triangles
- Return type:
Example
Create a smooth sphere asset and pin it as a static collider:
V, F = app.mesh.icosphere(r=0.5, subdiv_count=4) app.asset.add.tri("sphere", V, F)
- line(_p0: list[float], _p1: list[float], n: int) Rod[source]ΒΆ
Create a line mesh with the given start and end points and resolution.
- Parameters:
_p0 (list[float]) β the start point of the line
_p1 (list[float]) β the end point of the line
n (int) β the number of segments; the line has
n + 1vertices
- Returns:
a line mesh, a pair of vertices and edges
- Return type:
Example
Create a tall vertical rod and register it as an asset:
V, E = app.mesh.line([0, 0.01, 0], [0.01, 15, 0], 960) app.asset.add.rod("strand", V, E)
- load_tri(path: str) TriMesh[source]ΒΆ
Load a triangle mesh from a file.
- Parameters:
path (str) β path to the mesh file
- Returns:
a triangle mesh, a pair of vertices and triangles
- Return type:
Example
Load a ribbon mesh from disk and register it as a tri asset:
V, F = app.mesh.load_tri("fishingknot.ply") app.asset.add.tri("ribbon", V, F)
- make_cache_dir()[source]ΒΆ
Create the cache directory if it does not already exist.
Example
Ensure the mesh cache directory is present before loading presets or importing files:
app.mesh.make_cache_dir() V, F = app.mesh.preset("armadillo")
- mobius(length_split: int = 70, width_split: int = 15, twists: int = 1, r: float = 1, flatness: float = 1, width: float = 1, scale: float = 1) TriMesh[source]ΒΆ
Create a Mobius strip mesh with the given length split, width split, twists, radius, flatness, width, and scale.
- Parameters:
length_split (int) β number of segments along the length of the strip
width_split (int) β number of segments across the width of the strip
twists (int) β number of half-twists in the strip
r (float) β radius of the strip (distance from center to middle of strip)
flatness (float) β controls the z-extent of the strip
width (float) β width of the strip
scale (float) β overall scale factor applied to the mesh
- Returns:
a Mobius strip mesh, a pair of vertices and triangles
- Return type:
Example
Create a Mobius strip and plot it:
V, F = app.mesh.mobius(length_split=120, width_split=20, twists=1) app.plot.create().tri(V, F)
- preset(name: str) TriMesh[source]ΒΆ
Load a preset mesh, downloading it from a remote source on first use and caching it locally.
- Parameters:
name (str) β the name of the preset mesh. Available names are
armadillo,knot, andbunny.- Returns:
a preset mesh, a pair of vertices and triangles
- Return type:
- Raises:
ValueError β if
nameis not a recognized preset.Exception β if the mesh cannot be downloaded after multiple retries.
Example
Load the armadillo, decimate it, then tetrahedralize and normalize:
V, F, T = ( app.mesh.preset("armadillo") .decimate(19000) .tetrahedralize() .normalize() ) app.plot.create().tet(V, T)
- rectangle(res_x: int = 32, width: float = 2, height: float = 1, ex: list[float] | None = None, ey: list[float] | None = None, gen_uv: bool = True) TriMesh[source]ΒΆ
Create a rectangle mesh with the given resolution, width, and height, spanned by the vectors
exandey.- Parameters:
res_x (int) β resolution along the
exaxiswidth (float) β the width of the rectangle
height (float) β the height of the rectangle
ex (list[float] | None) β a 3D vector to span the rectangle. Defaults to
[1, 0, 0].ey (list[float] | None) β a 3D vector to span the rectangle. Defaults to
[0, 1, 0].gen_uv (bool) β if True, include UV coordinates in vertices (Nx5), otherwise Nx3
- Returns:
a rectangle mesh, a pair of vertices (Nx5: x,y,z,u,v or Nx3: x,y,z) and triangles
- Return type:
Example
Create a tall thin ribbon lying in the xz plane:
V, F = app.mesh.rectangle( res_x=4, width=0.15, height=12.0, ex=[1, 0, 0], ey=[0, 0, 1], ) app.asset.add.tri("ribbon", V, F)
- square(res: int = 32, size: float = 2, ex: list[float] | None = None, ey: list[float] | None = None, gen_uv: bool = True) TriMesh[source]ΒΆ
Create a square mesh with the given resolution and size, spanned by the vectors
exandey.- Parameters:
res (int) β resolution of the mesh
size (float) β the side length of the square
ex (list[float] | None) β a 3D vector to span the square. Defaults to
[1, 0, 0].ey (list[float] | None) β a 3D vector to span the square. Defaults to
[0, 1, 0].gen_uv (bool) β if True, include UV coordinates in vertices (Nx5), otherwise Nx3
- Returns:
a square mesh, a pair of vertices and triangles
- Return type:
Example
Build a 128-resolution sheet in the xz plane and register it:
V, F = app.mesh.square(res=128, ex=[1, 0, 0], ey=[0, 0, 1]) app.asset.add.tri("sheet", V, F)
- tet_box(width: float = 1, height: float = 1, depth: float = 1) TetMesh[source]ΒΆ
Create a box tetrahedral mesh directly without subdivision.
Unlike
box().tetrahedralize(), this creates a minimal tetrahedral mesh with only 8 vertices and 5 tetrahedra, preserving the original box shape without any surface subdivision.- Parameters:
width (float) β width of the box (x-axis)
height (float) β height of the box (y-axis)
depth (float) β depth of the box (z-axis)
- Returns:
a tetrahedral box mesh
- Return type:
Example
Register a minimal tet box (8 verts, 5 tets) as a tet asset:
V, F, T = app.mesh.tet_box(width=1, height=1, depth=1) app.asset.add.tet("block", V, F, T)
- torus(r: float = 1, R: float = 0.25, n: int = 32) TriMesh[source]ΒΆ
Create a torus mesh with the given major and minor radii and resolution.
- Parameters:
r (float) β major radius of the torus (passed as
major_radius)R (float) β minor radius of the torus (passed as
minor_radius)n (int) β number of sections used for both the major and minor loops
- Returns:
a torus mesh, a pair of vertices and triangles
- Return type:
Example
Create a torus to use as a fixed obstacle:
V, F = app.mesh.torus(r=0.5, R=0.125) app.asset.add.tri("torus", V, F)
- class frontend.Object(asset: AssetManager, name: str)[source]ΒΆ
Bases:
objectThe object class.
An
Objectis a placed instance of a registered asset. It carries a 4x4 transform, per-vertex colors, pins, stitches, and material parameters. Instances are created viascene.add("<mesh_name>")and are typically configured through chainable methods.Example
Chain placement, material, and pinning onto a cloth sheet:
sheet = scene.add("sheet").at(0, 0.6, 0).jitter() sheet.param.set("strain-limit", 0.05) sheet.pin(sheet.grab([-1, 0, -1]) + sheet.grab([1, 0, -1]))
- animate_rotate(axis, angle: float, center=None, t_start: float = 0.0, t_end: float = 1.0) Object[source]ΒΆ
Animate the static object by rotating around an axis over time.
- Parameters:
axis β [ax, ay, az] rotation axis (will be normalized).
angle (float) β Rotation angle in degrees.
center β Center of rotation [cx, cy, cz]. Defaults to object centroid.
t_start (float) β Start time in seconds.
t_end (float) β End time in seconds.
- Returns:
The object with the animation added.
- Return type:
Example
Rotate a pinned static collider 180 degrees around y over 2 seconds:
roller = scene.add("cylinder").at(0, 0.5, 0).pin() scene.select("cylinder").animate_rotate( [0, 1, 0], 180.0, t_start=0.0, t_end=2.0, )
- apply_transform(x: ndarray, translate: bool) ndarray[source]ΒΆ
Apply the objectβs transformation to a set of vertices.
- Parameters:
x (np.ndarray) β The vertices to transform (N, 3).
translate (bool) β Whether to include the translation component.
- Returns:
The transformed vertices (N, 3).
- Return type:
np.ndarray
Example
Manually map a batch of points through the objectβs current transform:
obj = scene.add("sheet").at(0, 0.6, 0) world_pts = obj.apply_transform(obj.get("V"), True)
- at(x: float, y: float, z: float) Object[source]ΒΆ
Set the translation component of the transform.
- Parameters:
x (float) β The x-coordinate.
y (float) β The y-coordinate.
z (float) β The z-coordinate.
- Returns:
The object with the updated position.
- Return type:
Example
Drop a sheet 0.6 units above the origin:
scene.add("sheet").at(0, 0.6, 0)
- bbox() tuple[ndarray, ndarray][source]ΒΆ
Compute the bounding box of the object.
- Returns:
The dimensions and center of the bounding box.
- Return type:
tuple[np.ndarray, np.ndarray]
Example
Read a sheetβs size and center in world space:
sheet = scene.add("sheet") size, center = sheet.bbox() print(size, center)
- clear()[source]ΒΆ
Clear the object data.
Example
Reset an objectβs transform and pin state:
obj = scene.select("sheet") obj.clear() obj.at(0, 0.6, 0)
- collision_windows(windows: list) Object[source]ΒΆ
Set collision active time windows: list of (t_start, t_end) pairs.
Example
Enable contact only during t in [0.2, 1.0] and [2.0, 3.0]:
scene.add("sheet").collision_windows([(0.2, 1.0), (2.0, 3.0)])
- color(red: float, green: float, blue: float) Object[source]ΒΆ
Set the color of the object.
- Parameters:
red (float) β The red component.
green (float) β The green component.
blue (float) β The blue component.
- Returns:
The object with the updated color.
- Return type:
Example
Color a falling armadillo light gray:
scene.add("armadillo").color(0.75, 0.75, 0.75)
- cylinder_color(center: list[float], direction: list[float], up: list[float]) Object[source]ΒΆ
Set the color along the cylinder direction.
- Parameters:
center (list[float]) β The center of the cylinder.
direction (list[float]) β The direction of the cylinder.
up (list[float]) β The up vector of the cylinder.
- Returns:
The object with the updated color.
- Return type:
Example
Apply a cylinder gradient used in the twist demo:
obj = scene.add("cylinder").cylinder_color( [0, 0, 0], [1, 0, 0], [0, 1, 0], )
- default_color(red: float, green: float, blue: float) Object[source]ΒΆ
Set the default color of the object.
- Parameters:
red (float) β The red component.
green (float) β The green component.
blue (float) β The blue component.
- Returns:
The object with the updated default color.
- Return type:
Example
Override the auto-assigned hue for a dynamic object:
scene.add("sheet").default_color(1.0, 0.85, 0.0)
- direction(_ex: list[float], _ey: list[float]) Object[source]ΒΆ
Set two orthogonal directions of a shell required for Baraff-Witkin model.
- Parameters:
_ex (list[float]) β The 3D x-direction vector.
_ey (list[float]) β The 3D y-direction vector.
- Returns:
The object with the updated direction.
- Return type:
Example
Pin the warp and weft directions of a flat sheet in the xz plane:
sheet = scene.add("sheet") sheet.param.set("model", "baraff-witkin") sheet.direction([1, 0, 0], [0, 0, 1])
- direction_color(x: float, y: float, z: float) Object[source]ΒΆ
Set the color along the direction of the object.
- Parameters:
x (float) β The x-component of the direction.
y (float) β The y-component of the direction.
z (float) β The z-component of the direction.
- Returns:
The object with the updated color.
- Return type:
Example
Shade a cylinder along its long axis:
scene.add("cylinder").direction_color(1, 0, 0)
- dyn_color(color: str, intensity: float = 0.75) Object[source]ΒΆ
Set the dynamic color of the object.
- Parameters:
color (str) β The dynamic color type. Currently only
"area"is supported.intensity (float, optional) β Blend intensity of the dynamic color. Defaults to 0.75.
- Returns:
The object with the updated dynamic color.
- Return type:
Example
Highlight stretched triangles on a trampoline sheet:
scene.add("sheet").dyn_color("area", 1.0)
- property dynamic_color: EnumColorΒΆ
Get the dynamic color type.
Example
Inspect the dynamic color mode previously selected:
obj = scene.add("sheet") print(obj.dynamic_color)
- property dynamic_intensity: floatΒΆ
Get the dynamic color intensity.
Example
Read the scalar intensity used by dynamic coloring:
obj = scene.add("sheet") print(obj.dynamic_intensity)
- get(key: str) ndarray | None[source]ΒΆ
Get an associated value of the object with respect to the key.
- Parameters:
key (str) β The key of the value.
- Returns:
The value associated with the key.
- Return type:
Optional[np.ndarray]
Example
Fetch the rest-pose vertices and face list of an asset:
sheet = scene.add("sheet") V = sheet.get("V") F = sheet.get("F")
- grab(direction: list[float], eps: float = 0.001) list[int][source]ΒΆ
Select vertices that are furthest along a specified direction.
- Parameters:
direction (list[float]) β The direction vector.
eps (float, optional) β Tolerance (in dot-product units) from the maximum. Defaults to 1e-3.
- Returns:
The indices of the selected vertices.
- Return type:
list[int]
Example
Pin the two top corners of a sheet to hang it:
sheet = scene.add("sheet") sheet.pin(sheet.grab([-1, 1, 0]) + sheet.grab([1, 1, 0]))
- jitter(r: float = 0.01) Object[source]ΒΆ
Add random jitter to the translation.
- Parameters:
r (float, optional) β The jitter magnitude.
- Returns:
The object with the jittered position.
- Return type:
Example
Break symmetry for a falling armadillo on a trampoline:
scene.add("armadillo").at(0, 1, 0).jitter().velocity(0, -5, 0)
- mat4x4(matrix: ndarray) Object[source]ΒΆ
Set the full 4x4 transformation matrix directly.
Replaces the current transform entirely. The matrix is applied as:
world_pos = matrix[:3,:3] @ local_pos + matrix[:3,3]- Parameters:
matrix (np.ndarray) β A 4x4 transformation matrix.
- Returns:
The object with the updated transform.
- Return type:
Example
Apply a pre-computed transform imported from Blender:
import numpy as np M = np.eye(4) M[:3, 3] = [0, 0.6, 0] scene.add("sheet").mat4x4(M)
- max(dim: str) float[source]ΒΆ
Get the maximum coordinate value along a specified dimension.
- Parameters:
dim (str) β The dimension to get the maximum value along, either βxβ, βyβ, or βzβ.
- Returns:
The maximum coordinate value.
- Return type:
float
Example
Check that a sheet sits above y=0:
sheet = scene.add("sheet").at(0, 0.6, 0) assert sheet.min("y") >= 0 print(sheet.max("y"))
- min(dim: str) float[source]ΒΆ
Get the minimum coordinate value along a specified dimension.
- Parameters:
dim (str) β The dimension to get the minimum value along, either βxβ, βyβ, or βzβ.
- Returns:
The minimum coordinate value.
- Return type:
float
Example
Lift an object so its lowest point sits at y=0:
obj = scene.add("armadillo") obj.at(0, -obj.min("y"), 0)
- move(delta, t_start: float = 0.0, t_end: float = 1.0) Object[source]ΒΆ
Animate the static object by a translational delta over time.
- Parameters:
delta β [dx, dy, dz] translation delta in world space.
t_start (float) β Start time in seconds.
t_end (float) β End time in seconds.
- Returns:
The object with the animation added.
- Return type:
Example
Slide a static (fully pinned) collider along +x between t=0 and t=2:
scene.add("sphere").at(-1, 0, 0).pin() scene.select("sphere").move([2, 0, 0], t_start=0.0, t_end=2.0)
- property name: strΒΆ
Get name of the object.
Example
Read the asset reference name back from an object:
obj = scene.add("sheet") assert obj.name == "sheet"
- normalize() Object[source]ΒΆ
Normalize the object so that it fits within a unit cube.
- Returns:
The normalized object.
- Return type:
Example
Normalize a tetrahedral asset before scaling it into place:
arm = scene.add("armadillo").normalize().scale(0.75) arm.at(0, 1, 0)
- property obj_type: strΒΆ
Get the type of the object.
- Returns:
The type of the object, either βrodβ, βtriβ, or βtetβ.
- Return type:
str
Example
Branch on object topology when iterating the scene:
for obj in scene.object_dict.values(): if obj.obj_type == "tet": obj.param.set("young-mod", 1e6)
- property object_color: list[float] | NoneΒΆ
Get the object color.
Example
Read the RGB color previously assigned via
color():obj = scene.add("sheet").color(0.9, 0.3, 0.3) print(obj.object_color)
- property object_velocity: list[float] | ndarrayΒΆ
Get the object velocity.
Example
Inspect the initial velocity set via
velocity():obj = scene.add("sheet").velocity(0, 0, -1) print(obj.object_velocity)
- property param: ParamHolderΒΆ
Get the material parameters of the object.
- Returns:
The material parameters of the object.
- Return type:
ParamHolder
Example
Configure the Youngβs modulus through the returned holder:
scene.add("sheet").param.set("young-mod", 1e5)
- pin(ind: list[int] | None = None) PinHolder[source]ΒΆ
Set specified vertices as pinned.
An object with every vertex pinned is a static collider β its motion is prescribed, not simulated. Use the returned
PinHolderto animate it viaPinHolder.move_by(),PinHolder.move_to(), orPinHolder.transform_keyframes().- Parameters:
ind (Optional[list[int]], optional) β The indices of the vertices to pin.
None (If)
None. (all vertices are pinned. Defaults to)
- Returns:
The pin holder.
- Return type:
PinHolder
Example
Static sphere that slides across the scene:
(scene.add("sphere") .at(-1, 0, 0) .pin() .move_by([8, 0, 0], t_start=0.0, t_end=5.0))
- property pin_list: list[PinHolder]ΒΆ
Get the list of pin holders.
Example
Iterate pin holders attached to an object:
obj = scene.add("sheet") obj.pin([0, 1, 2]) for holder in obj.pin_list: print(len(holder.index))
- property position: list[float]ΒΆ
Get the object translation from the transform matrix.
Example
Read back the translation set by
at():obj = scene.add("sheet").at(0, 0.6, 0) assert obj.position == [0.0, 0.6, 0.0]
- report()[source]ΒΆ
Report the object data.
Example
Print a summary of an object after configuring it:
obj = scene.add("sheet").at(0, 0.6, 0) obj.pin(obj.grab([-1, 0, -1])) obj.report()
- rotate(angle: float, axis: str) Object[source]ΒΆ
Apply rotation around a specified axis to the transform.
- Parameters:
angle (float) β The rotation angle in degrees.
axis (str) β The rotation axis (βxβ, βyβ, or βzβ).
- Returns:
The object with the updated rotation.
- Return type:
Example
Stand a sheet upright by rotating 90 degrees around x:
scene.add("sheet").rotate(90, "x").at(0, 0.5, 0)
- scale(_scale: float) Object[source]ΒΆ
Apply uniform scale to the transform.
- Parameters:
_scale (float) β The scale factor.
- Returns:
The object with the updated scale.
- Return type:
Example
Shrink an armadillo to 0.75 of its original size:
scene.add("armadillo").scale(0.75).at(0, 1, 0)
- set_uv(uv: list[ndarray]) Object[source]ΒΆ
Set the UV coordinates of the object.
- Parameters:
uv (list[np.ndarray]) β The UV coordinates for each face.
- Returns:
The object with the updated UV coordinates.
- Return type:
Example
Supply per-face UVs for a triangulated sheet:
import numpy as np sheet = scene.add("sheet") n_faces = len(sheet.get("F")) uv = [np.zeros((3, 2), dtype=np.float32) for _ in range(n_faces)] sheet.set_uv(uv)
- property static: boolΒΆ
Get whether the object is static.
Example
Pinning every vertex turns an object into a static collider:
obj = scene.add("sphere").pin().object assert obj.static
- static_color(red: float, green: float, blue: float) Object[source]ΒΆ
Set the static color of the object.
- Parameters:
red (float) β The red component.
green (float) β The green component.
blue (float) β The blue component.
- Returns:
The object with the updated static color.
- Return type:
Example
Tint a fully pinned collider a light gray:
scene.add("sphere").pin() scene.select("sphere").static_color(0.75, 0.75, 0.75)
- stitch(name: str) Object[source]ΒΆ
Apply stitch to the object.
- Parameters:
name (str) β The name of stitch registered in the asset manager.
- Returns:
The stitched object.
- Return type:
Example
Attach a glue stitch registered earlier in the asset manager:
app.asset.add.stitch("glue", stitch_data) scene.add("dress").stitch("glue").rotate(-90, "x")
- property transform_matrix: ndarrayΒΆ
Get the current 4x4 transformation matrix.
Example
Inspect the composed translation, rotation, and scale:
obj = scene.add("sheet").at(0, 0.6, 0) print(obj.transform_matrix)
- update_static()[source]ΒΆ
Recompute whether the object is static.
When every vertex is pinned and no pin carries operations, a pull strength, or an unpin time, the object is treated as static. The result is cached on
self._static.Example
Typically invoked internally by
Scene.build()after pins are finalized, but can be called directly to refresh the cached flag after editing pin data in place:obj = scene.select("sheet") obj.pin() obj.update_static() assert obj.static
- property uv_coords: list[ndarray] | NoneΒΆ
Get the UV coordinates.
Example
Check whether the asset carries UV data for texturing:
obj = scene.add("sheet") if obj.uv_coords is not None: print(obj.uv_coords[0].shape)
- velocity(u: float, v: float, w: float, t: float = 0.0) Object[source]ΒΆ
Set the velocity of the object.
- Parameters:
u (float) β The velocity in the x-direction.
v (float) β The velocity in the y-direction.
w (float) β The velocity in the z-direction.
t (float) β Time in seconds. 0.0 sets initial velocity; >0 adds a timed override.
- Returns:
The object with the updated velocity.
- Return type:
Example
Give an armadillo a downward initial velocity of 5 m/s:
scene.add("armadillo").at(0, 1, 0).velocity(0, -5, 0)
- velocity_schedule(schedule: list) Object[source]ΒΆ
Set a list of (time, [vx, vy, vz]) velocity overrides.
Example
Kick an object, then stop it a second later:
obj = scene.add("armadillo").at(0, 1, 0) obj.velocity_schedule([ (0.0, [0, -5, 0]), (1.0, [0, 0, 0]), ])
- vert_color(color: ndarray) Object[source]ΒΆ
Set the vertex colors of the object.
- Parameters:
color (np.ndarray) β The vertex colors.
- Returns:
The object with the updated vertex colors.
- Return type:
Example
Paint each vertex of a sheet with its own RGB:
import numpy as np sheet = scene.add("sheet") n = len(sheet.get("V")) sheet.vert_color(np.random.rand(n, 3))
- vertex(translate: bool) ndarray[source]ΒΆ
Get the transformed vertices of the object.
- Parameters:
translate (bool) β Whether to translate the vertices.
- Returns:
The transformed vertices.
- Return type:
np.ndarray
Example
Read world-space vertex positions after
.athas been set:sheet = scene.add("sheet").at(0, 0.6, 0) world_vert = sheet.vertex(True)
- class frontend.ObjectAdder(scene: Scene)[source]ΒΆ
Bases:
objectFactory for introducing meshes into a
Scene.Reached as
scene.add. Calling it returns anObjectthat can be chained with transforms, pins, and colors. Invisible colliders live underscene.add.invisible.Example
Drop a cloth sheet and an invisible ground wall into a scene:
scene = app.scene.create() scene.add("sheet").at(0, 0.6, 0) scene.add.invisible.wall([0, 0, 0], [0, 1, 0]) fixed = scene.build()
- invisibleΒΆ
The invisible object adder.
- Type:
- class frontend.ParamDecoder[source]ΒΆ
Bases:
objectLoad and apply
param.picklewritten by the Blender addon.Parameters are split across object-level (velocity, fTetWild hints, per-key
param.setvalues), pin configuration, cross-stitch constraints, explicit merge pairs, invisible walls / spheres, and session-level scene settings.set_path()loads the pickle, then theapply_*methods dispatch each section.Example
Apply a pickle to an existing scene and session (this is what
BlenderApp.make()does internally):from frontend._decoder_ import ParamDecoder decoder = ParamDecoder().set_path("/path/to/param.pickle") decoder.apply_to_objects(scene) decoder.apply_pin_config(scene) decoder.apply_invisible_colliders(scene) fixed_scene = scene.build() decoder.apply_to_session(session)
- apply_invisible_colliders(scene, verbose: bool = False)[source]ΒΆ
Create invisible wall and sphere colliders on the scene from the loaded pickle data.
Must be called BEFORE
scene.build().Example
Add the pickleβs invisible walls and spheres just before building:
from frontend._decoder_ import ParamDecoder decoder = ParamDecoder().set_path("/path/to/param.pickle") decoder.apply_invisible_colliders(scene) fixed_scene = scene.build()
- apply_pin_config(scene, verbose: bool = False)[source]ΒΆ
Apply saved pin configuration to scene pin holders.
Applies unpin time, pull strength, pin group id, embedded move keyframes, and explicit pin operations (spin / scale / move_by / torque) to each pin holder that has a matching config entry. Call after the scene is populated and pins are created.
Example
Reapply pin settings after populating objects:
from frontend._decoder_ import ParamDecoder decoder = ParamDecoder().set_path("/path/to/param.pickle") decoder.apply_to_objects(scene) decoder.apply_pin_config(scene, verbose=True)
- apply_to_objects(scene: Scene, verbose: bool = False)[source]ΒΆ
Apply the loaded parameter data to the objects in
scene.Call
set_path()first. Per-object dicts (velocity, velocity-schedule, collision-windows) are keyed by UUID; other keys are forwarded toobj.param.set. fTetWild overrides are consumed at populate-time and skipped here.- Parameters:
scene (Scene) β The scene to which the parameters will be applied.
verbose (bool) β Enable verbose logging.
Example
Load a pickle and apply per-object parameters to a populated scene:
from frontend._decoder_ import ParamDecoder decoder = ParamDecoder().set_path("/path/to/param.pickle") decoder.apply_to_objects(scene, verbose=True)
- apply_to_session(session: Session, verbose: bool = False)[source]ΒΆ
Apply scene-level and dynamic parameters from the loaded data to
session.Call
set_path()first. Static scene parameters are forwarded tosession.param.set;inactive-momentumis additionally set as a dynamic hold. Dynamic parameter keyframes fromdyn_paramare applied viasession.param.dyn(...).- Parameters:
session (Session) β The session to which the parameters will be applied.
verbose (bool) β Enable verbose logging.
Example
Apply scene-level and dynamic parameters to a freshly initialized session:
from frontend._decoder_ import ParamDecoder decoder = ParamDecoder().set_path("/path/to/param.pickle") session = app.session.create(scene).init(fixed_scene) decoder.apply_to_session(session) session.build().start()
- property cross_stitch: listΒΆ
- property explicit_merge_pairs: listΒΆ
- set_path(filepath: str) ParamDecoder[source]ΒΆ
Load parameter data from a pickle file and cache it on this decoder.
- Parameters:
filepath (str) β Path to the pickle file. Must end in
.pickle.- Returns:
selffor chaining.- Return type:
Example
Chain the load with a subsequent apply call:
from frontend._decoder_ import ParamDecoder decoder = ParamDecoder().set_path("/path/to/param.pickle") decoder.apply_to_objects(scene)
- class frontend.ParamManager[source]ΒΆ
Bases:
objectClass to manage simulation parameters.
Example
Configure the standard simulation parameters for a session before building it:
session = app.session.create(scene) ( session.param.set("frames", 120) .set("dt", 0.01) .set("fps", 30) ) session = session.build()
- change(value: Any) ParamManager[source]ΒΆ
Change the value of the dynamic parameter at the current time.
- Parameters:
value (Any) β The new value of the dynamic parameter. May be a scalar, bool, or list/tuple of floats, depending on the key.
- Returns:
The updated ParamManager object.
- Return type:
- Raises:
ValueError β If no dynamic key is currently selected.
Example
Slow playback to 10% after the third second via a dynamic key:
( session.param.dyn("playback") .time(2.99).hold() .time(3.0).change(0.1) )
- clear(key: str) ParamManager[source]ΒΆ
Reset a parameter to its default value and drop any dynamic entries for it.
- Parameters:
key (str) β The parameter key.
- Returns:
The updated ParamManager object.
- Return type:
Example
Revert a single parameter back to its default after trying an override:
session.param.set("dt", 0.001) session.param.clear("dt")
- clear_all()[source]ΒΆ
Clear all parameters to their default values.
Example
Reset every parameter back to its default before configuring a new run:
session.param.clear_all() session.param.set("frames", 60).set("dt", 0.01)
- copy() ParamManager[source]ΒΆ
Copy the ParamManager object.
- Returns:
The copied ParamManager object.
- Return type:
Example
Snapshot the current parameters before tweaking one of them:
baseline = session.param.copy() session.param.set("dt", 0.005)
- dyn(key: str) ParamManager[source]ΒΆ
Select the current dynamic parameter key and reset the internal time cursor.
- Parameters:
key (str) β The dynamic parameter key.
- Returns:
The updated ParamManager object.
- Return type:
- Raises:
ValueError β If
keydoes not exist.
Example
Flip gravity between t=1s and t=2s, then restore it:
g = session.param.get("gravity") (session.param.dyn("gravity") .time(1.0).hold() .time(1.5).change([-x for x in g]) .time(2.0).change(g))
- export(path: str)[source]ΒΆ
Export the parameters to
param.toml(anddyn_param.txtwhen present).In fast-check mode,
framesis forced to1.- Parameters:
path (str) β The path to the export directory.
Example
Write the parameters alongside a session directory for inspection or external launching:
session.param.export(fixed_session.info.path)
- get(key: str | None = None) Any[source]ΒΆ
Get the value of a parameter.
- Parameters:
key (Optional[str]) β The parameter key. Must be specified.
- Returns:
The value of the parameter.
- Return type:
Any
- Raises:
ValueError β If
keyisNone.
Example
Read the current gravity vector so a dynamic override can flip its sign later:
g = session.param.get("gravity") print(g)
- hold() ParamManager[source]ΒΆ
Hold the current value of the dynamic parameter at the current time.
- Returns:
The updated ParamManager object.
- Return type:
- Raises:
ValueError β If no dynamic key is currently selected.
Example
Keep playback steady until t=2.99s, then drop it at t=3.0s:
( session.param.dyn("playback") .time(2.99).hold() .time(3.0).change(0.1) )
- items()[source]ΒΆ
Get all parameter items.
- Returns:
The parameter items.
- Return type:
ItemsView
Example
Inspect every parameter currently configured on the session:
for key, value in session.param.items(): print(f"{key} = {value}")
- set(key: str, value: Any | None = None) ParamManager[source]ΒΆ
Set a parameter value.
If
valueisNone, the parameter is set toTrue.- Parameters:
key (str) β The parameter key. Must not contain an underscore; use
-instead.value (Any, optional) β The parameter value. Defaults to
None(interpreted asTrue).
- Returns:
The updated ParamManager object.
- Return type:
- Raises:
ValueError β If
keycontains an underscore or does not exist.
Example
Chain several
.setcalls to configure a session. Keys use hyphens, never underscores:( session.param.set("frames", 250) .set("dt", 0.01) .set("fps", 30) .set("min-newton-steps", 32) .set("gravity", [0, 0, 0]) )
- time(time: float) ParamManager[source]ΒΆ
Advance the current time cursor for the dynamic parameter.
- Parameters:
time (float) β The new current time. Must be strictly greater than the previous value.
- Returns:
The updated ParamManager object.
- Return type:
- Raises:
ValueError β If
timeis not strictly increasing.
Example
Advance the cursor between two dynamic-value updates:
( session.param.dyn("playback") .time(1.0).hold() .time(2.0).change(0.5) )
- class frontend.Plot(engine: str, param: PlotParam)[source]ΒΆ
Bases:
objectA plot backed by either the pythreejs or software rasterizer engine.
Example
Construct a plot via
PlotManager.create()and draw a mesh:plot = app.plot.create() V, F = app.mesh.square(res=32) plot.tri(V, F)
- curve(vert: ndarray, _edge: ndarray = array([], dtype=float64), color: ndarray = array([], dtype=float64), param_override: dict | None = None) Plot[source]ΒΆ
Plot a curve as a sequence of connected line segments.
If
_edgeis empty, a closed loop is built by connecting each vertex to the next one (with wrap-around).- Parameters:
vert (np.ndarray) β The vertex positions (Nx3 or Nx2) of the curve. 2D inputs are padded to 3D by appending a zero column.
_edge (np.ndarray) β Optional explicit edge elements (Ex2) of the curve.
color (np.ndarray) β Per-vertex colors (Nx3) with each value in [0, 1].
param_override (Optional[dict]) β Fields to override on a copy of the plot parameters.
- Returns:
selffor method chaining.- Return type:
Example
Plot a parametric 2D curve as a closed loop:
N = 500 t = np.linspace(0, 2 * np.pi, N, endpoint=False) r = 0.85 * np.cos(10 * t) + 1 P = np.column_stack([r * np.cos(t), r * np.sin(t)]) app.plot.create().curve(P)
- edge(vert: ndarray, edge: ndarray, color: ndarray, param_override: dict | None = None) Plot[source]ΒΆ
Plot a set of edges.
- Parameters:
vert (np.ndarray) β The vertices (Nx3) used by the edges.
edge (np.ndarray) β The edge elements (Ex2) as pairs of vertex indices.
color (np.ndarray) β Per-vertex colors (Nx3) with each value in [0, 1].
param_override (Optional[dict]) β Fields to override on a copy of the plot parameters.
- Returns:
selffor method chaining.- Return type:
Example
Draw just the edges of a mesh colored uniformly:
V, F = app.mesh.icosphere(r=1.0, subdiv_count=2) edges = np.vstack([F[:, [0, 1]], F[:, [1, 2]], F[:, [2, 0]]]) colors = np.tile([1.0, 0.3, 0.3], (len(V), 1)) app.plot.create().edge(V, edges, colors)
- is_jupyter_notebook() bool[source]ΒΆ
Return True if the code is running inside a Jupyter notebook.
Example
Skip expensive mesh conversion when not in a notebook:
plot = app.plot.create() if plot.is_jupyter_notebook(): plot.tri(V, F)
- plot(vert: ndarray, color: ndarray = array([], dtype=float64), tri: ndarray = array([], dtype=float64), seg: ndarray = array([], dtype=float64), pts: ndarray = array([], dtype=float64), param_override: dict | None = None) Plot[source]ΒΆ
Plot a mesh with optional triangles, edges, and points.
Rendering is only performed when running inside a Jupyter notebook.
- Parameters:
vert (np.ndarray) β The vertices (Nx3) of the mesh.
color (np.ndarray) β Per-vertex colors (Nx3) with each value in [0, 1].
tri (np.ndarray) β The triangle elements (Fx3) of the mesh.
seg (np.ndarray) β The edge elements (Ex2) of the mesh.
pts (np.ndarray) β The point element indices (Px1) of the mesh.
param_override (Optional[dict]) β Fields to override on a copy of the plot parameters.
- Returns:
selffor method chaining.- Return type:
Example
Render triangles and edges together with a camera override:
plot = app.plot.create() plot.plot(V, tri=F, seg=edges, param_override={"fov": 30})
- point(vert: ndarray, param_override: dict | None = None) Plot[source]ΒΆ
Plot a set of points.
- Parameters:
vert (np.ndarray) β The vertex positions (Nx3) of the points.
param_override (Optional[dict]) β Fields to override on a copy of the plot parameters.
- Returns:
selffor method chaining.- Return type:
Example
Scatter a random point cloud:
pts = np.random.rand(200, 3) app.plot.create().point(pts)
- tet(vert: ndarray, tet: ndarray, axis: int = 0, cut: float = 0.5, color: ndarray = array([], dtype=float64), param_override: dict | None = None) Plot[source]ΒΆ
Plot a tetrahedral mesh by rendering the cut surface as triangles.
Tetrahedra whose centroid lies past
cutalongaxiscontribute their faces; internal faces shared by two such tetrahedra are removed, leaving only the visible surface of the cut region.flat_shadingis forced on unlessparam_overrideexplicitly sets it.- Parameters:
vert (np.ndarray) β The vertices (Nx3) of the mesh.
tet (np.ndarray) β The tetrahedral elements (Tx4) of the mesh.
axis (int) β The axis index (0, 1, or 2) along which to cut.
cut (float) β The cut ratio in [0, 1] along
axis.color (np.ndarray) β Per-vertex colors (Nx3) with each value in [0, 1].
param_override (Optional[dict]) β Fields to override on a copy of the plot parameters.
- Returns:
selffor method chaining.- Return type:
Example
Slice an armadillo tet mesh along the x axis and preview it:
V, F, T = app.mesh.preset("armadillo").decimate(19000).tetrahedralize() app.plot.create().tet(V, T, axis=0, cut=0.5)
- tri(vert: ndarray, tri: ndarray, stitch: tuple[ndarray, ndarray] = (array([], dtype=float64), array([], dtype=float64)), color: ndarray = array([], dtype=float64), param_override: dict | None = None) Plot[source]ΒΆ
Plot a triangle mesh, optionally with visualized stitch connections.
If
stitchis provided, additional vertices and edges are generated to visualize each stitch as a line segment.- Parameters:
vert (np.ndarray) β The vertices (Nx3 or Nx2) of the mesh. 2D inputs are padded to 3D by appending a zero column.
tri (np.ndarray) β The triangle elements (Fx3) of the mesh.
stitch (tuple[np.ndarray, np.ndarray]) β Stitch data as a pair of arrays: an index array (Sx3) and a weight array (Sx2).
color (np.ndarray) β Per-vertex colors (Nx3) with each value in [0, 1].
param_override (Optional[dict]) β Fields to override on a copy of the plot parameters.
- Returns:
selffor method chaining.- Return type:
- Raises:
ValueError β If
tridoes not have exactly 3 columns.
Example
Plot a tetrahedralized armadilloβs surface triangles:
V, F, T = app.mesh.preset("armadillo").decimate(19000).tetrahedralize() app.plot.create().tri(V, F)
- update(vert: ndarray | None = None, color: ndarray | None = None, recompute_normals: bool = True)[source]ΒΆ
Update the cached vertex or color buffers and forward to the engine.
- Parameters:
vert (Optional[np.ndarray]) β New vertex positions. Only the leading
len(vert)rows of the cached buffer are overwritten.color (Optional[np.ndarray]) β New per-vertex colors. Only the leading
len(color)rows of the cached buffer are overwritten.recompute_normals (bool) β Whether to recompute normals after the update.
Example
Animate a cached plot by pushing new vertex positions each frame:
plot = app.plot.create().tri(V, F) for t in range(10): V_t = V + np.array([0, 0.01 * t, 0]) plot.update(vert=V_t)
- class frontend.PlotManager[source]ΒΆ
Bases:
objectFactory for creating
Plotinstances with shared parameters.Example
Reach the plot manager from the app and render a sheet:
app = App.create("demo") V, F = app.mesh.square(res=64) app.plot.create().tri(V, F)
- create(engine: str = 'threejs') Plot[source]ΒΆ
Create a new plot using the given rendering engine.
- Parameters:
engine (str) β The rendering engine to use. Either
"threejs"or"software".- Returns:
A new plot bound to this managerβs parameters.
- Return type:
Example
Create a plot and chain a triangle viewer call:
V, F = app.mesh.icosphere(r=1.0, subdiv_count=3) app.plot.create().tri(V, F)
- class frontend.Rod(iterable=(), /)[source]ΒΆ
Bases:
tuple[ndarray,ndarray]A class representing a rod mesh.
A rod mesh is a pair of vertices and edges. The first element of the tuple is the array of vertices and the second element is the array of edges.
Example
Obtain a rod from the line helper and unpack it:
rod = app.mesh.line([0, 0, 0], [1, 0, 0], n=16) V, E = rod app.asset.add.rod("strand", V, E)
- normalize() Rod[source]ΒΆ
Normalize the rod mesh in place so that the maximum bounding box extent is 1.
Example
Normalize a freshly built rod and then scale it:
rod = app.mesh.line([0, 0, 0], [10, 0, 0], n=32).normalize().scale(0.5) V, E = rod
- scale(scale_x: float, scale_y: float | None = None, scale_z: float | None = None) Rod[source]ΒΆ
Scale the rod mesh in place with the given scaling factors.
- Parameters:
scale_x (float) β scaling factor for the x-axis
scale_y (float | None) β scaling factor for the y-axis. If
None,scale_xis used.scale_z (float | None) β scaling factor for the z-axis. If
None,scale_xis used.
- Returns:
this rod with scaled vertices
- Return type:
Example
Shrink a rod uniformly to a quarter of its original length:
rod = app.mesh.line([0, 0, 0], [4, 0, 0], n=32).scale(0.25) V, E = rod
- class frontend.Scene(name: str, plot: PlotManager | None, asset: AssetManager)[source]ΒΆ
Bases:
objectA scene class.
A
Scenecollects objects, pins, invisible colliders, and stitch data, then compiles them into aFixedSceneviabuild().Example
Build a tiny drape scene from registered assets:
scene = app.scene.create() sheet = scene.add("sheet").at(0, 0.6, 0) sheet.pin(sheet.grab([-1, 0, -1]) + sheet.grab([1, 0, -1])) scene.add("sphere").at(0, 0, 0).pin() fixed = scene.build().report()
- addΒΆ
The object adder.
- Type:
- property asset_manager: AssetManagerΒΆ
Get the asset manager.
Example
Reach into the asset manager attached to this scene:
mgr = scene.asset_manager print(mgr.list())
- build(progress_callback=None) FixedScene[source]ΒΆ
Build the fixed scene from the current scene.
- Parameters:
progress_callback β Optional callable
f(fraction, step_info)invoked as the build progresses.- Returns:
The built fixed scene.
- Return type:
Example
Compile the scene and chain a summary print:
fixed = scene.build().report() session = app.session.create(fixed).build()
- clear() Scene[source]ΒΆ
Clear all objects from the scene.
- Returns:
The cleared scene.
- Return type:
Example
Start from a blank slate before re-adding objects:
scene.clear() scene.add("sheet").at(0, 0.6, 0)
- max(axis: str) float[source]ΒΆ
Get the maximum value of the scene along a specific axis.
- Parameters:
axis (str) β The axis to get the maximum value along, either βxβ, βyβ, or βzβ.
- Returns:
The maximum vertex coordinate along the specified axis.
- Return type:
float
Example
Place a ceiling wall just above the highest vertex:
y_max = scene.max("y") scene.add.invisible.wall([0, y_max + 0.01, 0], [0, -1, 0])
- min(axis: str) float[source]ΒΆ
Get the minimum value of the scene along a specific axis.
- Parameters:
axis (str) β The axis to get the minimum value along, either βxβ, βyβ, or βzβ.
- Returns:
The minimum vertex coordinate along the specified axis.
- Return type:
float
Example
Place a ground wall just below the lowest vertex:
y_min = scene.min("y") scene.add.invisible.wall([0, y_min - 0.01, 0], [0, 1, 0])
- property object_dict: dict[str, Object]ΒΆ
Get the object dictionary.
Example
Iterate every added object by its reference name:
for name, obj in scene.object_dict.items(): print(name, obj.obj_type)
- select(name: str) Object[source]ΒΆ
Select an object from the scene by its name.
- Parameters:
name (str) β The reference name of the object to select.
- Returns:
The selected object.
- Return type:
Example
Adjust an already-added object by ref name:
scene.add("sheet") sheet = scene.select("sheet") sheet.at(0, 0.6, 0)
- set_explicit_merge_pairs(pairs: list[dict])[source]ΒΆ
Set explicit per-vertex merge pairs captured at snap time.
Each entry must contain non-empty
source_uuidandtarget_uuid; missing keys raiseValueError.Example
Typically invoked internally by the Blender add-on decoder, but can be called directly to wire up per-vertex merge constraints:
scene.set_explicit_merge_pairs([ {"source_uuid": "a", "target_uuid": "b", "source_vert": 0, "target_vert": 12}, ])
- set_surface_map(name: str, tri_indices: ndarray, coefs: ndarray, surf_tri: ndarray)[source]ΒΆ
Store frame-embedding surface mapping for a tetrahedralized object.
- Parameters:
name β Object UUID key.
tri_indices β Closest triangle index in tet surface per original vertex (N,).
coefs β Frame coefficients (c1, c2, c3) per original vertex (N, 3).
surf_tri β Surface triangles of the tet mesh (Q, 3).
Example
Typically invoked internally by the Blender add-on decoder when a TetMesh is registered, but can be called directly to attach a surface map:
scene.set_surface_map("obj-uuid", tri_idx, coefs, surf_tri)
- class frontend.SceneDecoder(filepath: str, asset_manager: AssetManager, mesh_manager: MeshManager)[source]ΒΆ
Bases:
objectDecode
data.picklewritten by the Blender addon into scene objects.Handles canonical mesh deduplication, per-instance transforms, cached tetrahedralization for SOLID groups, pin registration with Blender-to-sim surface mapping, UV assignment for SHELL groups, and rod / static groups. Used internally by
BlenderApp.Example
Drive the decoder directly to populate a scene (this is what
BlenderApp.populate()does internally):from frontend._asset_ import AssetManager from frontend._mesh_ import MeshManager from frontend._decoder_ import SceneDecoder assets = AssetManager() meshes = MeshManager("/tmp/cache") decoder = SceneDecoder("/path/to/data.pickle", assets, meshes) decoder.populate_objects(scene, verbose=True)
- populate_objects(scene: Scene, verbose: bool = False, progress_callback=None, ftetwild_by_uuid: dict | None = None) Scene[source]ΒΆ
Populate
scenewith objects from the decoderβs pickle data.Handles STATIC, SOLID, SHELL, and ROD groups, including canonical mesh deduplication by UUID, per-instance transforms, cached tetrahedralization (with per-UUID fTetWild overrides), pin registration with Blender-to-sim surface mapping, and stitches.
- Parameters:
scene (Scene) β The scene to populate.
verbose (bool) β Enable verbose logging.
progress_callback β Optional callable
fn(progress: float, info: str)invoked during loading.progressis in[0.0, 1.0].ftetwild_by_uuid (dict | None) β Optional mapping of object UUID to fTetWild keyword arguments used during tetrahedralization.
- Returns:
The populated scene.
- Return type:
Example
Populate a fresh scene from a Blender pickle:
from frontend._asset_ import AssetManager from frontend._mesh_ import MeshManager from frontend._decoder_ import SceneDecoder decoder = SceneDecoder( "/path/to/data.pickle", AssetManager(), MeshManager("/tmp/cache"), ) decoder.populate_objects(scene, verbose=True) fixed_scene = scene.build()
- class frontend.SceneInfo(name: str, scene: Scene)[source]ΒΆ
Bases:
objectLightweight metadata handle carrying the scene name.
Example
Look up the name of the scene you are currently editing:
print(scene.info.name)
- class frontend.SceneManager(plot: PlotManager | None, asset: AssetManager)[source]ΒΆ
Bases:
objectSceneManager class. Use this to manage scenes.
Example
Create a scene through the appβs scene manager and build it:
app = App.create("demo") scene = app.scene.create() scene.add("sheet").at(0, 0.6, 0) fixed = scene.build()
- clear()[source]ΒΆ
Clear all the scenes in the manager.
Example
Remove every scene before rebuilding from scratch:
app.scene.clear() scene = app.scene.create()
- create(name: str = '') Scene[source]ΒΆ
Create a new scene.
If a scene with the given name already exists, it is replaced.
- Parameters:
name (str) β The name of the scene to create. If empty, defaults to
"scene".- Returns:
The created scene.
- Return type:
Example
Create two named scenes side by side:
cloth_scene = app.scene.create("cloth") rods_scene = app.scene.create("rods")
- list() list[str][source]ΒΆ
List all the scenes in the manager.
- Returns:
A list of scene names.
- Return type:
list[str]
Example
Inspect which scenes are currently registered:
for name in app.scene.list(): print(name)
- remove(name: str)[source]ΒΆ
Remove a scene from the manager.
- Parameters:
name (str) β The name of the scene to remove.
Example
Drop a scene that is no longer needed:
app.scene.remove("cloth")
- select(name: str, create: bool = True) Scene[source]ΒΆ
Select a scene.
If the scene exists, it is returned. If it does not exist and
createis True, a new scene is created and returned.- Parameters:
name (str) β The name of the scene to select.
create (bool, optional) β Whether to create a new scene if it does not exist. Defaults to True.
- Returns:
The selected (or newly created) scene.
- Return type:
Example
Fetch an existing scene by name, creating it lazily:
scene = app.scene.select("cloth") scene.add("sheet")
- class frontend.Session(app_name: str, app_root: str, proj_root: str, data_dirpath: str, name: str, autogenerated: int | None = None)[source]ΒΆ
Bases:
objectClass to setup a simulation session.
Instances are created via
SessionManager.create(), configured via theparammanager, then finalized withbuild()to produce aFixedSession.Example
Configure a session and build it into a runnable fixed session:
session = app.session.create(scene) session.param.set("frames", 120).set("dt", 0.01) fixed_session = session.build() fixed_session.start(blocking=True)
- property app_name: strΒΆ
Get the application name.
Example
Read the owning application name:
session = app.session.create(scene) print(session.app_name)
- property app_root: strΒΆ
Get the application root directory.
Example
Locate the application root on disk:
session = app.session.create(scene) print(session.app_root)
- build() FixedSession[source]ΒΆ
Build and persist a
FixedSessionfrom this session.Pickles the built session into its directory and creates a symlink (or a
.txtfallback on Windows without symlink privileges) under the data dir for convenient access.- Returns:
The newly built fixed session.
- Return type:
Example
Finalize a configured session and start it:
session.param.set("frames", 60).set("dt", 0.01) fixed_session = session.build() fixed_session.start(blocking=True)
- property fixed_scene: FixedScene | NoneΒΆ
Get the fixed scene.
- Returns:
The fixed scene object.
- Return type:
Optional[FixedScene]
Example
Inspect the bound scene before finalizing:
session = app.session.create(scene) print(session.fixed_scene)
- property fixed_session: FixedSession | NoneΒΆ
Get the fixed session.
- Returns:
The fixed session object, or
Noneifbuild()has not been called yet.- Return type:
Optional[FixedSession]
Example
Access the built runnable session after calling
build():session = app.session.create(scene) session.build() fixed = session.fixed_session
- init(scene: FixedScene) Session[source]ΒΆ
Attach a fixed scene to this session.
- Parameters:
scene (FixedScene) β The fixed scene.
- Returns:
This session, for chaining.
- Return type:
Example
SessionManager.create()calls this internally, but it can also be used to re-bind a scene to an existing session:session.init(scene).param.set("frames", 60)
- property name: strΒΆ
Get the session name.
Example
Retrieve the session name for logging or display:
session = app.session.create(scene) print(session.name)
- property param: ParamManagerΒΆ
Get the session parameter manager.
Example
Configure solver parameters before building the session:
session = app.session.create(scene) session.param.set("frames", 120).set("dt", 0.01)
- property proj_root: strΒΆ
Get the project root directory.
Example
Print the project root for the current session:
session = app.session.create(scene) print(session.proj_root)
- class frontend.SessionExport(fixed_session: FixedSession)[source]ΒΆ
Bases:
objectClass to handle session export operations.
Example
Export every simulated frame and zip the output directory:
session.start(blocking=True) session.export.animation().zip()
- animation(path: str = '', ext='ply', include_static: bool = True, clear: bool = False, options: dict | None = None) Zippable[source]ΒΆ
Export the animation frames.
If no frames are available yet, waits for the simulation if it is running, otherwise returns early. When
ffmpegis available and rendered PNGs are produced, also encodes anframe.mp4video in the export directory.- Parameters:
path (str) β The path to the export directory. If empty, a default path is used.
ext (str, optional) β The file extension. Defaults to
"ply".include_static (bool, optional) β Whether to include the static mesh. Defaults to
True.clear (bool, optional) β Whether to clear the existing files. Defaults to
False.options (dict, optional) β Additional arguments passed to the renderer.
- Returns:
A handle to the export directory that can be zipped.
- Return type:
Zippable
Example
Export every frame as PLY and zip the result:
session.start(blocking=True) session.export.animation().zip()
- frame(path: str = '', frame: int | None = None, include_static: bool = True, options: dict | None = None, delete_exist: bool = False) FixedSession[source]ΒΆ
Export a specific frame.
- Parameters:
path (str) β The path to the export file.
frame (Optional[int], optional) β The frame number. If
None, the latest available frame is used. Defaults toNone.include_static (bool, optional) β Whether to include the static mesh. Defaults to
True.options (dict, optional) β Additional arguments passed to the renderer.
delete_exist (bool, optional) β Whether to delete the existing file. Defaults to
False.
- Returns:
The owning fixed session object.
- Return type:
Example
Export just the latest frame as an OBJ file:
session.export.frame("latest.obj")
- shell_command(param: ParamManager) str[source]ΒΆ
Generate a platform-specific launcher script for the solver.
On Windows, writes a
command.batfile; on Linux/macOS, writes acommand.shscript (marked executable).- Parameters:
param (ParamManager) β The simulation parameters.
- Returns:
The path to the generated launcher script.
- Return type:
str
Example
Regenerate the solver launcher alongside a session (useful for re-running from the command line):
path = session.export.shell_command(session.session.param) print(path)
- class frontend.SessionGet(fixed_session: FixedSession)[source]ΒΆ
Bases:
objectClass to handle session data retrieval operations.
Example
Pull the most recent vertex buffer and a log channel from a running or completed session:
vert, frame = session.get.vertex() per_frame_ms = session.get.log.numbers("time-per-frame")
- command() str | None[source]ΒΆ
Get the path to the solver launcher script.
On Windows this points at
command.bat; elsewhere atcommand.sh.- Returns:
The path to the launcher script if it exists,
Noneotherwise.- Return type:
Optional[str]
Example
Print the launcher path so it can be re-run from a shell:
path = session.get.command() if path: print(path)
- latest_frame() int[source]ΒΆ
Get the latest frame number.
- Returns:
The latest frame number.
- Return type:
int
Example
Poll the most recent frame index while the solver runs:
frame = session.get.latest_frame() print(f"solver is on frame {frame}")
- property log: SessionLogΒΆ
Get the session log object.
Example
Fetch the list of emitted log channels:
session = session.build().start(blocking=True) channels = session.get.log.names()
- nvidia_smi() None[source]ΒΆ
Read and print the exported nvidia-smi outputs.
Reads both nvidia-smi.txt and nvidia-smi-q.txt from the nvidia-smi directory and prints their concatenated contents.
Example
Inspect the GPU state captured at the start of a run:
session.get.nvidia_smi()
- param_summary() list[str][source]ΒΆ
Get the parameter summary from the param_summary.txt file.
- Returns:
The lines from the parameter summary file, or empty list if file doesnβt exist.
- Return type:
list[str]
Example
Print the parameter summary captured by the solver at launch:
for line in session.get.param_summary(): print(line)
- saved() list[int][source]ΒΆ
Get the list of saved frame numbers.
- Returns:
The list of saved frame numbers.
- Return type:
list[int]
Example
Resume from the newest saved state if any exist:
saved = session.get.saved() if saved: session.resume(max(saved))
- vertex(n: int | None = None) tuple[ndarray, int] | None[source]ΒΆ
Get the vertex data for a specific frame.
- Parameters:
n (Optional[int], optional) β The frame number. If
None, the latest frame is returned. Defaults toNone.- Returns:
A tuple
(vertices, frame)whereverticeshas shape(N, 3)and dtypefloat32, orNoneif no data is available.- Return type:
Optional[tuple[np.ndarray, int]]
Example
Read the latest exported vertex buffer from disk:
result = session.get.vertex() if result is not None: vert, frame = result print(vert.shape, frame)
- vertex_frame_count() int[source]ΒΆ
Get the highest frame index that has an exported vertex buffer.
- Returns:
The highest frame index found in
output/vert_*.bin, or0if none exist.- Return type:
int
Example
Wait until at least one frame is on disk before replaying:
while session.get.vertex_frame_count() == 0: time.sleep(0.5)
- class frontend.SessionInfo(name: str)[source]ΒΆ
Bases:
objectClass to store session information.
Example
Read the on-disk directory path for a built session:
fixed_session = session.build() print(fixed_session.info.name) print(fixed_session.info.path)
- property name: strΒΆ
Get the name of the session.
Example
Inspect the session name before starting a run:
session = app.session.create(fixed_scene).build() print(session.info.name)
- property path: strΒΆ
Get the path to the session directory.
Example
Read the on-disk session directory after building:
session = app.session.create(fixed_scene).build() print(session.info.path)
- set_path(path: str) SessionInfo[source]ΒΆ
Set the path to the session directory.
- Parameters:
path (str) β The path to the session directory.
- Returns:
This instance, for chaining.
- Return type:
Example
Normally this is called by
SessionManagerduring session construction. A direct call may be useful when relocating an existing session on disk:info = SessionInfo("my_run") info.set_path("/data/sessions/my_run") print(info.path)
- class frontend.SessionManager(app_name: str, app_root: str, proj_root: str, data_dirpath: str)[source]ΒΆ
Bases:
objectClass to manage simulation sessions.
Example
Create a session from a built scene and launch it:
session = app.session.create(scene) session.param.set("frames", 60).set("dt", 0.01) session = session.build() session.start(blocking=True)
- clear(force: bool = True)[source]ΒΆ
Clear all sessions.
- Parameters:
force (bool, optional) β Whether to force clearing.
Example
Remove every session and any running solver before starting fresh:
app.session.clear(force=True)
- create(scene: FixedScene, name: str = '') Session[source]ΒΆ
Create a new session.
If
nameis empty, an auto-generated name is used:"session"for the first call, then"session-1","session-2", β¦ for subsequent calls.- Parameters:
scene (FixedScene) β The scene object.
name (str) β The name of the session. Defaults to
""(auto-generated).
- Returns:
The created session.
- Return type:
- Raises:
Exception β If the scene has violations (self-intersections, contact-offset violations, etc.).
Example
Create a session from a built fixed scene and configure it:
scene = app.scene.create() scene.add("sheet").at(0, 0.5, 0) scene = scene.build() session = app.session.create(scene) session.param.set("frames", 60).set("dt", 0.01) session = session.build()
- delete(name: str, force: bool = True)[source]ΒΆ
Delete a session.
- Parameters:
name (str) β The name of the session.
force (bool, optional) β Whether to force deletion.
Example
Tear down a named session, terminating the solver if it is still running:
app.session.delete("run-A", force=True)
- list()[source]ΒΆ
List all sessions.
- Returns:
The sessions.
- Return type:
dict
Example
Print the names of every session currently tracked by the app:
for name in app.session.list(): print(name)
- select(name: str = 'session')[source]ΒΆ
Select an existing session by name.
- Parameters:
name (str) β The name of the session. Defaults to
"session".- Returns:
The selected session.
- Return type:
- Raises:
ValueError β If no session with the given name exists.
Example
Re-fetch a previously-created session by name:
app.session.create(scene, name="run-A") session = app.session.select("run-A")
- class frontend.SessionOutput(session: FixedSession)[source]ΒΆ
Bases:
objectClass to handle session output operations.
Example
Locate the solver output directory for a built session (used by exporters and log readers):
print(session.output.path)- property path: strΒΆ
Get the path to the output directory.
Example
Locate the solver output directory for post-processing:
session = session.build().start(blocking=True) output_dir = session.output.path
- class frontend.Sphere[source]ΒΆ
Bases:
objectAn invisible sphere class.
Example
Add an inverted hemispherical bowl as a collider:
scene.add.invisible.sphere([0, 1, 0], 1.0).invert().hemisphere()- add(pos: list[float], radius: float) Sphere[source]ΒΆ
Add an invisible sphere information.
- Parameters:
pos (list[float]) β The position of the sphere.
radius (float) β The radius of the sphere.
- Returns:
The sphere.
- Return type:
Example
Instantiate a unit sphere at the origin:
sphere = Sphere().add([0, 0, 0], 1.0)
- property entry: list[tuple[list[float], float, float]]ΒΆ
Get the sphere entries.
Example
Inspect the keyframed (position, radius, time) list:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.5) for pos, r, t in sphere.entry: print(t, r, pos)
- get_entry() list[tuple[list[float], float, float]][source]ΒΆ
Get the time-dependent sphere entries.
Example
Iterate over an animated sphereβs keyframes (position, radius, time):
sphere = scene.add.invisible.sphere([0, 0, 0], 0.25) sphere.transform_to([0, 0.5, 0], 0.5, 2.0) for pos, r, t in sphere.get_entry(): print(t, r, pos)
- hemisphere() Sphere[source]ΒΆ
Turn the sphere into a hemisphere, so the top half becomes empty, like a bowl.
Example
Cup-shaped collider by combining
invertandhemisphere:scene.add.invisible.sphere([0, 1, 0], 1.0).invert().hemisphere()
- interp(transition: str) Sphere[source]ΒΆ
Set the transition type for the sphere.
- Parameters:
transition (str) β The transition type. Defaults to
"linear"if never set.- Returns:
The sphere.
- Return type:
Example
Ease the sphere motion with a smoothstep:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.5) sphere.move_to([0, 0.5, 0], 2.0).interp("smooth")
- invert() Sphere[source]ΒΆ
Invert the sphere, so the inside becomes empty and the outside becomes solid.
Example
Trap objects inside an inverted sphere boundary:
scene.add.invisible.sphere([0, 0, 0], 2.0).invert()
- property is_hemisphere: boolΒΆ
Get whether sphere is hemisphere.
Example
Check the hemisphere flag on a collider:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.5) assert sphere.is_hemisphere in (True, False)
- property is_inverted: boolΒΆ
Get whether sphere is inverted.
Example
An inverted sphere acts as a containing bowl:
sphere = scene.add.invisible.sphere([0, 0, 0], 1.0, invert=True) assert sphere.is_inverted
- move_by(delta: list[float], time: float) Sphere[source]ΒΆ
Move the sphere by a positional delta at a specific time.
- Parameters:
delta (list[float]) β The positional delta to move the sphere.
time (float) β The absolute time to move the sphere.
- Returns:
The sphere.
- Return type:
Example
Slide a collider sphere 1 unit along +x by t=2:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.5) sphere.move_by([1, 0, 0], 2.0)
- move_to(pos: list[float], time: float) Sphere[source]ΒΆ
Move the sphere to an absolute position at a specific time.
- Parameters:
pos (list[float]) β The target position of the sphere.
time (float) β The absolute time to move the sphere.
- Returns:
The sphere.
- Return type:
Example
Drop a ball from above to y=0 by t=1:
sphere = scene.add.invisible.sphere([0, 1, 0], 0.25) sphere.move_to([0, 0, 0], 1.0)
- property param: SphereParamΒΆ
Get the sphere parameters.
Example
Adjust a sphere parameter through the returned holder:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.5) sphere.param.set("friction", 0.2)
- radius(radius: float, time: float) Sphere[source]ΒΆ
Change the radius of the sphere at a specific time.
- Parameters:
radius (float) β The target radius of the sphere.
time (float) β The absolute time to change the radius.
- Returns:
The sphere.
- Return type:
Example
Inflate the sphere from 0.25 to 0.5 by t=1:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.25) sphere.radius(0.5, 1.0)
- transform_to(pos: list[float], radius: float, time: float) Sphere[source]ΒΆ
Change the sphere to a new position and radius at a specific time.
- Parameters:
pos (list[float]) β The target position of the sphere.
radius (float) β The target radius of the sphere.
time (float) β The absolute time to transform the sphere.
- Returns:
The sphere.
- Return type:
Example
Grow the collider sphere as it slides into place:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.25) sphere.transform_to([0, 0.5, 0], 0.5, 2.0)
- property transition: strΒΆ
Get the sphere transition.
Example
Read back the interpolation mode:
sphere = scene.add.invisible.sphere([0, 0, 0], 0.5).interp("smooth") assert sphere.transition == "smooth"
- class frontend.TetMesh(iterable=(), /)[source]ΒΆ
Bases:
tuple[ndarray,ndarray,ndarray]A class representing a tetrahedral mesh.
A tetrahedral mesh is a triple of vertices, surface triangles, and tetrahedra.
- surface_mapΒΆ
Optional tuple of
(tri_indices, coefs)for reconstructing the original surface via frame-embedding interpolation.coefsare the three frame coefficients(c1, c2, c3)per original vertex, wherec1andc2are affine coordinates in the triangleβs edge basis, andc3is the signed normal offset in absolute world units.
Example
Tetrahedralize a surface mesh and unpack the result:
tet = app.mesh.icosphere(r=0.35, subdiv_count=4).tetrahedralize() V, F, T = tet app.asset.add.tet("sphere", V, F, T)
- has_surface_mapping() bool[source]ΒΆ
Check if this TetMesh has surface mapping data.
Example
Guard a call to
interpolate_surface()with this check:tet = surface_mesh.tetrahedralize() if tet.has_surface_mapping(): orig = tet.interpolate_surface()
- interpolate_surface(deformed_vert: ndarray | None = None) ndarray[source]ΒΆ
Reconstruct original-resolution positions from the deformed tet surface.
Uses the stored frame-embedding mapping
p' = x0' + c1*b1' + c2*b2' + c3*n_hat'.- Parameters:
deformed_vert β deformed tet mesh vertices. If
None, the current vertices are used.- Returns:
reconstructed vertex positions matching the original surface vertex count.
- Return type:
np.ndarray
- Raises:
ValueError β if no surface mapping has been set on this mesh.
Example
Recover the original-resolution surface from a deformed tet mesh:
tet = surface_mesh.tetrahedralize() deformed = tet[0] * 2.0 orig_surface = tet.interpolate_surface(deformed) app.plot.create().tri(orig_surface, surface_mesh[1])
- normalize() TetMesh[source]ΒΆ
Return
selfafter invokingnormalize()on the vertex array.Example
Normalize the armadillo tet mesh in one chain:
V, F, T = ( app.mesh.preset("armadillo").decimate(19000).tetrahedralize().normalize() )
- scale(scale_x: float, scale_y: float | None = None, scale_z: float | None = None) TetMesh[source]ΒΆ
Scale the tet mesh with the given scaling factors.
- Parameters:
scale_x (float) β scaling factor for the x-axis
scale_y (float | None) β scaling factor for the y-axis. If
None,scale_xis used.scale_z (float | None) β scaling factor for the z-axis. If
None,scale_xis used.
- Returns:
this tet mesh (
self)- Return type:
Example
Squash a tet mesh along the y axis before registering it:
tet = app.mesh.tet_box(1, 1, 1).scale(1.0, 0.25, 1.0) V, F, T = tet
- set_surface_mapping(tri_indices: ndarray, coefs: ndarray) TetMesh[source]ΒΆ
Set the frame-embedding surface mapping used by
interpolate_surface().- Parameters:
tri_indices β closest triangle in the tet surface per original vertex, shape
(N,)coefs β frame coefficients
(c1, c2, c3)per original vertex, shape(N, 3)
- Returns:
this tet mesh with the surface mapping attached
- Return type:
Example
Attach a precomputed mapping back onto a tet mesh:
tet = app.mesh.create.tet(V, F, T).set_surface_mapping(tri_idx, coefs) orig = tet.interpolate_surface()
- class frontend.TriMesh(iterable=(), /)[source]ΒΆ
Bases:
tuple[ndarray,ndarray]A class representing a triangle mesh.
A triangle mesh is a pair of vertices and triangles.
Example
Create a sphere, decimate it, then tetrahedralize it in one chain:
tet = ( app.mesh.icosphere(r=1.0, subdiv_count=4) .decimate(2000) .tetrahedralize() ) V, F, T = tet
- compute_cache_path(name: str) str[source]ΒΆ
Compute a cache file path derived from the mesh hash and the given tag.
Example
Typically invoked automatically by the mesh pipeline. To look up where a tagged cache entry would live on disk:
path = mesh.compute_cache_path("decimate__19000") print(path)
- static create(vert: ndarray, elm: ndarray, cache_dir: str) TriMesh[source]ΒΆ
Create a triangle mesh, recompute its hash, and bind the given cache directory.
Example
Typically invoked automatically by the mesh pipeline. To build a TriMesh directly from raw arrays:
import numpy as np from frontend._mesh_ import TriMesh mesh = TriMesh.create(V, F, cache_dir="/tmp/ppf-cache") V2, F2 = mesh
- decimate(target_tri: int) TriMesh[source]ΒΆ
Reduce the number of triangles in the mesh to the target count.
Uses quadric decimation via
trimesh, followed by a cleanup pass that merges skinny triangles. Results are cached on disk.- Parameters:
target_tri (int) β target number of triangles (must be less than the current count)
- Returns:
a decimated mesh
- Return type:
Example
Decimate the armadillo preset down to 19000 triangles:
tet = app.mesh.preset("armadillo").decimate(19000).tetrahedralize()
- export(path)[source]ΒΆ
Export the mesh to a file using
trimesh.The output format is inferred from the file extension of
path(for example.ply,.obj,.stl).- Parameters:
path (str) β export path
Example
Save an icosphere as a PLY file:
app.mesh.icosphere(r=1.0, subdiv_count=3).export("sphere.ply")
- load_cache(path: str) TriMesh | None[source]ΒΆ
Load a cached mesh from the given path, or return
Noneif it does not exist.Example
Typically invoked automatically by the mesh pipeline. To probe for a cached result before recomputing:
cached = mesh.load_cache(mesh.compute_cache_path("decimate__19000")) if cached is None: cached = mesh.decimate(19000)
- normalize() TriMesh[source]ΒΆ
Return
selfafter invokingnormalize()on the vertex array.Example
Rescale an imported mesh so its largest extent is 1:
V, F = app.mesh.load_tri("big.ply").normalize()
- recompute_hash() TriMesh[source]ΒΆ
Recompute the SHA-256 hash of the mesh from its vertex and element arrays.
Example
Typically invoked automatically after mutating operations. To refresh the hash manually (for example after editing
mesh[0]in place):mesh.recompute_hash() cache_path = mesh.compute_cache_path("custom")
- save_cache(path: str) TriMesh[source]ΒΆ
Save the meshβs vertex and triangle arrays to the given
.npzpath.Example
Typically invoked automatically by operations like
decimate(). To snapshot a mesh to disk manually:path = mesh.compute_cache_path("snapshot") mesh.save_cache(path)
- scale(scale_x: float, scale_y: float | None = None, scale_z: float | None = None) TriMesh[source]ΒΆ
Scale the triangle mesh with the given scaling factors.
- Parameters:
scale_x (float) β scaling factor for the x-axis
scale_y (float | None) β scaling factor for the y-axis. If
None,scale_xis used.scale_z (float | None) β scaling factor for the z-axis. If
None,scale_xis used.
- Returns:
this triangle mesh (
self)- Return type:
Example
Flatten a sphere into an oblate shape before registering it:
V, F = app.mesh.icosphere(r=1.0, subdiv_count=3).scale(1.0, 0.3, 1.0) app.asset.add.tri("pill", V, F)
- set_cache_dir(cache_dir: str) TriMesh[source]ΒΆ
Set the cache directory used by this mesh.
Example
Typically invoked automatically by the mesh pipeline. To retarget an existing mesh to a different cache location:
mesh.set_cache_dir("/tmp/ppf-cache-alt")
- subdivide(n: int = 1, method: str = 'midpoint')[source]ΒΆ
Subdivide the mesh with the given number of iterations and method.
Results are cached on disk.
- Parameters:
n (int) β number of subdivision iterations
method (str) β subdivision method. Available methods are
"midpoint"and"loop".
- Returns:
a subdivided mesh
- Return type:
- Raises:
Exception β if the mesh is not a triangle mesh or
methodis unknown.
Example
Subdivide a coarse box once using Loop subdivision:
V, F = app.mesh.box(1, 1, 1).subdivide(n=1, method="loop") app.plot.create().tri(V, F)
- tetrahedralize(*args, **kwargs) TetMesh[source]ΒΆ
Tetrahedralize a surface triangle mesh using fTetWild.
The original surface is preserved via a frame-embedding mapping, allowing interpolation of deformed positions back to the original surface structure. fTetWild is invoked in a subprocess so that other Python threads remain responsive. Results are cached on disk.
- Keyword Arguments:
edge_length_fac (float) β tetrahedral edge length as a fraction of the bbox diagonal (default:
0.05).optimize (bool) β whether to optimize the mesh (default:
True).epsilon (float) β fTetWild
epsilontolerance (optional).stop_energy (float) β fTetWild stop-energy threshold (optional).
num_opt_iter (int) β number of optimization iterations (optional).
simplify (bool) β whether to simplify the input surface (optional).
coarsen (bool) β whether to coarsen the tet mesh (optional).
status_callback (callable | None) β called periodically with a status string while the subprocess runs.
status_interval (float) β polling interval in seconds for
status_callback(default:5.0).
- Returns:
a tetrahedral mesh with surface mapping attached. Use
TetMesh.interpolate_surface()to recover deformed positions in the original surface structure.- Return type:
- Raises:
RuntimeError β if the fTetWild subprocess exits with a non-zero status.
Example
Tetrahedralize a sphere and recover the original surface after simulation via
TetMesh.interpolate_surface():tet = app.mesh.icosphere(r=0.35, subdiv_count=4).tetrahedralize( edge_length_fac=0.05 ) V, F, T = tet app.asset.add.tet("sphere", V, F, T)
- triangulate(target: int = 1024, min_angle: float = 20) TriMesh[source]ΒΆ
Triangulate a closed 2D line shape.
The current mesh must be a 2D line mesh (vertices with shape
(N, 2)and segment elements of shape(M, 2)). Results are cached on disk.- Parameters:
target (int) β target number of triangles (used to derive a maximum triangle area)
min_angle (float) β minimum triangle angle in degrees passed to the triangulator
- Returns:
a triangulated mesh
- Return type:
- Raises:
Exception β if the element array is not a line mesh.
Example
Turn a closed 2D curve into a filled triangle mesh:
pts = np.array( [[np.cos(t), np.sin(t)] for t in np.linspace(0, 2 * np.pi, 64, endpoint=False)] ) V, F = app.mesh.create.tri(pts).triangulate(target=1024)
- class frontend.Utils[source]ΒΆ
Bases:
objectUtility class for frontend.
Example
Check whether the solver is running and stop it if so before kicking off a new simulation:
from frontend import Utils if Utils.busy(): Utils.terminate() print("gpus:", Utils.get_gpu_count())
- MIN_SM = 60ΒΆ
- static busy() bool[source]ΒΆ
Check if the solver is running.
- Returns:
True if the solver is running, False otherwise.
- Return type:
bool
Example
Poll until the current simulation has released the GPU:
import time from frontend import Utils while Utils.busy(): time.sleep(1)
- static check_gpu()[source]ΒΆ
Check that an NVIDIA GPU with sufficient compute capability is present.
- Raises:
RuntimeError β If nvidia-smi is not found or the GPUβs SM version is below MIN_SM.
Example
Validate the GPU before building a session, falling back to a helpful message on unsupported hardware:
from frontend import Utils try: Utils.check_gpu() except RuntimeError as e: print("GPU check failed:", e)
- static ci_name() str | None[source]ΒΆ
Determine if the code is running in a CI environment.
- Returns:
The name of the CI environment read from the
.CIfile, orNoneif no.CIfile is present.- Return type:
Optional[str]
- Raises:
ValueError β If the
.CIfile exists but is empty.
Example
Skip a heavy example when running under CI:
from frontend import Utils if Utils.ci_name() is not None: print("running in CI; using short config")
- static get_ci_dir() str[source]ΒΆ
Get the path to the CI local directory.
Example
Resolve the CI scratch directory when running under a CI environment:
from frontend import Utils if Utils.ci_name() is not None: print(Utils.get_ci_dir())
- static get_ci_root() str[source]ΒΆ
Get the path to the CI directory.
Example
Print the root directory under which per-CI-environment artifacts are stored:
from frontend import Utils print(Utils.get_ci_root())
- static in_jupyter_notebook()[source]ΒΆ
Determine if the code is running in a Jupyter notebook.
Returns
Falsewhen a.CLIor.CImarker file is present alongside this module, or when IPython is unavailable.Example
Gate optional rich widgets behind notebook detection:
from frontend import Utils if Utils.in_jupyter_notebook(): print("interactive widgets available") else: print("running from a script")
- static is_fast_check() bool[source]ΒΆ
Determine if fast check mode is enabled.
Fast check mode forces simulations to run for only 1 frame, enabling quick validation of all examples.
- Returns:
True if fast check mode is enabled.
- Return type:
bool
Example
Shorten a simulation when running under fast-check mode:
from frontend import Utils frames = 1 if Utils.is_fast_check() else 240 print("frames:", frames)
- static set_fast_check(enabled: bool = True)[source]ΒΆ
Set fast check mode.
- Parameters:
enabled β Whether to enable fast check mode.
Example
Toggle fast-check mode on before running a smoke test and turn it back off afterwards:
from frontend import Utils Utils.set_fast_check(True) try: assert Utils.is_fast_check() finally: Utils.set_fast_check(False)
- class frontend.Wall[source]ΒΆ
Bases:
objectAn invisible wall class.
Example
Box a trampoline in with four invisible walls:
gap = 0.025 scene.add.invisible.wall([1 + gap, 0, 0], [-1, 0, 0]) scene.add.invisible.wall([-1 - gap, 0, 0], [1, 0, 0]) scene.add.invisible.wall([0, 0, 1 + gap], [0, 0, -1]) scene.add.invisible.wall([0, 0, -1 - gap], [0, 0, 1])
- add(pos: list[float], normal: list[float]) Wall[source]ΒΆ
Add the initial wall entry.
- Parameters:
pos (list[float]) β The position of the wall.
normal (list[float]) β The outer normal of the wall.
- Returns:
The invisible wall.
- Return type:
Example
Place a ground wall at y=0 with the outer normal facing up:
wall = Wall().add([0, 0, 0], [0, 1, 0])
- property entry: list[tuple[list[float], float]]ΒΆ
Get the wall entries.
Example
Iterate the wall animation keyframes:
wall = scene.add.invisible.wall([0, 0, 0], [0, 1, 0]) for pos, t in wall.entry: print(t, pos)
- get_entry() list[tuple[list[float], float]][source]ΒΆ
Get a list of time-dependent wall entries.
- Returns:
A list of time-dependent entries, each containing a position and time.
- Return type:
list[tuple[list[float], float]]
Example
Inspect every keyframed position on an animated wall:
wall = scene.add.invisible.wall([0, 0, 0], [0, 1, 0]) wall.move_to([0, 0.2, 0], 2.0) for pos, t in wall.get_entry(): print(t, pos)
- interp(transition: str) Wall[source]ΒΆ
Set the transition type for the wall.
- Parameters:
transition (str) β The transition type. Defaults to
"linear"if never set.- Returns:
The invisible wall.
- Return type:
Example
Ease the wall motion with a smoothstep instead of a linear ramp:
wall.move_to([0.5, 0, 0], 3.0).interp("smooth")
- move_by(delta: list[float], time: float) Wall[source]ΒΆ
Move the wall by a positional delta at a specific time.
- Parameters:
delta (list[float]) β The positional delta to move the wall.
time (float) β The absolute time to move the wall.
- Returns:
The invisible wall.
- Return type:
Example
Raise the ground wall by 0.2 units at t=2 seconds:
wall = scene.add.invisible.wall([0, 0, 0], [0, 1, 0]) wall.move_by([0, 0.2, 0], 2.0)
- move_to(pos: list[float], time: float) Wall[source]ΒΆ
Move the wall to an absolute position at a specific time.
- Parameters:
pos (list[float]) β The target position of the wall.
time (float) β The absolute time to move the wall.
- Returns:
The invisible wall.
- Return type:
Example
Animate a piston wall toward an absolute target:
wall = scene.add.invisible.wall([0, 0, 0], [1, 0, 0]) wall.move_to([0.5, 0, 0], 3.0)
- property normal: list[float]ΒΆ
Get the wall normal.
Example
Read the outer normal back from an added wall:
wall = scene.add.invisible.wall([0, 0, 0], [0, 1, 0]) print(wall.normal)
- property param: WallParamΒΆ
Get the wall parameters.
Example
Tweak a wall parameter through the returned holder:
wall = scene.add.invisible.wall([0, 0, 0], [0, 1, 0]) wall.param.set("friction", 0.2)
- property transition: strΒΆ
Get the wall transition.
Example
Check the interpolation mode of an animated wall:
wall = scene.add.invisible.wall([0, 0, 0], [0, 1, 0]).interp("smooth") assert wall.transition == "smooth"
- frontend.get_cache_dir() str[source]ΒΆ
Get the ppf-cts cache directory.
Returns a platform-appropriate cache directory for ppf-cts. - Linux/Mac: ~/.cache/ppf-cts - Windows: <project>/.cache/ppf-cts (project-relative)
- Returns:
Path to the ppf-cts cache directory.
- Return type:
str
Example
Stage a downloaded mesh inside the shared cache directory so later runs can reuse it:
import os from frontend import get_cache_dir cache = get_cache_dir() mesh_path = os.path.join(cache, "fishingknot.ply") print(mesh_path)