mmgpy 0.5.0__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mmgpy/__init__.py +296 -0
- mmgpy/__main__.py +13 -0
- mmgpy/_io.py +535 -0
- mmgpy/_logging.py +290 -0
- mmgpy/_mesh.py +2286 -0
- mmgpy/_mmgpy.cpython-311-x86_64-linux-gnu.so +0 -0
- mmgpy/_mmgpy.pyi +2140 -0
- mmgpy/_options.py +304 -0
- mmgpy/_progress.py +850 -0
- mmgpy/_pyvista.py +410 -0
- mmgpy/_result.py +143 -0
- mmgpy/_transfer.py +273 -0
- mmgpy/_validation.py +669 -0
- mmgpy/_version.py +3 -0
- mmgpy/_version.py.in +3 -0
- mmgpy/bin/mmg2d_O3 +0 -0
- mmgpy/bin/mmg3d_O3 +0 -0
- mmgpy/bin/mmgs_O3 +0 -0
- mmgpy/interactive/__init__.py +24 -0
- mmgpy/interactive/sizing_editor.py +790 -0
- mmgpy/lagrangian.py +394 -0
- mmgpy/lib/libmmg2d.so +0 -0
- mmgpy/lib/libmmg2d.so.5 +0 -0
- mmgpy/lib/libmmg2d.so.5.8.0 +0 -0
- mmgpy/lib/libmmg3d.so +0 -0
- mmgpy/lib/libmmg3d.so.5 +0 -0
- mmgpy/lib/libmmg3d.so.5.8.0 +0 -0
- mmgpy/lib/libmmgs.so +0 -0
- mmgpy/lib/libmmgs.so.5 +0 -0
- mmgpy/lib/libmmgs.so.5.8.0 +0 -0
- mmgpy/lib/libvtkCommonColor-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonComputationalGeometry-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonDataModel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonExecutionModel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonMath-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonMisc-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonSystem-9.5.so.1 +0 -0
- mmgpy/lib/libvtkCommonTransforms-9.5.so.1 +0 -0
- mmgpy/lib/libvtkDICOMParser-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersCellGrid-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersExtraction-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersGeneral-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersGeometry-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersHybrid-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersHyperTree-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersModeling-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersParallel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersReduction-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersSources-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersStatistics-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersTexture-9.5.so.1 +0 -0
- mmgpy/lib/libvtkFiltersVerdict-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOCellGrid-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOGeometry-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOImage-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOLegacy-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOParallel-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOParallelXML-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOXML-9.5.so.1 +0 -0
- mmgpy/lib/libvtkIOXMLParser-9.5.so.1 +0 -0
- mmgpy/lib/libvtkImagingCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkImagingSources-9.5.so.1 +0 -0
- mmgpy/lib/libvtkParallelCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkParallelDIY-9.5.so.1 +0 -0
- mmgpy/lib/libvtkRenderingCore-9.5.so.1 +0 -0
- mmgpy/lib/libvtkdoubleconversion-9.5.so.1 +0 -0
- mmgpy/lib/libvtkexpat-9.5.so.1 +0 -0
- mmgpy/lib/libvtkfmt-9.5.so.1 +0 -0
- mmgpy/lib/libvtkjpeg-9.5.so.1 +0 -0
- mmgpy/lib/libvtkjsoncpp-9.5.so.1 +0 -0
- mmgpy/lib/libvtkkissfft-9.5.so.1 +0 -0
- mmgpy/lib/libvtkloguru-9.5.so.1 +0 -0
- mmgpy/lib/libvtklz4-9.5.so.1 +0 -0
- mmgpy/lib/libvtklzma-9.5.so.1 +0 -0
- mmgpy/lib/libvtkmetaio-9.5.so.1 +0 -0
- mmgpy/lib/libvtkpng-9.5.so.1 +0 -0
- mmgpy/lib/libvtkpugixml-9.5.so.1 +0 -0
- mmgpy/lib/libvtksys-9.5.so.1 +0 -0
- mmgpy/lib/libvtktiff-9.5.so.1 +0 -0
- mmgpy/lib/libvtktoken-9.5.so.1 +0 -0
- mmgpy/lib/libvtkverdict-9.5.so.1 +0 -0
- mmgpy/lib/libvtkzlib-9.5.so.1 +0 -0
- mmgpy/metrics.py +596 -0
- mmgpy/progress.py +69 -0
- mmgpy/py.typed +0 -0
- mmgpy/repair/__init__.py +37 -0
- mmgpy/repair/_core.py +226 -0
- mmgpy/repair/_elements.py +241 -0
- mmgpy/repair/_vertices.py +219 -0
- mmgpy/sizing.py +370 -0
- mmgpy/ui/__init__.py +97 -0
- mmgpy/ui/__main__.py +87 -0
- mmgpy/ui/app.py +1837 -0
- mmgpy/ui/parsers.py +501 -0
- mmgpy/ui/remeshing.py +448 -0
- mmgpy/ui/samples.py +249 -0
- mmgpy/ui/utils.py +280 -0
- mmgpy/ui/viewer.py +587 -0
- mmgpy-0.5.0.dist-info/METADATA +186 -0
- mmgpy-0.5.0.dist-info/RECORD +109 -0
- mmgpy-0.5.0.dist-info/WHEEL +6 -0
- mmgpy-0.5.0.dist-info/entry_points.txt +13 -0
- mmgpy-0.5.0.dist-info/licenses/LICENSE +38 -0
- share/man/man1/mmg2d.1.gz +0 -0
- share/man/man1/mmg3d.1.gz +0 -0
- share/man/man1/mmgs.1.gz +0 -0
mmgpy/__init__.py
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""Python bindings for the MMG library."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import site
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from ._logging import (
|
|
11
|
+
configure_logging,
|
|
12
|
+
disable_logging,
|
|
13
|
+
enable_debug,
|
|
14
|
+
get_log_file,
|
|
15
|
+
get_logger,
|
|
16
|
+
set_log_file,
|
|
17
|
+
set_log_level,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_logger = get_logger()
|
|
21
|
+
|
|
22
|
+
# Version info
|
|
23
|
+
try:
|
|
24
|
+
from . import _version # type: ignore[attr-defined]
|
|
25
|
+
|
|
26
|
+
__version__ = _version.__version__
|
|
27
|
+
except ImportError:
|
|
28
|
+
__version__ = "unknown"
|
|
29
|
+
|
|
30
|
+
# Core C++ bindings
|
|
31
|
+
from ._mmgpy import ( # type: ignore[attr-defined]
|
|
32
|
+
MMG_VERSION,
|
|
33
|
+
mmg2d, # noqa: F401 # Available for advanced users
|
|
34
|
+
mmg3d, # noqa: F401 # Available for advanced users
|
|
35
|
+
mmgs, # noqa: F401 # Available for advanced users
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _find_mmg_executable(base_name: str) -> str | None: # pragma: no cover
|
|
40
|
+
"""Find an MMG executable in mmgpy/bin or venv bin directory.
|
|
41
|
+
|
|
42
|
+
Note: We do NOT use shutil.which() because it would find the Python
|
|
43
|
+
entry point scripts in venv/bin/, causing infinite recursion.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
base_name: Base name of executable (e.g., "mmg3d_O3")
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Full path to executable, or None if not found
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
exe_name = f"{base_name}.exe" if sys.platform == "win32" else base_name
|
|
53
|
+
|
|
54
|
+
# Check mmgpy/bin relative to this package (works for wheel installs)
|
|
55
|
+
package_bin = Path(__file__).parent / "bin" / exe_name
|
|
56
|
+
if package_bin.exists():
|
|
57
|
+
return str(package_bin)
|
|
58
|
+
|
|
59
|
+
# Fall back to mmgpy/bin in site-packages (for editable installs)
|
|
60
|
+
site_packages_list = site.getsitepackages()
|
|
61
|
+
# On Windows, prefer the actual site-packages over the venv root
|
|
62
|
+
if sys.platform == "win32" and len(site_packages_list) > 1:
|
|
63
|
+
site_packages = Path(site_packages_list[1])
|
|
64
|
+
else:
|
|
65
|
+
site_packages = Path(site_packages_list[0])
|
|
66
|
+
|
|
67
|
+
exe_path = site_packages / "mmgpy" / "bin" / exe_name
|
|
68
|
+
if exe_path.exists():
|
|
69
|
+
return str(exe_path)
|
|
70
|
+
|
|
71
|
+
# Check venv bin/Scripts directory (executables copied there by CMake)
|
|
72
|
+
# This is the fallback for editable installs where CMake copies executables
|
|
73
|
+
venv_bin_name = "Scripts" if sys.platform == "win32" else "bin"
|
|
74
|
+
venv_bin = Path(sys.prefix) / venv_bin_name / exe_name
|
|
75
|
+
# Only use if it's an actual executable (not a Python entry point script)
|
|
76
|
+
# Native executables are typically larger than 1KB (Python scripts are ~300 bytes)
|
|
77
|
+
min_native_exe_size = 1024
|
|
78
|
+
if venv_bin.exists() and venv_bin.stat().st_size > min_native_exe_size:
|
|
79
|
+
return str(venv_bin)
|
|
80
|
+
|
|
81
|
+
# For editable installs, check the scikit-build-core build directory
|
|
82
|
+
# The build directory is typically at the project root (parent of src/)
|
|
83
|
+
package_dir = Path(__file__).parent
|
|
84
|
+
if "src" in str(package_dir):
|
|
85
|
+
# Editable install - look in build directory
|
|
86
|
+
project_root = package_dir.parent.parent # src/mmgpy -> src -> project_root
|
|
87
|
+
build_dir = project_root / "build"
|
|
88
|
+
if build_dir.exists():
|
|
89
|
+
# Search for executable in any build subdirectory
|
|
90
|
+
for build_subdir in build_dir.iterdir():
|
|
91
|
+
if build_subdir.is_dir():
|
|
92
|
+
exe_path = build_subdir / "mmgpy" / "bin" / exe_name
|
|
93
|
+
if exe_path.exists():
|
|
94
|
+
return str(exe_path)
|
|
95
|
+
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _run_mmg2d() -> None: # pragma: no cover
|
|
100
|
+
"""Run the mmg2d_O3 executable."""
|
|
101
|
+
exe_path = _find_mmg_executable("mmg2d_O3")
|
|
102
|
+
if exe_path:
|
|
103
|
+
subprocess.run([exe_path, *sys.argv[1:]], check=False)
|
|
104
|
+
else:
|
|
105
|
+
_logger.error("mmg2d_O3 executable not found in PATH or mmgpy/bin")
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _run_mmg3d() -> None: # pragma: no cover
|
|
110
|
+
"""Run the mmg3d_O3 executable."""
|
|
111
|
+
exe_path = _find_mmg_executable("mmg3d_O3")
|
|
112
|
+
if exe_path:
|
|
113
|
+
subprocess.run([exe_path, *sys.argv[1:]], check=False)
|
|
114
|
+
else:
|
|
115
|
+
_logger.error("mmg3d_O3 executable not found in PATH or mmgpy/bin")
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _run_mmgs() -> None: # pragma: no cover
|
|
120
|
+
"""Run the mmgs_O3 executable."""
|
|
121
|
+
exe_path = _find_mmg_executable("mmgs_O3")
|
|
122
|
+
if exe_path:
|
|
123
|
+
subprocess.run([exe_path, *sys.argv[1:]], check=False)
|
|
124
|
+
else:
|
|
125
|
+
_logger.error("mmgs_O3 executable not found in PATH or mmgpy/bin")
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _run_mmg() -> None: # pragma: no cover
|
|
130
|
+
"""Run the appropriate mmg executable based on auto-detected mesh type.
|
|
131
|
+
|
|
132
|
+
This unified command automatically detects the mesh type from the input file
|
|
133
|
+
and delegates to the appropriate mmg2d_O3, mmg3d_O3, or mmgs_O3 executable.
|
|
134
|
+
"""
|
|
135
|
+
import meshio # noqa: PLC0415
|
|
136
|
+
|
|
137
|
+
from ._io import _detect_mesh_kind # noqa: PLC0415
|
|
138
|
+
from ._mesh import MeshKind # noqa: PLC0415
|
|
139
|
+
|
|
140
|
+
args = sys.argv[1:]
|
|
141
|
+
if not args or args[0] in ("-h", "--help"):
|
|
142
|
+
print( # noqa: T201
|
|
143
|
+
"mmg - Unified mesh remeshing tool with auto-detection\n\n"
|
|
144
|
+
"Usage: mmg <input_mesh> [options]\n\n"
|
|
145
|
+
"This command automatically detects the mesh type and delegates to:\n"
|
|
146
|
+
" - mmg2d (or mmg2d_O3) for 2D planar meshes (triangles with z~=0)\n"
|
|
147
|
+
" - mmg3d (or mmg3d_O3) for 3D volumetric meshes (tetrahedra)\n"
|
|
148
|
+
" - mmgs (or mmgs_O3) for 3D surface meshes (triangles in 3D space)\n\n"
|
|
149
|
+
"All standard mmg options are passed through to the executable.\n"
|
|
150
|
+
"Run 'mmg3d -h', 'mmg2d -h', or 'mmgs -h' for specific options.",
|
|
151
|
+
)
|
|
152
|
+
sys.exit(0)
|
|
153
|
+
|
|
154
|
+
if args[0] in ("-v", "--version"):
|
|
155
|
+
print(f"mmgpy {__version__}") # noqa: T201
|
|
156
|
+
print(f"MMG {MMG_VERSION}") # noqa: T201
|
|
157
|
+
sys.exit(0)
|
|
158
|
+
|
|
159
|
+
# MMG flags that take an argument (skip the next arg when detecting input file)
|
|
160
|
+
flags_with_args = {
|
|
161
|
+
"-o",
|
|
162
|
+
"-out",
|
|
163
|
+
"-sol",
|
|
164
|
+
"-met",
|
|
165
|
+
"-ls",
|
|
166
|
+
"-lag",
|
|
167
|
+
"-ar",
|
|
168
|
+
"-nr",
|
|
169
|
+
"-hmin",
|
|
170
|
+
"-hmax",
|
|
171
|
+
"-hsiz",
|
|
172
|
+
"-hausd",
|
|
173
|
+
"-hgrad",
|
|
174
|
+
"-hgradreq",
|
|
175
|
+
"-m",
|
|
176
|
+
"-v",
|
|
177
|
+
"-xreg",
|
|
178
|
+
"-nreg",
|
|
179
|
+
"-nsd",
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Find the input mesh file (first positional argument that's a file)
|
|
183
|
+
input_mesh = None
|
|
184
|
+
skip_next = False
|
|
185
|
+
for arg in args:
|
|
186
|
+
if skip_next:
|
|
187
|
+
skip_next = False
|
|
188
|
+
continue
|
|
189
|
+
if arg in flags_with_args:
|
|
190
|
+
skip_next = True
|
|
191
|
+
continue
|
|
192
|
+
if not arg.startswith("-") and Path(arg).exists():
|
|
193
|
+
input_mesh = arg
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
if input_mesh is None:
|
|
197
|
+
_logger.error("No input mesh file found in arguments")
|
|
198
|
+
sys.exit(1)
|
|
199
|
+
|
|
200
|
+
# Detect mesh type
|
|
201
|
+
try:
|
|
202
|
+
meshio_mesh = meshio.read(input_mesh)
|
|
203
|
+
mesh_kind = _detect_mesh_kind(meshio_mesh)
|
|
204
|
+
except Exception:
|
|
205
|
+
_logger.exception(
|
|
206
|
+
"Failed to detect mesh type from '%s'. "
|
|
207
|
+
"Try using a specific command instead: mmg3d, mmg2d, or mmgs",
|
|
208
|
+
input_mesh,
|
|
209
|
+
)
|
|
210
|
+
sys.exit(1)
|
|
211
|
+
|
|
212
|
+
# Map mesh kind to executable
|
|
213
|
+
exe_map = {
|
|
214
|
+
MeshKind.TETRAHEDRAL: ("mmg3d_O3", _run_mmg3d),
|
|
215
|
+
MeshKind.TRIANGULAR_2D: ("mmg2d_O3", _run_mmg2d),
|
|
216
|
+
MeshKind.TRIANGULAR_SURFACE: ("mmgs_O3", _run_mmgs),
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
exe_name, run_func = exe_map[mesh_kind]
|
|
220
|
+
_logger.info("Detected %s mesh, using %s", mesh_kind.value, exe_name)
|
|
221
|
+
|
|
222
|
+
# Delegate to the appropriate executable
|
|
223
|
+
run_func()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
from . import interactive, lagrangian, metrics, progress, repair, sizing
|
|
227
|
+
from ._io import read
|
|
228
|
+
from ._mesh import Mesh, MeshCheckpoint, MeshKind
|
|
229
|
+
from ._options import Mmg2DOptions, Mmg3DOptions, MmgSOptions
|
|
230
|
+
from ._progress import CancellationError, ProgressEvent, rich_progress
|
|
231
|
+
from ._pyvista import from_pyvista, to_pyvista
|
|
232
|
+
from ._result import RemeshResult
|
|
233
|
+
from ._transfer import interpolate_field, transfer_fields
|
|
234
|
+
from ._validation import (
|
|
235
|
+
IssueSeverity,
|
|
236
|
+
QualityStats,
|
|
237
|
+
ValidationError,
|
|
238
|
+
ValidationIssue,
|
|
239
|
+
ValidationReport,
|
|
240
|
+
)
|
|
241
|
+
from .lagrangian import detect_boundary_vertices, move_mesh, propagate_displacement
|
|
242
|
+
from .sizing import (
|
|
243
|
+
BoxSize,
|
|
244
|
+
CylinderSize,
|
|
245
|
+
PointSize,
|
|
246
|
+
SizingConstraint,
|
|
247
|
+
SphereSize,
|
|
248
|
+
apply_sizing_constraints,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
__all__ = [
|
|
252
|
+
"MMG_VERSION",
|
|
253
|
+
"BoxSize",
|
|
254
|
+
"CancellationError",
|
|
255
|
+
"CylinderSize",
|
|
256
|
+
"IssueSeverity",
|
|
257
|
+
"Mesh",
|
|
258
|
+
"MeshCheckpoint",
|
|
259
|
+
"MeshKind",
|
|
260
|
+
"Mmg2DOptions",
|
|
261
|
+
"Mmg3DOptions",
|
|
262
|
+
"MmgSOptions",
|
|
263
|
+
"PointSize",
|
|
264
|
+
"ProgressEvent",
|
|
265
|
+
"QualityStats",
|
|
266
|
+
"RemeshResult",
|
|
267
|
+
"SizingConstraint",
|
|
268
|
+
"SphereSize",
|
|
269
|
+
"ValidationError",
|
|
270
|
+
"ValidationIssue",
|
|
271
|
+
"ValidationReport",
|
|
272
|
+
"__version__",
|
|
273
|
+
"apply_sizing_constraints",
|
|
274
|
+
"configure_logging",
|
|
275
|
+
"detect_boundary_vertices",
|
|
276
|
+
"disable_logging",
|
|
277
|
+
"enable_debug",
|
|
278
|
+
"from_pyvista",
|
|
279
|
+
"get_log_file",
|
|
280
|
+
"get_logger",
|
|
281
|
+
"interactive",
|
|
282
|
+
"interpolate_field",
|
|
283
|
+
"lagrangian",
|
|
284
|
+
"metrics",
|
|
285
|
+
"move_mesh",
|
|
286
|
+
"progress",
|
|
287
|
+
"propagate_displacement",
|
|
288
|
+
"read",
|
|
289
|
+
"repair",
|
|
290
|
+
"rich_progress",
|
|
291
|
+
"set_log_file",
|
|
292
|
+
"set_log_level",
|
|
293
|
+
"sizing",
|
|
294
|
+
"to_pyvista",
|
|
295
|
+
"transfer_fields",
|
|
296
|
+
]
|
mmgpy/__main__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Entry point for running mmgpy as a module.
|
|
2
|
+
|
|
3
|
+
This enables usage like:
|
|
4
|
+
python -m mmgpy mesh.stl -o remeshed.stl
|
|
5
|
+
uvx mmgpy mesh.stl -o remeshed.stl
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from . import _run_mmg
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
_run_mmg()
|