diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/.DS_Store differ diff --git a/gyroid_gd_bunny.py b/gyroid_gd_bunny.py new file mode 100644 index 00000000..1ff11514 --- /dev/null +++ b/gyroid_gd_bunny.py @@ -0,0 +1,82 @@ +import numpy as np +import pyvista as pv +from pyvista import examples + + +def gyroid(x, y, z): + return np.sin(x) * np.cos(y) + np.sin(y) * np.cos(z) + np.sin(z) * np.cos(x) + + +repeat_cell = np.array([8, 8, 8]) +cell_size = np.array([1, 1, 1]) +center_offset = 0.5 +resolution = 15 + +offset = np.pi / 2.0 + +linspaces: list[np.ndarray] = [] +for repeat_cell_axis, cell_size_axis in zip(repeat_cell, cell_size, strict=False): + linspaces.append( + np.linspace( + -center_offset * cell_size_axis * repeat_cell_axis, + center_offset * cell_size_axis * repeat_cell_axis, + resolution * repeat_cell_axis, + ), + ) + +x, y, z = np.meshgrid(*linspaces) + +grid = pv.StructuredGrid(x, y, z) +kx, ky, kz = 2 * np.pi / cell_size + +surface_function = gyroid(kx * x, ky * y, kz * z) + +bunny = examples.download_bunny() +transform_matrix = np.array([[40, 0, 0, 0], [0, 40, 0, 0], [0, 0, 40, 0], [0, 0, 0, 1]]) +bunny.transform(transform_matrix, inplace=True) +center = bunny.center_of_mass() +bunny.translate(-center, inplace=True) +print("newcenter = ", bunny.center_of_mass()) +print(bunny.bounds) +print(grid.bounds) + +grid.compute_implicit_distance(bunny, inplace=True) + +# normalize : +dist = -1.0 * grid["implicit_distance"] +dist[dist > 0] = 0 +dist_norm = (dist - min(dist)) / (max(dist) - min(dist)) +x_t = 0.5 +length = 0.2 +reg_func = 0.6 * (1.0 + np.tanh((dist_norm - x_t) / length)) - 0.2 + +print(min(dist)) +print(max(dist)) + +print(min(dist_norm)) +print(max(dist_norm)) + +print(min(reg_func)) +print(max(reg_func)) + +grid["lower_surface"] = surface_function.ravel(order="F") - offset * reg_func +grid["upper_surface"] = surface_function.ravel(order="F") + offset * reg_func +sheet = grid.clip_scalar(scalars="upper_surface", invert=False).clip_scalar( + scalars="lower_surface", +) +upper_skeletal = grid.clip_scalar(scalars="upper_surface") +lower_skeletal = grid.clip_scalar(scalars="lower_surface", invert=False) + +clipped = sheet.clip_surface(bunny, invert=False) +clipped.compute_implicit_distance(bunny, inplace=True) + +print(f"relative density = {clipped.volume / bunny.volume:.2%}") + +clipped2 = clipped.clip("y", origin=(0, -0.5, 0.0), invert=False) +clipped2["dist"] = -1.0 * clipped2["implicit_distance"] + +pl = pv.Plotter() +# pl.add_mesh(grid, color="b", opacity = 0.1) +pl.add_mesh(bunny, color="w", opacity=0.1) +pl.add_mesh(clipped2, scalars="dist", cmap="inferno") +pl.show() diff --git a/microgen/external.py b/microgen/external.py index 4fd5ede4..a03a73a4 100644 --- a/microgen/external.py +++ b/microgen/external.py @@ -2,6 +2,8 @@ - `Neper - Polycrystal Generation and Meshing`_ - `mmg - Robust, Open-source & Multidisciplinary Software for Remeshing`_ + (via the ``mmgpy`` Python bindings — install with + ``pip install mmgpy`` or ``conda install -c conda-forge mmgpy``) .. _Neper - Polycrystal Generation and Meshing: https://neper.info/ .. _mmg - Robust, Open-source & Multidisciplinary Software for Remeshing: https://www.mmgtools.org/ @@ -13,7 +15,7 @@ import subprocess import warnings from pathlib import Path -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any import numpy as np @@ -31,6 +33,21 @@ # Seed-row index in the *cell seed* section. _TESS_SEED_WEIGHT_INDEX = 4 +_MMGPY_INSTALL_HINT = ( + "microgen's MMG remeshing path requires mmgpy. " + "Install it with: pip install mmgpy " + "or: conda install -c conda-forge mmgpy" +) + + +def _require_mmgpy() -> Any: + """Import :mod:`mmgpy` lazily and surface a helpful error otherwise.""" + try: + import mmgpy # noqa: PLC0415 + except ImportError as err: + raise ImportError(_MMGPY_INSTALL_HINT) from err + return mmgpy + # --------------------------------------------------------------------------- # Neper @@ -294,40 +311,132 @@ def _read_polyhedra(f: TextIO) -> dict: class MmgError(Exception): - """Raised when an mmg command fails.""" + """Raised when an Mmg invocation fails.""" + + +# CLI flag → mmgpy option-key translation. Most flags map 1:1 to mmgpy's +# options dict; two flags use renamed keys (``-nr`` → ``angle=0`` disables +# ridge detection; ``-A`` → ``anisosize=1`` requests anisotropic adaptation). +_SKIP = object() + + +def _as_float(v: Any) -> Any: + return _SKIP if v is None or v is False else float(v) + + +def _as_int_or_one(v: Any) -> Any: + return _SKIP if v is None or v is False else (1 if v is True else int(v)) + + +def _as_int_or_zero(v: Any) -> Any: + return _SKIP if v is None or v is False else (0 if v is True else int(v)) + + +def _as_float_or_zero(v: Any) -> Any: + return _SKIP if v is None or v is False else (0.0 if v is True else float(v)) + + +def _as_presence(v: Any) -> Any: + return 1 if v else _SKIP + + +def _as_angle_zero(v: Any) -> Any: + return 0 if v else _SKIP + + +_FLAG_SPEC: dict[str, tuple[str, Any]] = { + # Numeric / value-carrying. + "hausd": ("hausd", _as_float), + "hgrad": ("hgrad", _as_float), + "hmax": ("hmax", _as_float), + "hmin": ("hmin", _as_float), + "hsiz": ("hsiz", _as_float), + "rmc": ("rmc", _as_float), + "ar": ("ar", _as_float), + # Bool-true → 1, otherwise int cast; some keys are renamed. + "v": ("verbose", _as_int_or_one), + "m": ("mem", _as_int_or_one), + "nreg": ("nreg", _as_int_or_one), + "nsd": ("nsd", _as_int_or_one), + "octree": ("octree", _as_int_or_one), + # 0 is meaningful (bool-true defaults to 0). + "lag": ("lag", _as_int_or_zero), + "ls": ("ls", _as_float_or_zero), + # Boolean / presence-only. + "noinsert": ("noinsert", _as_presence), + "nomove": ("nomove", _as_presence), + "nosurf": ("nosurf", _as_presence), + "noswap": ("noswap", _as_presence), + "optim": ("optim", _as_presence), + "optimLES": ("optimLES", _as_presence), + "opnbdy": ("opnbdy", _as_presence), + "nofem": ("nofem", _as_presence), + # CLI synonyms with renamed option keys. + "A": ("anisosize", _as_presence), + "rn": ("renum", _as_presence), + "nr": ("angle", _as_angle_zero), +} + + +def _build_mmg_options(flag_kwargs: dict[str, Any]) -> dict[str, Any]: + """Translate Mmg CLI-style kwargs into an ``mmgpy`` options dict.""" + opts: dict[str, Any] = {} + for raw_key, raw_val in flag_kwargs.items(): + spec = _FLAG_SPEC.get(raw_key) + if spec is None: + continue + out_key, mapper = spec + result = mapper(raw_val) + if result is not _SKIP: + opts[out_key] = result + return opts + + +def _run_mmg( + binding_name: str, + *, + input_mesh: str | None, + output_mesh: str | None, + solution: str | None, + metric: str | None, + options: dict[str, Any], +) -> None: + """Invoke ``mmgpy.{mmg2d,mmgs,mmg3d}.remesh`` and re-raise as MmgError. + + The ``input_mesh is None`` guard runs *before* :func:`_require_mmgpy` so + the historical "no-args raises MmgError" contract is preserved even on + machines without mmgpy installed. + """ + if input_mesh is None: + err_msg = f"Mmg.{binding_name} requires an input mesh path (input=...)" + raise MmgError(err_msg) + + mmgpy = _require_mmgpy() + binding = getattr(mmgpy, binding_name) + try: + binding.remesh( + input_mesh=input_mesh, + input_sol=solution, + input_met=metric, + output_mesh=output_mesh, + options=options, + ) + except Exception as err: # noqa: BLE001 + raise MmgError(f"mmg invocation failed: {err}") from err class Mmg: - """Wrapper around the ``mmg2d_O3`` / ``mmgs_O3`` / ``mmg3d_O3`` binaries. + """Thin wrapper around :mod:`mmgpy` with a CLI-style keyword interface. - Each method exposes the underlying mmg CLI flags as keyword arguments - using the same short names as the binaries themselves (``input``, - ``output``, ``ls``, ``nr``, ``A``, ``ar``, ...). See the - `mmg documentation `_ - for the full reference. - - A larger redesign of this interface is planned; for now it intentionally - mirrors the original flat-kwarg shape. + Each static method accepts CLI-named keyword arguments (``input``, + ``output``, ``hausd``, ``hgrad``, …), translates them to an ``mmgpy`` + options dict, and forwards to :func:`mmgpy.mmg{2d,s,3d}.remesh`. """ - @staticmethod - def _run_mmg_command(cmd: list[str]) -> None: - try: - subprocess.check_output(cmd, stderr=subprocess.STDOUT) - except (subprocess.CalledProcessError, FileNotFoundError) as error: - mmg_failed_command = " ".join(cmd) - raise MmgError( - f"mmg command '{mmg_failed_command}' failed", - ) from error - @staticmethod def mmg2d( - d=None, - h=None, m=None, v=None, - val=None, - default=None, input=None, output=None, solution=None, @@ -341,7 +450,6 @@ def mmg2d( hsiz=None, lag=None, ls=None, - _3dMedit=None, noinsert=None, nomove=None, nosurf=None, @@ -353,88 +461,48 @@ def mmg2d( opnbdy=None, rmc=None, ): - """Run mmg2d_O3 from the command line with given arguments.""" - cmd = ["mmg2d_O3"] - if d: - cmd.append("-d") - if h: - cmd.append("-h") - if m: - if isinstance(m, bool): - m = "" - cmd.extend(["-m", str(m)]) - if v: - if isinstance(v, bool): - v = 1 - cmd.extend(["-v", str(v)]) - if val: - cmd.append("-val") - if default: - cmd.append("-default") - if input: - cmd.extend(["-in", input]) - if output: - cmd.extend(["-out", output]) - if solution: - cmd.extend(["-sol", solution]) - if metric: - cmd.extend(["-met", metric]) - if A: - cmd.append("-A") - if ar: - cmd.extend(["-ar", str(ar)]) - if hausd: - cmd.extend(["-hausd", str(hausd)]) - if hgrad: - cmd.extend(["-hgrad", str(hgrad)]) - if hmax: - cmd.extend(["-hmax", str(hmax)]) - if hmin: - cmd.extend(["-hmin", str(hmin)]) - if hsiz: - cmd.extend(["-hsiz", str(hsiz)]) - if lag or lag == 0: - if isinstance(lag, bool): - lag = 0 - cmd.extend(["-lag", str(lag)]) - if ls or ls == 0: - ls_value = ls - if isinstance(ls_value, bool): - ls_value = 0 - cmd.extend(["-ls", str(ls_value)]) - if _3dMedit: - cmd.extend(["-3dMedit", str(_3dMedit)]) - if noinsert: - cmd.append("-noinsert") - if nomove: - cmd.append("-nomove") - if nosurf: - cmd.append("-nosurf") - if noswap: - cmd.append("-noswap") - if nr: - cmd.append("-nr") - if nreg: - cmd.extend(["-nreg", str(nreg)]) - if nsd: - cmd.extend(["-nsd", str(nsd)]) - if optim: - cmd.append("-optim") - if opnbdy: - cmd.append("-opnbdy") - if rmc: - cmd.extend(["-rmc", str(rmc)]) - - Mmg._run_mmg_command(cmd) + """Run mmg2d on ``input`` writing to ``output``. + + Forwards to :func:`mmgpy.mmg2d.remesh`. + """ + opts = _build_mmg_options( + { + "m": m, + "v": v, + "A": A, + "ar": ar, + "hausd": hausd, + "hgrad": hgrad, + "hmax": hmax, + "hmin": hmin, + "hsiz": hsiz, + "lag": lag, + "ls": ls, + "noinsert": noinsert, + "nomove": nomove, + "nosurf": nosurf, + "noswap": noswap, + "nr": nr, + "nreg": nreg, + "nsd": nsd, + "optim": optim, + "opnbdy": opnbdy, + "rmc": rmc, + }, + ) + _run_mmg( + "mmg2d", + input_mesh=input, + output_mesh=output, + solution=solution, + metric=metric, + options=opts, + ) @staticmethod def mmgs( - d=None, - h=None, m=None, v=None, - val=None, - default=None, input=None, output=None, solution=None, @@ -457,79 +525,46 @@ def mmgs( optim=None, rn=None, ): - """Run mmgs_O3 from the command line with given arguments.""" - cmd = ["mmgs_O3"] - if d: - cmd.append("-d") - if h: - cmd.append("-h") - if m: - if isinstance(m, bool): - m = "" - cmd.extend(["-m", str(m)]) - if v: - if isinstance(v, bool): - v = 1 - cmd.extend(["-v", str(v)]) - if val: - cmd.append("-val") - if default: - cmd.append("-default") - if input: - cmd.extend(["-in", input]) - if output: - cmd.extend(["-out", output]) - if solution: - cmd.extend(["-sol", solution]) - if metric: - cmd.extend(["-met", metric]) - if A: - cmd.append("-A") - if ar: - cmd.extend(["-ar", str(ar)]) - if hausd: - cmd.extend(["-hausd", str(hausd)]) - if hgrad: - cmd.extend(["-hgrad", str(hgrad)]) - if hmax: - cmd.extend(["-hmax", str(hmax)]) - if hmin: - cmd.extend(["-hmin", str(hmin)]) - if hsiz: - cmd.extend(["-hsiz", str(hsiz)]) - if ls or ls == 0: - ls_value = ls - if isinstance(ls_value, bool): - ls_value = 0 - cmd.extend(["-ls", str(ls_value)]) - if noinsert: - cmd.append("-noinsert") - if nomove: - cmd.append("-nomove") - if nosurf: - cmd.append("-nosurf") - if noswap: - cmd.append("-noswap") - if nr: - cmd.append("-nr") - if nreg: - cmd.extend(["-nreg", str(nreg)]) - if nsd: - cmd.extend(["-nsd", str(nsd)]) - if optim: - cmd.append("-optim") - if rn: - cmd.append("-rn") - Mmg._run_mmg_command(cmd) + """Run mmgs (surface remesher) on ``input`` writing to ``output``. + + Forwards to :func:`mmgpy.mmgs.remesh`. + """ + opts = _build_mmg_options( + { + "m": m, + "v": v, + "A": A, + "ar": ar, + "hausd": hausd, + "hgrad": hgrad, + "hmax": hmax, + "hmin": hmin, + "hsiz": hsiz, + "ls": ls, + "noinsert": noinsert, + "nomove": nomove, + "nosurf": nosurf, + "noswap": noswap, + "nr": nr, + "nreg": nreg, + "nsd": nsd, + "optim": optim, + "rn": rn, + }, + ) + _run_mmg( + "mmgs", + input_mesh=input, + output_mesh=output, + solution=solution, + metric=metric, + options=opts, + ) @staticmethod def mmg3d( - d=None, - h=None, m=None, v=None, - val=None, - default=None, input=None, output=None, solution=None, @@ -558,82 +593,44 @@ def mmg3d( rmc=None, rn=None, ): - """Run mmg3d_O3 from the command line with given arguments.""" - cmd = ["mmg3d_O3"] - if d: - cmd.append("-d") - if h: - cmd.append("-h") - if m: - if isinstance(m, bool): - m = "" - cmd.extend(["-m", str(m)]) - if v: - if isinstance(v, bool): - v = 1 - cmd.extend(["-v", str(v)]) - if val: - cmd.append("-val") - if default: - cmd.append("-default") - if input: - cmd.extend(["-in", input]) - if output: - cmd.extend(["-out", output]) - if solution: - cmd.extend(["-sol", solution]) - if metric: - cmd.extend(["-met", metric]) - if A: - cmd.append("-A") - if ar: - cmd.extend(["-ar", str(ar)]) - if octree: - cmd.extend(["-octree", str(octree)]) - if hausd: - cmd.extend(["-hausd", str(hausd)]) - if hgrad: - cmd.extend(["-hgrad", str(hgrad)]) - if hmax: - cmd.extend(["-hmax", str(hmax)]) - if hmin: - cmd.extend(["-hmin", str(hmin)]) - if hsiz: - cmd.extend(["-hsiz", str(hsiz)]) - if lag or lag == 0: - if isinstance(lag, bool): - lag = 0 - cmd.extend(["-lag", str(lag)]) - if ls or ls == 0: - ls_value = ls - if isinstance(ls_value, bool): - ls_value = 0 - cmd.extend(["-ls", str(ls_value)]) - if nofem: - cmd.append("-nofem") - if noinsert: - cmd.append("-noinsert") - if nomove: - cmd.append("-nomove") - if nosurf: - cmd.append("-nosurf") - if noswap: - cmd.append("-noswap") - if nr: - cmd.append("-nr") - if nreg: - cmd.extend(["-nreg", str(nreg)]) - if nsd: - cmd.extend(["-nsd", str(nsd)]) - if optim: - cmd.append("-optim") - if optimLES: - cmd.append("-optimLES") - if opnbdy: - cmd.append("-opnbdy") - if rmc: - cmd.extend(["-rmc", str(rmc)]) - if rn: - cmd.append("-rn") - - Mmg._run_mmg_command(cmd) + """Run mmg3d (volume remesher) on ``input`` writing to ``output``. + + Forwards to :func:`mmgpy.mmg3d.remesh`. + """ + opts = _build_mmg_options( + { + "m": m, + "v": v, + "A": A, + "ar": ar, + "octree": octree, + "hausd": hausd, + "hgrad": hgrad, + "hmax": hmax, + "hmin": hmin, + "hsiz": hsiz, + "lag": lag, + "ls": ls, + "nofem": nofem, + "noinsert": noinsert, + "nomove": nomove, + "nosurf": nosurf, + "noswap": noswap, + "nr": nr, + "nreg": nreg, + "nsd": nsd, + "optim": optim, + "optimLES": optimLES, + "opnbdy": opnbdy, + "rmc": rmc, + "rn": rn, + }, + ) + _run_mmg( + "mmg3d", + input_mesh=input, + output_mesh=output, + solution=solution, + metric=metric, + options=opts, + ) diff --git a/microgen/shape/strut_lattice/__init__.py b/microgen/shape/strut_lattice/__init__.py index f19ee2fd..0466de8b 100644 --- a/microgen/shape/strut_lattice/__init__.py +++ b/microgen/shape/strut_lattice/__init__.py @@ -1,4 +1,5 @@ -"""Strut-based lattice structures. +""" +Strut-based lattice structures. ======================================== Strut Lattice (:mod:`microgen.shape.strut_lattice`) @@ -40,11 +41,11 @@ "CustomLattice", "Diamond", "FaceCenteredCubic", - "TruncatedCuboctahedron", "Octahedron", "OctetTruss", - "TruncatedCube", "RhombicCuboctahedron", "RhombicDodecahedron", + "TruncatedCube", + "TruncatedCuboctahedron", "TruncatedOctahedron", ] diff --git a/microgen/shape/surface_functions.py b/microgen/shape/surface_functions.py index f7e48b5c..6f9f0fbe 100644 --- a/microgen/shape/surface_functions.py +++ b/microgen/shape/surface_functions.py @@ -5,7 +5,8 @@ def gyroid(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Gyroid. + """ + Gyroid. .. math:: sin(x) cos(y) + sin(y) cos(z) + sin(z) cos(x) = 0 @@ -28,7 +29,8 @@ def gyroid(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def schwarz_p(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Schwarz P. + """ + Schwarz P. .. math:: cos(x) + cos(y) + cos(z) = 0 @@ -51,7 +53,8 @@ def schwarz_p(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def schwarz_d(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Schwarz D. + """ + Schwarz D. .. math:: sin(x) sin(y) sin(z) + sin(x) cos(y) cos(z) +\ @@ -80,7 +83,8 @@ def schwarz_d(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def neovius(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Neovius. + """ + Neovius. .. math:: 3 cos(x) + cos(y) + cos(z) + 4 cos(x) cos(y) cos(z) = 0 @@ -103,7 +107,8 @@ def neovius(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def schoen_iwp(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Schoen IWP. + """ + Schoen IWP. .. math:: 2 (cos(x) cos(y) + cos(y) cos(z) + cos(z) cos(x)) \ @@ -129,7 +134,8 @@ def schoen_iwp(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def schoen_frd(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Schoen FRD. + """ + Schoen FRD. .. math:: 4 cos(x) cos(y) cos(z) \ @@ -155,7 +161,8 @@ def schoen_frd(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def fischer_koch_s(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Fischer-Koch S. + """ + Fischer-Koch S. .. math:: cos(2 x) sin(y) cos(z) + cos(x) cos(2 y) sin(z) + sin(x) cos(y) cos(2 z) = 0 @@ -182,7 +189,8 @@ def fischer_koch_s(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def pmy(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """PMY. + """ + PMY. .. math:: 2 cos(x) cos(y) cos(z) + sin(2 x) sin(y) + sin(x) sin(2 z) + sin(2 y) sin(z) = 0 @@ -210,7 +218,8 @@ def pmy(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def honeycomb(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Honneycomb. + """ + Honneycomb. .. math:: sin(x) cos(y) + sin(y) + cos(z) = 0 @@ -233,7 +242,8 @@ def honeycomb(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def lidinoid(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Lidinoid. + """ + Lidinoid. .. math:: 0.5 (sin(2 x) cos(y) sin(z) + @@ -271,7 +281,8 @@ def lidinoid(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def split_p(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: - """Split P. + """ + Split P. .. math:: 1.1 (sin(2 x) cos(y) sin(z) + @@ -310,7 +321,8 @@ def split_p(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray: def honeycomb_gyroid(x: float, y: float, _: float) -> float: - """Honeycomb Gyroid. + """ + Honeycomb Gyroid. .. math:: sin(x) cos(y) + sin(y) + cos(x) = 0 @@ -333,7 +345,8 @@ def honeycomb_gyroid(x: float, y: float, _: float) -> float: def honeycomb_schwarz_p(x: float, y: float, _: float) -> float: - """Honeycomb Schwarz P. + """ + Honeycomb Schwarz P. .. math:: cos(x) + cos(y) = 0 @@ -356,7 +369,8 @@ def honeycomb_schwarz_p(x: float, y: float, _: float) -> float: def honeycomb_schwarz_d(x: float, y: float, _: float) -> float: - """Honneycomb Schwarz D. + """ + Honneycomb Schwarz D. .. math:: cos(x) cos(y) + sin(x) sin(y) + sin(x) cos(y) + cos(x) sin(y) = 0 @@ -379,7 +393,8 @@ def honeycomb_schwarz_d(x: float, y: float, _: float) -> float: def honeycomb_schoen_iwp(x: float, y: float, _: float) -> float: - """Honneycomb Schoen IWP. + """ + Honneycomb Schoen IWP. .. math:: cos(x) cos(y) + cos(y) + cos(x) = 0 @@ -402,7 +417,8 @@ def honeycomb_schoen_iwp(x: float, y: float, _: float) -> float: def honeycomb_lidinoid(x: float, y: float, _: float) -> float: - """Honeycomb Lidinoid. + """ + Honeycomb Lidinoid. .. math:: 1.1 (sin(2 x) cos(y) + sin(2 y) sin(x) + cos(x) sin(y)) diff --git a/microgen/shape/tpms_grading.py b/microgen/shape/tpms_grading.py index a7463f0d..c1a4b847 100644 --- a/microgen/shape/tpms_grading.py +++ b/microgen/shape/tpms_grading.py @@ -55,7 +55,8 @@ def compute_offset( self: OffsetGrading, grid: pv.UnstructuredGrid | pv.StructuredGrid, ) -> npt.NDArray[np.float64]: - """Compute the offset of the grid. + """ + Compute the offset of the grid. This method should compute the offset on each point of the grid and return \ it as a 1D array. @@ -87,7 +88,8 @@ def __init__( furthest_offset: float, boundary_weight: float = 1.0, ) -> None: - """Initialize the ImplicitDistance object. + """ + Initialize the ImplicitDistance object. Parameters ----------