Constructor
Domain(*args, **kwargs)
Parameters
| Parameter | Type | Description |
|---|---|---|
*args |
variable |
Can be:
|
**kwargs |
dict | Additional keyword arguments passed to data processing methods |
Attributes
| Attribute | Type | Description |
|---|---|---|
points |
np.ndarray | Point cloud defining the domain (n×d array) |
normals |
np.ndarray | Normal vectors at each point (n×d array) |
boundary |
pv.PolyData | Boundary mesh of the domain |
mesh |
pv.UnstructuredGrid | Interior tetrahedral/triangular mesh |
patches |
list | List of Patch objects for implicit function |
functions |
list | List of implicit functions |
n |
int | Number of points |
d |
int | Spatial dimension (2 or 3) |
characteristic_length |
float | Characteristic length scale of the domain |
area |
float | Surface area (3D) or perimeter (2D) |
volume |
float | Volume (3D) or area (2D) |
convexity |
float | Ratio of domain volume to convex hull volume |
Methods
Core Methods
set_data(*args, **kwargs)
Set the data for the domain from point-wise data or a PyVista object.
Parameters
*args: Points array, points and normals arrays, or PyVista object**kwargs: Additional processing parameters
create(min_patch_size=10, max_patch_size=20, overlap=0.2, feature_angle=30)
Create patches for the domain. This is the first step in building the implicit function.
Parameters
min_patch_size(int, default=10): Minimum number of points required to form a patch.max_patch_size(int, default=20): Target maximum number of points per patch (nearest-neighbor window).overlap(float, default=0.2): Fraction [0, 1] controlling allowed overlap of points between patches.feature_angle(float, default=30): Maximum angle in degrees between point-wise normal vectors for inclusion in the same patch (used only when normals are provided).
solve(method="L-BFGS-B", precision=9)
Solve the individual patch interpolation problems prior to blending.
Parameters
method(str, default="L-BFGS-B"): Optimization method for SciPy's minimize (see Patch.solve).precision(int, default=9): Decimal places to round the solved constants.
build(resolution=25, skip_boundary=False)
Build the implicit function describing the domain and optionally extract boundary/mesh artifacts.
Parameters
resolution(int, default=25): Grid resolution for boundary extraction.skip_boundary(bool, default=False): If true, only assemble fast-evaluation structures and skip boundary and interior mesh generation.
Evaluation Methods
__call__(points, **kwargs)
Evaluate the implicit function at given points.
Parameters
points(np.ndarray): Points to evaluate (m×d array)k(int, default=1): Number of nearest patches to considernormalize(bool, default=True): Normalize output valuestolerance(float): Tolerance for patch selection
Returns
values(np.ndarray): Implicit function values (m×1 array)
within(points, level=0, **kwargs)
Determine if points are within the domain.
Returns
inside(np.ndarray): Boolean array indicating if points are inside
Mesh Generation
get_boundary(resolution, **kwargs)
Extract the boundary surface of the domain.
Parameters
resolution(int): Grid resolution for marching cubes/squaresget_largest(bool, default=True): Reserved (current implementation always returns the largest connected component)
Returns
boundary(pv.PolyData): Boundary meshgrid(pv.ImageData): Volumetric grid used for extraction
get_interior(verbose=False, **kwargs)
Generate tetrahedral (3D) or triangular (2D) mesh of the interior.
Parameters
verbose(bool): Print mesh generation progress**kwargs: Parameters passed to TetGen/Triangle
Returns
mesh(pv.UnstructuredGrid): Interior mesh
Point Sampling
get_interior_points(n, tree=None, volume_threshold=None, threshold=None, method=None, implicit_range=(-1.0, 0.0), **kwargs)
Sample random points from the domain interior.
Parameters
n(int): Number of points to sampletree(KDTree, optional): Existing tree structure for collision detectionvolume_threshold(float, optional): Maximum distance from treethreshold(float, optional): Minimum distance from treemethod(str, optional): Sampling method ('implicit_only', 'preallocate', or default)implicit_range(tuple): Range of implicit function values to accept
Returns
points(np.ndarray): Sampled points (n×d array)cells(np.ndarray): Cell indices for each point
get_boundary_points(n, method=None, **kwargs)
Sample random points from the domain boundary.
Parameters
n(int): Number of points to samplemethod(str, optional): Sampling method
Returns
points(np.ndarray): Sampled boundary points (n×d array)
Utility Methods
set_random_seed(seed)
Set the random seed for reproducible sampling.
set_random_generator()
Initialize the random number generator with the set seed.
Persistence (Save/Load)
Domains can be saved to and loaded from .dmn files, allowing you to preserve
expensive implicit function computations and share domain configurations.
save(path, include_boundary=False, include_mesh=False, include_patch_normals=True)
Save the domain to a .dmn file.
Parameters
path(str): Output filename. If no extension is provided,.dmnis appended.include_boundary(bool): Persist the boundary mesh for visualization without recomputation. Default is False.include_mesh(bool): Persist the interior mesh for sampling without recomputation. Default is False.include_patch_normals(bool): Include per-patch normals to fully restore Patch objects. Default is True.
Notes
The saved file contains the implicit function coefficients and patch data needed to reconstruct the domain. Including boundary and mesh data increases file size but speeds up subsequent operations.
Domain.load(path) classmethod
Load a domain from a .dmn file.
Parameters
path(str): Path to a.dmnfile.
Returns
Domain: Fully initialized Domain instance ready for fast evaluation.
Notes
The loaded domain has its function_tree rebuilt and is ready for use
with __call__ (fast evaluation). Patches are reconstructed from stored
arrays, enabling you to call build() again if needed.
Save/Load Example
from svv.domain.domain import Domain
import pyvista as pv
# Create domain from a complex mesh
mesh = pv.read("heart_ventricle.stl")
domain = Domain(mesh)
domain.create() # Creates patches (can be slow for complex meshes)
domain.solve() # Solves RBF system (expensive)
domain.build() # Builds boundary and mesh
# Save domain for later use
domain.save("ventricle.dmn", include_boundary=True, include_mesh=True)
# Later, load the domain instantly
loaded_domain = Domain.load("ventricle.dmn")
# Use immediately - no need to call create/solve/build
point = [[0.5, 0.5, 0.5]]
inside = loaded_domain(point) < 0 # Fast evaluation works immediately
# Or call build() if you need fresh boundary/mesh
loaded_domain.build()
include_boundary=True
and include_mesh=True significantly speeds up subsequent loads at the cost of
larger file sizes. This is especially useful when iterating on tree generation with the same domain.
Operators
domain1 + domain2
Union of two domains (set union operation).
# Create union of two domains
combined_domain = domain1 + domain2
domain1 - domain2
Difference of two domains (set difference operation).
# Subtract domain2 from domain1
difference_domain = domain1 - domain2
Examples
Creating a Domain from STL File
import pyvista as pv
from svv.domain.domain import Domain
# Load STL file
mesh = pv.read('vessel.stl')
# Create domain
domain = Domain(mesh)
# Build implicit function with custom resolution
domain.create()
domain.solve()
domain.build(resolution=30)
# Get mesh statistics
print(f"Domain volume: {domain.volume:.2f}")
print(f"Domain surface area: {domain.area:.2f}")
print(f"Convexity: {domain.convexity:.3f}")
Customized Patch Allocation
import pyvista as pv
from svv.domain.domain import Domain
mesh = pv.read('vessel.stl')
domain = Domain(mesh)
# Tune patch allocation for dense data with noisy normals
domain.create(
min_patch_size=8, # allow smaller patches in sparse areas
max_patch_size=32, # include more neighbors in dense regions
overlap=0.35, # increase overlap for smoother blending
feature_angle=45 # be more tolerant to normal variation/noise
)
domain.solve()
domain.build(resolution=30)
- Dense point clouds: increase
max_patch_sizeto capture local context and reduce edge artifacts. - Sparse/heterogeneous sampling: decrease
min_patch_sizeso isolated regions still form patches. - Noisy or inconsistent normals: raise
feature_angleto admit neighbors with larger normal differences; lower it to preserve sharp features. - Smoother blends vs. speed: higher
overlapyields smoother transitions across patches but increases compute.
Boolean Operations on Domains
# Create two domains
sphere1 = pv.Sphere(center=(0, 0, 0), radius=1.0)
domain1 = Domain(sphere1)
domain1.create()
domain1.solve()
domain1.build()
sphere2 = pv.Sphere(center=(0.5, 0, 0), radius=0.8)
domain2 = Domain(sphere2)
domain2.create()
domain2.solve()
domain2.build()
# Create union (merged domains)
union_domain = domain1 + domain2
# Create difference (domain1 with hole from domain2)
diff_domain = domain1 - domain2
# Evaluate at test points
test_points = np.random.randn(100, 3)
union_values = union_domain(test_points)
diff_values = diff_domain(test_points)
Sampling Points with Collision Avoidance
from scipy.spatial import cKDTree
# Create domain
domain = Domain(mesh)
domain.create()
domain.solve()
domain.build()
# Create existing tree structure (e.g., from vessel centerlines)
existing_points = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]])
tree = cKDTree(existing_points)
# Sample points avoiding existing structure
new_points, cells = domain.get_interior_points(
n=1000,
tree=tree,
threshold=0.5, # Minimum distance from existing points
volume_threshold=5.0 # Maximum distance from existing points
)
# Sample boundary points
boundary_points = domain.get_boundary_points(n=500)
Notes
Implicit Function: The domain uses radial basis function (RBF) interpolation to create a smooth implicit function representation. Points inside the domain have negative values, points outside have positive values, and the boundary is at the zero level set.
Memory Usage: For large point clouds or high-resolution meshes, the implicit function construction can be memory-intensive. Consider using lower resolution for initial testing.
Performance: The domain uses optimized Cython routines for point sampling (c_sample.pyx) and allocation (c_allocate.pyx). These provide significant speedups for large-scale operations.
create() → solve() → build() to properly construct the implicit function before using evaluation or sampling methods.
See Also
svv.domain.Patch- Individual patch for RBF interpolationsvv.tree.Tree- Vascular tree generation using domainssvv.forest.Forest- Collection of trees within a domain