Skip to content

Commit 1e05b80

Browse files
authored
Added Cell and Marker Functionality (#4)
* feat: Enhance mesh functionality with polygon support and cell types - Updated `Mesh` interface to include `cellTypes` for VTK compatibility. - Implemented `inferCellTypes` and `getCellTypes` methods in `MeshUtils` for automatic cell type inference. - Added triangulation support for mixed polygons in `MeshUtils`, ensuring compatibility with THREE.js. - Enhanced tests for cell types and triangulation, covering various polygon configurations. - Updated TypeScript documentation to reflect new features and usage examples. * feat: Add support for mesh markers and boundary conditions - Introduced marker support in the Mesh class to define boundary conditions, material regions, and geometric features. - Added methods for automatic conversion between list-of-lists and flattened marker storage. - Implemented marker reconstruction from flattened structures. - Enhanced marker type detection based on element sizes. - Added serialization support for markers during mesh encoding/decoding. - Updated README.md to include examples and documentation for marker usage. - Created new example notebook for working with mesh markers. - Added unit tests for marker functionality, including creation, reconstruction, and serialization. - Updated TypeScript interfaces and utility functions to support marker handling. * feat: Introduce cell type utilities and integrate with Mesh class for improved element size handling * Enhance mesh functionality with automatic marker size and offset calculations - Updated `TestDictArrays` to include new array fields: `index_sizes` and `cell_types`. - Modified `TestIndexSizes` to validate polygon count and inferred sizes for triangles. - Enhanced `TestPydanticMesh` to support polygon structures and validate mesh properties. - Added tests for custom mesh handling, ensuring proper serialization and optimization. - Implemented automatic calculation of `markerSizes` and `markerOffsets` in `MeshUtils`. - Introduced utility functions `inferSizesFromCellTypes` and `sizesToOffsets` for VTK cell type handling. - Updated TypeScript README and tests to reflect new marker auto-calculation features.
1 parent a057e46 commit 1e05b80

16 files changed

Lines changed: 2974 additions & 207 deletions

python/README.md

Lines changed: 221 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ pip install meshly
1414
- `Mesh` class: A Pydantic-based representation of a 3D mesh with methods for optimization and simplification
1515
- Support for custom mesh subclasses with additional attributes
1616
- Automatic encoding/decoding of numpy array attributes, including nested arrays in dictionaries
17-
- Enhanced polygon support with automatic inference of polygon structure from input data
17+
- Enhanced polygon support with automatic `index_sizes` inference and mixed polygon mesh support
18+
- VTK-compatible `cell_types` with automatic inference from polygon structure
1819
- Mesh copying functionality for creating independent copies
1920
- `EncodedMesh` class: A container for encoded mesh data
2021

@@ -41,7 +42,10 @@ pip install meshly
4142
### Advanced Features
4243

4344
- **Nested Array Support**: Automatically encode/decode numpy arrays within nested dictionary structures
44-
- **Flexible Polygon Formats**: Support for triangles, quads, and mixed polygon meshes with automatic structure inference
45+
- **Flexible Polygon Formats**: Support for triangles, quads, and mixed polygon meshes with automatic `index_sizes` inference
46+
- **Index Sizes Management**: Automatic calculation and validation of polygon vertex counts for complex mesh structures
47+
- **VTK Cell Types**: Automatic inference and validation of VTK-compatible cell type identifiers
48+
- **Marker Support**: Define boundary conditions, material regions, and geometric features with automatic conversion between list and flattened formats
4549
- **Deep Copying**: Create independent mesh copies with the [`copy()`](python/meshly/mesh.py:129) method
4650
- **Enhanced Validation**: Automatic validation and conversion of polygon structures and array data
4751

@@ -196,6 +200,7 @@ np.testing.assert_allclose(array, decoded_array)
196200
For more detailed examples, see the Jupyter notebooks in the [examples](examples/) directory:
197201
- [array_example.ipynb](examples/array_example.ipynb): Working with arrays, compression, and file I/O
198202
- [mesh_example.ipynb](examples/mesh_example.ipynb): Working with Pydantic-based meshes, custom subclasses, and serialization
203+
- [markers_example.ipynb](examples/markers_example.ipynb): Working with mesh markers, cell types, and boundary conditions for finite element analysis
199204

200205
## Custom Mesh Subclasses
201206

@@ -256,7 +261,7 @@ Benefits of custom mesh subclasses:
256261

257262
## Enhanced Polygon Support
258263

259-
Meshly provides enhanced support for different polygon types and automatically infers polygon structure:
264+
Meshly provides enhanced support for different polygon types and automatically infers polygon structure through the `index_sizes` field, with optional `cell_types` for VTK compatibility:
260265

261266
```python
262267
# Triangular mesh (traditional format)
@@ -275,17 +280,227 @@ mixed_indices = [
275280
[7, 8, 9, 10, 11] # Pentagon
276281
]
277282

278-
# All formats are automatically handled
283+
# All formats are automatically handled with automatic index_sizes inference
279284
mesh1 = Mesh(vertices=vertices, indices=triangular_indices)
280-
mesh2 = Mesh(vertices=vertices, indices=quad_indices)
281-
mesh3 = Mesh(vertices=vertices, indices=mixed_indices)
285+
mesh2 = Mesh(vertices=vertices, indices=quad_indices) # index_sizes: [4, 4]
286+
mesh3 = Mesh(vertices=vertices, indices=mixed_indices) # index_sizes: [3, 4, 5]
282287

283288
# Access polygon information
284289
print(f"Polygon count: {mesh2.polygon_count}")
285290
print(f"Is uniform: {mesh2.is_uniform_polygons}")
291+
print(f"Index sizes: {mesh2.index_sizes}") # Shows polygon sizes
286292
print(f"Original structure: {mesh2.get_polygon_indices()}")
293+
294+
# You can also explicitly provide index_sizes for validation
295+
flat_indices = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=np.uint32)
296+
explicit_sizes = np.array([3, 4, 2], dtype=np.uint32) # Triangle, quad, line
297+
mesh4 = Mesh(
298+
vertices=vertices,
299+
indices=flat_indices,
300+
index_sizes=explicit_sizes
301+
)
302+
```
303+
304+
### Index Sizes Field
305+
306+
The [`index_sizes`](python/meshly/mesh.py:120) field stores the number of vertices for each polygon and enables support for mixed polygon meshes:
307+
308+
- **Automatic Inference**: When you provide 2D arrays or lists of lists, `index_sizes` is automatically calculated
309+
- **Validation**: When explicitly provided, it validates against the inferred structure
310+
- **Reconstruction**: Used by [`get_polygon_indices()`](python/meshly/mesh.py:172) to recreate the original polygon structure
311+
- **Storage**: Automatically encoded and stored with the mesh data
312+
313+
```python
314+
# Mixed polygon mesh with explicit index_sizes
315+
vertices = np.array([[0,0,0], [1,0,0], [1,1,0], [0,1,0], [0.5,0.5,1]], dtype=np.float32)
316+
indices = np.array([0, 1, 2, 3, 4, 1, 2], dtype=np.uint32) # Quad + triangle
317+
index_sizes = np.array([4, 3], dtype=np.uint32)
318+
319+
mesh = Mesh(vertices=vertices, indices=indices, index_sizes=index_sizes)
320+
321+
# Check polygon structure
322+
print(f"Polygon count: {mesh.polygon_count}") # 2
323+
print(f"Index count: {mesh.index_count}") # 7
324+
print(f"Is uniform: {mesh.is_uniform_polygons}") # False
325+
print(f"Polygons: {mesh.get_polygon_indices()}") # [[0,1,2,3], [4,1,2]]
326+
print(f"Cell types: {mesh.cell_types}") # [9, 5] (VTK_QUAD, VTK_TRIANGLE)
327+
```
328+
329+
### Cell Types Support
330+
331+
The [`cell_types`](python/meshly/mesh.py:129) field provides VTK-compatible cell type identifiers for each polygon, automatically inferred from `index_sizes`:
332+
333+
```python
334+
# Automatic cell type inference
335+
mixed_indices = [
336+
[0], # Vertex
337+
[0, 1], # Line
338+
[0, 1, 2], # Triangle
339+
[0, 1, 2, 3], # Quad
340+
[0, 1, 2, 3, 4] # Pentagon
341+
]
342+
343+
mesh = Mesh(vertices=vertices, indices=mixed_indices)
344+
print(f"Cell types: {mesh.cell_types}") # [1, 3, 5, 9, 14]
345+
346+
# Explicit cell types
347+
explicit_types = [1, 3, 5, 9, 14] # VTK cell type constants
348+
mesh_explicit = Mesh(
349+
vertices=vertices,
350+
indices=mixed_indices,
351+
cell_types=explicit_types
352+
)
353+
354+
# Common VTK cell types:
355+
# 1: VTK_VERTEX, 3: VTK_LINE, 5: VTK_TRIANGLE, 9: VTK_QUAD
356+
# 10: VTK_TETRA, 12: VTK_HEXAHEDRON, 13: VTK_WEDGE, 14: VTK_PYRAMID
287357
```
288358

359+
## Mesh Markers
360+
361+
Meshly provides comprehensive support for mesh markers, which are essential for defining boundary conditions, material regions, and other geometric features in computational meshes:
362+
363+
### Basic Marker Usage
364+
365+
```python
366+
# Create a 2D mesh with boundary markers
367+
vertices = np.array([
368+
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0]
369+
], dtype=np.float32)
370+
371+
indices = np.array([0, 1, 2, 0, 2, 3], dtype=np.uint32)
372+
373+
# Define markers using list-of-lists format (automatically converted)
374+
markers = {
375+
"bottom_edge": [[0, 1]], # Line marker for bottom boundary
376+
"right_edge": [[1, 2]], # Line marker for right boundary
377+
"top_edge": [[2, 3]], # Line marker for top boundary
378+
"left_edge": [[3, 0]], # Line marker for left boundary
379+
"center_triangle": [[0, 1, 2]], # Triangle marker for element region
380+
}
381+
382+
mesh = Mesh(
383+
vertices=vertices,
384+
indices=indices,
385+
markers=markers,
386+
dim=2 # 2D mesh dimension
387+
)
388+
389+
print(f"Markers: {list(mesh.marker_indices.keys())}")
390+
print(f"Boundary elements: {mesh.get_reconstructed_markers()['bottom_edge']}")
391+
```
392+
393+
### Marker Storage and Efficiency
394+
395+
Markers are stored internally using an efficient flattened format that supports variable-sized elements:
396+
397+
```python
398+
# Access flattened marker structure
399+
for name, indices in mesh.marker_indices.items():
400+
offsets = mesh.marker_offsets[name]
401+
types = mesh.marker_cell_types[name]
402+
403+
print(f"{name}:")
404+
print(f" Flattened indices: {indices}")
405+
print(f" Element offsets: {offsets}")
406+
print(f" VTK cell types: {types}")
407+
408+
# Reconstruct original list format when needed
409+
original_format = mesh.get_reconstructed_markers()
410+
```
411+
412+
### Advanced Marker Features
413+
414+
```python
415+
# Mixed marker types in a single mesh
416+
mixed_markers = {
417+
"boundary_vertices": [[0], [2]], # Vertex markers (VTK type 1)
418+
"boundary_edges": [[0, 1], [1, 2]], # Line markers (VTK type 3)
419+
"material_regions": [[0, 1, 4], [2, 3, 4]], # Triangle markers (VTK type 5)
420+
"interface_quads": [[1, 2, 5, 4]], # Quad markers (VTK type 9)
421+
}
422+
423+
advanced_mesh = Mesh(
424+
vertices=vertices,
425+
indices=mixed_indices,
426+
markers=mixed_markers,
427+
dim=2
428+
)
429+
430+
# Automatic VTK cell type detection
431+
print(f"Marker types detected: {advanced_mesh.marker_cell_types}")
432+
```
433+
434+
### Custom Mesh Classes with Markers
435+
436+
```python
437+
class FiniteElementMesh(Mesh):
438+
"""Mesh with finite element analysis features."""
439+
440+
# Material properties for different regions
441+
material_properties: Dict[str, Dict[str, float]] = Field(default_factory=dict)
442+
443+
# Boundary condition specifications
444+
boundary_conditions: Dict[str, Dict[str, any]] = Field(default_factory=dict)
445+
446+
def get_boundary_elements(self, boundary_name: str) -> List[List[int]]:
447+
"""Get elements on a specific boundary."""
448+
return self.get_reconstructed_markers().get(boundary_name, [])
449+
450+
# Create FEM mesh with materials and boundary conditions
451+
fem_mesh = FiniteElementMesh(
452+
vertices=vertices,
453+
indices=indices,
454+
markers={
455+
"dirichlet_bc": [[0, 3]], # Fixed displacement boundary
456+
"neumann_bc": [[1, 2]], # Applied force boundary
457+
"material_steel": [[0, 1, 4]], # Steel region
458+
"material_aluminum": [[2, 3, 4]], # Aluminum region
459+
},
460+
material_properties={
461+
"steel": {"young_modulus": 200e9, "poisson_ratio": 0.3},
462+
"aluminum": {"young_modulus": 70e9, "poisson_ratio": 0.33},
463+
},
464+
boundary_conditions={
465+
"dirichlet_bc": {"type": "displacement", "value": [0.0, 0.0]},
466+
"neumann_bc": {"type": "force", "value": [1000.0, 0.0]},
467+
}
468+
)
469+
```
470+
471+
### Marker Serialization
472+
473+
Markers are fully preserved during mesh encoding/decoding and file I/O:
474+
475+
```python
476+
# Encode mesh with markers
477+
encoded = MeshUtils.encode(fem_mesh)
478+
print(f"Encoded marker arrays: {[k for k in encoded.arrays.keys() if 'marker' in k]}")
479+
480+
# Decode preserves all marker data
481+
decoded = MeshUtils.decode(FiniteElementMesh, encoded)
482+
assert fem_mesh.get_reconstructed_markers() == decoded.get_reconstructed_markers()
483+
484+
# ZIP file serialization also preserves markers
485+
MeshUtils.save_to_zip(fem_mesh, "fem_mesh.zip")
486+
loaded = MeshUtils.load_from_zip(FiniteElementMesh, "fem_mesh.zip")
487+
assert loaded.material_properties == fem_mesh.material_properties
488+
```
489+
490+
Key marker features:
491+
- **Automatic conversion** between list-of-lists and efficient flattened storage
492+
- **VTK compatibility** with standard cell type identifiers
493+
- **Mixed element types** (points, lines, triangles, quads) in a single marker set
494+
- **Type validation** ensures only supported element sizes (1-4 vertices)
495+
- **Full serialization** support with encoding/decoding and ZIP file I/O
496+
- **Easy reconstruction** back to list format for processing algorithms
497+
498+
Common use cases:
499+
- **Finite element analysis**: Boundary conditions and material regions
500+
- **Computational fluid dynamics**: Inlet/outlet boundaries and wall conditions
501+
- **Mesh processing**: Feature identification and region marking
502+
- **Visualization**: Highlighting specific mesh regions or boundaries
503+
289504
## Mesh Copying
290505

291506
Create independent copies of meshes with the [`copy()`](python/meshly/mesh.py:129) method:

0 commit comments

Comments
 (0)