From 9c3e976d917da6b509dd1ebe25fcd1d86975363e Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Wed, 21 Jun 2023 17:20:33 +0200 Subject: [PATCH 01/11] remove submodules --- .gitmodules | 3 --- examples/samples | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 examples/samples diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e759e1a75..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "examples/samples"] - path = examples/samples - url = git@github.com:tataratat/samples.git diff --git a/examples/samples b/examples/samples deleted file mode 160000 index 170914a2d..000000000 --- a/examples/samples +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 170914a2d42ad0d62402cdea26562a32c3af4734 From fa6456dcc25a372e9dacaebf54388ce2b03736bb Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Wed, 21 Jun 2023 14:55:01 +0200 Subject: [PATCH 02/11] remove splines --- gustaf/__init__.py | 31 - gustaf/helpers/data.py | 233 --- gustaf/spline/__init__.py | 51 - gustaf/spline/base.py | 594 ------- gustaf/spline/create.py | 775 -------- gustaf/spline/extract.py | 569 ------ gustaf/spline/ffd.py | 500 ------ gustaf/spline/microstructure/__init__.py | 9 - .../spline/microstructure/microstructure.py | 562 ------ .../spline/microstructure/tiles/__init__.py | 28 - .../microstructure/tiles/crosstile2d.py | 436 ----- .../microstructure/tiles/crosstile3d.py | 560 ------ .../tiles/inversecrosstile3d.py | 1563 ----------------- .../spline/microstructure/tiles/tilebase.py | 63 - gustaf/spline/proximity.py | 65 - gustaf/spline/visualize.py | 367 ---- 16 files changed, 6406 deletions(-) delete mode 100644 gustaf/spline/__init__.py delete mode 100644 gustaf/spline/base.py delete mode 100644 gustaf/spline/create.py delete mode 100644 gustaf/spline/extract.py delete mode 100644 gustaf/spline/ffd.py delete mode 100644 gustaf/spline/microstructure/__init__.py delete mode 100644 gustaf/spline/microstructure/microstructure.py delete mode 100644 gustaf/spline/microstructure/tiles/__init__.py delete mode 100644 gustaf/spline/microstructure/tiles/crosstile2d.py delete mode 100644 gustaf/spline/microstructure/tiles/crosstile3d.py delete mode 100644 gustaf/spline/microstructure/tiles/inversecrosstile3d.py delete mode 100644 gustaf/spline/microstructure/tiles/tilebase.py delete mode 100644 gustaf/spline/proximity.py delete mode 100644 gustaf/spline/visualize.py diff --git a/gustaf/__init__.py b/gustaf/__init__.py index 4ffe049cb..67ae98fa9 100644 --- a/gustaf/__init__.py +++ b/gustaf/__init__.py @@ -16,30 +16,6 @@ from gustaf.vertices import Vertices from gustaf.volumes import Volumes -has_spline = False -try: - from gustaf import spline - from gustaf.spline.base import NURBS, Bezier, BSpline, RationalBezier - from gustaf.spline.ffd import FFD - - has_spline = True -except ImportError as err: - # overwrites the all modules which depend on the `splinepy` library - # with an object which will throw an error - # as soon as it is used the first time. This means that any non spline - # functionality works as before, but as soon as these are used a - # comprehensive exception will be raised which is understandable in - # contrast to the possible multitude of errors previously possible - from gustaf.helpers.raise_if import ModuleImportRaiser - - spline = ModuleImportRaiser("splinepy", err) - BSpline = spline - NURBS = spline - Bezier = spline - RationalBezier = spline - FFD = spline - - __version__ = _version.version __all__ = [ @@ -58,11 +34,4 @@ "Edges", "Faces", "Volumes", - "spline", - "has_spline", - "BSpline", - "NURBS", - "Bezier", - "RationalBezier", - "FFD", ] diff --git a/gustaf/helpers/data.py b/gustaf/helpers/data.py index 7a1c03ec9..c5a3f6cc3 100644 --- a/gustaf/helpers/data.py +++ b/gustaf/helpers/data.py @@ -610,239 +610,6 @@ def as_arrow(self, key, default=None, raise_=True): return value -class SplineDataAdaptor(GustafBase): - """ - Prepares data to be presentable on spline. To support both - scalar-data and vector-data, which are representable with colors and - arrows respectively, this class will prepare data accordingly. - """ - - __slots__ = ( - "data", - "function", - "locations", - "is_spline", - "has_function", - "has_locations", - "has_evaluate", - "arrow_data_only", - "_user_created", - ) - - def __init__(self, data, locations=None, function=None): - """ """ - # default - self._user_created = True - self.data = data - self.is_spline = False - self.has_function = False - self.has_locations = False - self.has_evaluate = False - self.arrow_data_only = False - - # is spline we know? - if "CoreSpline" in str(type(data).__mro__): - self.is_spline = True - - # data has evaluate? - if hasattr(data, "evaluate"): - self.has_evaluate = callable(data.evaluate) - - # has function? - if function is not None: - self.has_function = True - if not callable(function): - raise ValueError("Given function isn't callable") - self.function = function - - # locations? - keep this compatible with functions. maybe - # we want to have some state dependent value at certain locations - if locations is not None: - # set what holds true - self.has_locations = True - self.arrow_data_only = True - self.locations = np.asanyarray(locations) - - # if this is not a spline we know, it doesn't have a function, - # it should: - # -> `data.evaluate` is callable, or - # -> len(data) == len(locations) - if not self.is_spline and not self.has_function: - len_matches = False - if hasattr(data, "__len__"): - len_matches = len(locations) == len(data) - if not (self.has_evaluate or len_matches): - raise ValueError( - "Data cannot be represented at specified locations." - "Requires one of the following requirements: " - "1) is a spline derived from splinepy's spline; " - "2) data has `data.evaluate()`; " - "3) length of the data and location should match." - ) - # location is specified, meaning we don't need sample() - return None - - # can call sample or has a function? - if not self.has_function and not self.is_spline: - raise ValueError( - "None spline data should at least have an accompanying " - "function." - ) - - def as_vertex_data(self, resolutions=None, on=None): - """ - Parameters - ---------- - resolutions: list or tuple - at: (n, d) array-like - - Returns - ------- - vertex_data: (m, r) array-like - """ - if resolutions is not None and on is not None: - raise ValueError( - "Please only specify either `resolutions` or `on`" - ) - - if self.has_locations and (resolutions is not None or on is not None): - raise ValueError( - "Location dependent data can't be evaluated with `resolutions`" - " or `at`." - ) - - # if resolutions is specified, this is not a location query - if resolutions is not None: - if self.has_function: - return self.function(self.data, resolutions=resolutions) - elif self.is_spline and self.data.para_dim > 2: - # TODO: replace this with generalized query helpers. - return self.data.extract.faces( - resolutions, watertight=False - ).vertices - else: - return self.data.sample(resolutions) - - # runtime location query - if on is not None: - if self.has_function: - return self.function(self.data, on=on) - elif self.has_evaluate: - return self.data.evaluate(on) - else: - raise ValueError( - "Given data can't support data extraction on specified " - f"locations ({on})." - ) - - # location specified - either evaluate function at the - if self.has_locations: - if self.has_function: - # function may want locations - try: - return self.function(self.data, self.locations) - except TypeError: # maybe too many args - return self.function(self.data) - elif self.has_evaluate: - return self.data.evaluate(self.locations) - else: - return self.data - - # should be returned by now - raise RuntimeError("Something went wrong while preparing spline data.") - - -class SplineData(DataHolder): - """ - Data manager for splines. - """ - - def __init__(self, helpee): - """ """ - if "GustafSpline" not in str(type(helpee).__mro__): - raise AttributeError("Helpee does not have `vertices`") - - super().__init__(helpee) - - def __setitem__(self, key, value): - """ - Selectively accept spline data. - - Parameters - ---------- - key: str - value: object - - Returns - ------- - None - """ - if isinstance(value, SplineDataAdaptor): - self._saved[key] = value - else: - adapted = SplineDataAdaptor(value) # will test usability - adapted._user_created = False # mark for __getitem__ - self._saved[key] = adapted - - def __getitem__(self, key): - """ - Returns value from __setitem__ - - Parameters - ---------- - key: str - - Returns - ------- - value: object - """ - saved = super().__getitem__(key) - if saved._user_created: - return saved - else: - return saved.data - - def as_scalar(self, key, resolutions, default=None): - """ - Return scalar value at given resolutions - - Parameters - ---------- - key: str - resolutions: list or tuple - default: object - Default is None and will return is key doesn't exist - - Returns - ------- - value: np.ndarray - """ - if key not in self._saved: - return default - - saved = super().__getitem__(key) - # will raise - return saved.as_vertex_data(resolutions=resolutions) - - def as_arrow(self, key, resolutions=None, on=None, default=None): - """ - Returns as-arrow-representable data on certain places, with given - resolution, or on predefined places. - - Parameters - ---------- - key: str - resolutions: list or tuple - on: array-like - """ - if key not in self._saved: - return default - - saved = super().__getitem__(key) - # will raise - return saved.as_vertex_data(resolutions=resolutions, on=on) - - Unique2DFloats = namedtuple( "Unique2DFloats", ["values", "ids", "inverse", "intersection"] ) diff --git a/gustaf/spline/__init__.py b/gustaf/spline/__init__.py deleted file mode 100644 index 1b07cd763..000000000 --- a/gustaf/spline/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -"""gustaf/spline/__init__.py. - -Interface for `splinepy` library and additional operations. Everything -related to `splinepy` is here, which includes its load function. -However, since create.spline does not rely on `splinepy`, it is not -here. -""" - -import splinepy -from splinepy import io - -from gustaf.helpers.data import SplineDataAdaptor -from gustaf.spline import base, create, extract, ffd, microstructure, visualize -from gustaf.spline.base import ( - NURBS, - Bezier, - BSpline, - RationalBezier, - from_mfem, - load_splines, - show, -) - -# overwrite name to type map -splinepy.settings.NAME_TO_TYPE = dict( - Bezier=Bezier, - RationalBezier=RationalBezier, - BSpline=BSpline, - NURBS=NURBS, -) - -# a shortcut -NAME_TO_TYPE = splinepy.settings.NAME_TO_TYPE - -__all__ = [ - "base", - "create", - "extract", - "Bezier", - "RationalBezier", - "BSpline", - "NURBS", - "SplineDataAdaptor", - "show", - "from_mfem", - "load_splines", - "ffd", - "microstructure", - "io", - "visualize", -] diff --git a/gustaf/spline/base.py b/gustaf/spline/base.py deleted file mode 100644 index c38c9248d..000000000 --- a/gustaf/spline/base.py +++ /dev/null @@ -1,594 +0,0 @@ -"""gustaf/spline/base.py. - -Base for splines. Contains show and inherited classes from `spline`. -""" -import numpy as np -import splinepy - -from gustaf import show as showmodule -from gustaf import utils -from gustaf._base import GustafBase -from gustaf.helpers.data import SplineData -from gustaf.spline import visualize -from gustaf.spline.create import Creator -from gustaf.spline.extract import Extractor -from gustaf.spline.proximity import Proximity - - -def show(spline, **kwargs): - """Shows splines with various options. - - They are excessively listed, so that it can be adjustable. - - Parameters - ----------- - spline: BSpline or NURBS - resolutions: int or (spline.para_dim,) array-like - control_points: bool - knots: bool - fitting_queries: bool - return_discrete: bool - Return dict of gustaf discrete objects, for example, - {Vertices, Edges, Faces}, instead of opening a window - return_showable: bool - Return dict of showable objects. - parametric_space: bool - Only relevant for `vedo` backend. - c: str - Default is None. Black for curves, else green. - alpha: float - lighting: str - control_point_ids: bool - color_spline: str - cmap: str - - Returns - -------- - things_to_show: dict - iff return_discrete==True, dict of gustaf objects that are showable. - iff return_showable==True, dict of backend objects that are showable. - """ - # Showing is only possible for following splines - allowed_dim_combo = ( - (1, 2), - (1, 3), - (2, 2), - (2, 3), - (3, 3), - ) - if (spline.para_dim, spline.dim) not in allowed_dim_combo: - raise ValueError("Sorry, can't show given spline.") - - if kwargs: - orig_show_options = spline.show_options - spline._show_options = spline.__show_option__(spline) - orig_show_options.copy_valid_options(spline.show_options) - for key, value in kwargs.items(): - try: - spline.show_options[key] = value - except BaseException: - utils.log.debug( - f"Skipping invalid option {key} for " - f"{spline.show_options._helps}." - ) - continue - - # Prepare things to show dict. - things_to_show = visualize.make_showable(spline) - - para_space = kwargs.pop("parametric_space", False) - if para_space: - p_spline = spline.create.parametric_view() - things_to_show["parametric_spline"] = p_spline - - if kwargs.get("return_discrete", False): - return things_to_show - - if kwargs.get("return_showable", False): - return {key: value.showable() for key, value in things_to_show.items()} - - if para_space: - things_to_show.pop("parametric_spline") - - return showmodule.show_vedo( - ["Parametric Space", p_spline], - ["Physical Space", *things_to_show.values()], - **kwargs, - ) - - return showmodule.show_vedo(things_to_show, **kwargs) - - -class GustafSpline(GustafBase): - __show_option__ = visualize.SplineShowOption - - def __init__(self): - """Constructor as abstractmethod. - - This needs to be inherited first to make sure duplicating - functions properly override splinepy.Spline - """ - self._extractor = Extractor(self) - self._proximity = Proximity(self) - self._creator = Creator(self) - self._show_options = self.__show_option__(self) - self._spline_data = SplineData(self) - - @property - def show_options(self): - """ - Show option manager for splines. - - Parameters - ---------- - None - - Returns - ------- - show_options: SplineShowOption - """ - return self._show_options - - @property - def spline_data(self): - """ - Spline data helper for splines. - - Parameters - ---------- - None - - Returns - ------- - spline_data: SplineData - """ - return self._spline_data - - @property - def extract(self): - """Returns spline extractor. Can directly perform extractions available - at `gustaf/spline/extract.py`. For more info, take a look at - `gustaf/spline/extract.py`: Extractor. - - Examples - --------- - >>> spline_faces = spline.extract.faces() - - Parameters - ----------- - None - - Returns - -------- - spline_extractor: Extractor - """ - return self._extractor - - @property - def create(self): - """Returns spline creator Can be used to create new splines using - geometric relations. - - Examples - -------- - >>> prism = spline.create.extrude(axis=[1,4,1]) - - Parameters - ---------- - None - - Returns - spline.Creator - """ - return self._creator - - @property - def proximity(self): - """Returns spline proximity helper. Can directly perform proximity - queries available at `gustaf/spline/proximity.py`. For more info, take - a look at `gustaf/spline/proximity.py`: Proximity. - - Examples - --------- - >>> closest_cp_ids = spline.proximity.closest_control_points(queries) - >>> closest_cp_ids, distances =\ - ... spline.proximity.closest_control_points( - ... queries, - ... return_distances=True - ... ) - - Parameters - ----------- - None - - Returns - -------- - spline_proximity: Proximity - """ - return self._proximity - - def show(self, **kwargs): - """Equivalent to `gustaf.spline.base.show(**kwargs)`""" - return show(self, **kwargs) - - def showable(self, **kwargs): - """Equivalent to - `gustaf.spline.base.show(return_showable=True,**kwargs)`""" - return show(self, return_showable=True, **kwargs) - - -class Bezier(GustafSpline, splinepy.Bezier): - def __init__( - self, - degrees=None, - control_points=None, - spline=None, - ): - """Bezier of gustaf. Inherited from splinepy.Bezier and GustafSpline. - - Attributes - ----------- - extract: Extractor - create: Creator - proximity: Proximity - - Parameters - ----------- - degrees: (para_dim,) list-like - control_points: (m, dim) list-like - - Returns - -------- - None - """ - splinepy.Bezier.__init__( - self, degrees=degrees, control_points=control_points, spline=spline - ) - GustafSpline.__init__(self) - - @property - def bezier(self): - """Returns same parametric representation as Bezier Spline. - - Parameters - ---------- - None - - Returns - ------- - same : Bezier - """ - return self.copy() - - @property - def rationalbezier(self): - """Returns same parametric representation as Rational Bezier Spline. - - Parameters - ---------- - None - - Returns - ------- - same : RationalBezier - """ - return RationalBezier( - degrees=self.degrees, - control_points=self.control_points, - weights=np.ones(self.control_points.shape[0]), - ) - - @property - def bspline(self): - """Returns same parametric representation as BSpline. - - Parameters - ----------- - None - - Returns - -------- - same_bspline : BSpline - """ - return BSpline( - degrees=self.degrees, - control_points=self.control_points, - knot_vectors=[ - [0] * (self.degrees[i] + 1) + [1] * (self.degrees[i] + 1) - for i in range(self.para_dim) - ], - ) - - @property - def nurbs(self): - """Returns same parametric representation as nurbs. - - Parameters - ----------- - None - - Returns - -------- - same_nurbs: NURBS - """ - return self.bspline.nurbs - - -class RationalBezier(GustafSpline, splinepy.RationalBezier): - def __init__( - self, - degrees=None, - control_points=None, - weights=None, - spline=None, - ): - """Rational Bezier of gustaf. Inherited from splinepy.RationalBezier - and GustafSpline. - - Attributes - ----------- - extract: Extractor - create: Creator - proximity: Proximity - - Parameters - ----------- - degrees: (para_dim,) list-like - control_points: (m, dim) list-like - weights : (m) list-like - - Returns - -------- - None - """ - splinepy.RationalBezier.__init__( - self, - degrees=degrees, - control_points=control_points, - weights=weights, - spline=spline, - ) - GustafSpline.__init__(self) - - @property - def rationalbezier(self): - """Returns same parametric representation as Rational Bezier Spline. - - Parameters - ---------- - None - - Returns - ------- - same : RationalBezier - """ - return self.copy() - - @property - def nurbs(self): - """Returns same parametric representation as nurbs. - - Parameters - ----------- - None - - Returns - -------- - same_nurbs: NURBS - """ - return NURBS( - degrees=self.degrees, - control_points=self.control_points, - knot_vectors=[ - [0] * (self.degrees[i] + 1) + [1] * (self.degrees[i] + 1) - for i in range(self.para_dim) - ], - weights=self.weights, - ) - - -class BSpline(GustafSpline, splinepy.BSpline): - def __init__( - self, - degrees=None, - knot_vectors=None, - control_points=None, - spline=None, - ): - """BSpline of gustaf. Inherited from splinepy.BSpline and GustafSpline. - - Attributes - ----------- - extract: Extractor - create: Creator - proximity: Proximity - - Parameters - ----------- - degrees: (para_dim,) list-like - knot_vectors: (para_dim, ...) list - control_points: (m, dim) list-like - - Returns - -------- - None - """ - splinepy.BSpline.__init__( - self, - degrees=degrees, - knot_vectors=knot_vectors, - control_points=control_points, - spline=spline, - ) - - GustafSpline.__init__(self) - - @property - def bspline(self): - """Returns same parametric representation as BSpline. - - Parameters - ----------- - None - - Returns - -------- - same_bspline : BSpline - """ - return self.copy() - - @property - def nurbs(self): - """Returns same nurbs. Overwrites one from splinepy to return correct - type. - - Parameters - ----------- - None - - Returns - -------- - same_nurbs: NURBS - """ - from copy import deepcopy - - return NURBS( - degrees=deepcopy(self.degrees), - knot_vectors=deepcopy(self.knot_vectors), - control_points=deepcopy(self.control_points), - # fix dtype to match splinepy's dtype. - weights=np.ones(self.control_points.shape[0], dtype="float64"), - ) - - -class NURBS(GustafSpline, splinepy.NURBS): - def __init__( - self, - degrees=None, - knot_vectors=None, - control_points=None, - weights=None, - spline=None, - ): - """NURBS of gustaf. Inherited from splinepy.NURBS. - - Attributes - ----------- - extract: Extractor - create: Creator - proximity: Proximity - - Parameters - ----------- - degrees: (para_dim,) list-like - knot_vectors: (para_dim,) list - control_points: (m, dim) list-like - weights: (m, 1) list-like - - Returns - -------- - None - """ - splinepy.NURBS.__init__( - self, - degrees=degrees, - knot_vectors=knot_vectors, - control_points=control_points, - weights=weights, - spline=spline, - ) - GustafSpline.__init__(self) - - @property - def _mfem_ids(self): - """Returns mfem index mapping. For ease of use. - - Parameters - ----------- - None - - Returns - -------- - None - """ - if self.para_dim != 2: - raise NotImplementedError( - "Sorry, only available for para_dim = 2 splines" - ) - - gustaf2mfem, mfem2gustaf = splinepy.io.mfem.mfem_index_mapping( - self.para_dim, - self.degrees, - self.knot_vectors, - ) - - return gustaf2mfem, mfem2gustaf - - @property - def nurbs(self): - """Returns same parametric representation as nurbs. - - Parameters - ----------- - None - - Returns - -------- - same_nurbs: NURBS - """ - return self.copy() - - -def from_mfem(nurbs_dict): - """Construct a gustaf NURBS. Reorganizes control points and weights. - - Parameters - ----------- - nurbs_dict: dict - - Returns - -------- - nurbs: NURBS - """ - _, m2gus = splinepy.io.mfem.mfem_index_mapping( - len(nurbs_dict["degrees"]), - nurbs_dict["degrees"], - nurbs_dict["knot_vectors"], - ) - - return NURBS( - degrees=nurbs_dict["degrees"], - knot_vectors=nurbs_dict["knot_vectors"], - control_points=nurbs_dict["control_points"][m2gus], - weights=nurbs_dict["weights"][m2gus], - ) - - -def load_splines(fname): - """Loads and creates gustaf NURBS. Does not perform any check or tests. - - Parameters - ----------- - fname: str - - Returns - -------- - gussplines: list - """ - # first get dict_splines using splinepy - dictsplines = splinepy.load_splines(fname, as_dict=True) - - # try to initialize with correct spline type - gussplines = list() - for dics in dictsplines: - is_bspline = "knot_vectors" in dics - is_nurbs = "weights" in dics - - if is_nurbs: - gussplines.append(NURBS(**dics)) - elif is_bspline: - gussplines.append(BSpline(**dics)) - else: - gussplines.append(Bezier(**dics)) - - return gussplines diff --git a/gustaf/spline/create.py b/gustaf/spline/create.py deleted file mode 100644 index 3f2f4451f..000000000 --- a/gustaf/spline/create.py +++ /dev/null @@ -1,775 +0,0 @@ -"""gustaf/spline/create.py. - -Creates splines. -""" - -import numpy as np - -from gustaf import settings, utils - - -def extruded(spline, extrusion_vector=None): - """Extrudes Splines. - - Parameters - ---------- - spline: GustafSpline - extrusion_vector: np.ndarray - """ - from gustaf.spline.base import GustafSpline - - # Check input type - if not issubclass(type(spline), GustafSpline): - raise NotImplementedError("Extrude only works for splines") - - # Check extrusion_vector - if extrusion_vector is not None: - # make flat extrusion_vector - extrusion_vector = np.asarray(extrusion_vector).ravel() - else: - raise ValueError("No extrusion extrusion_vector given") - - # Check extrusion_vector dimension - # formulate correct cps - if spline.dim == extrusion_vector.shape[0]: - cps = spline.control_points - elif spline.dim < extrusion_vector.shape[0]: - expansion_dimension = extrusion_vector.shape[0] - spline.dim - # one smaller dim is allowed - # warn that we assume new dim is all zero - utils.log.debug( - f"Given extrusion vector is {expansion_dimension} dimension " - "bigger than spline's dim. Assuming 0.0 entries for " - "new dimension.", - ) - cps = np.hstack( - ( - spline.control_points, - np.zeros((len(spline.control_points), expansion_dimension)), - ) - ) - else: - raise ValueError( - "Dimension Mismatch between extrusion extrusion vector " - "and spline." - ) - - # Start Extrusion - spline_dict = dict() - - spline_dict["degrees"] = np.concatenate((spline.degrees, [1])) - spline_dict["control_points"] = np.vstack((cps, cps + extrusion_vector)) - if spline.has_knot_vectors: - spline_dict["knot_vectors"] = spline.knot_vectors + [[0, 0, 1, 1]] - if spline.is_rational: - spline_dict["weights"] = np.concatenate( - (spline.weights, spline.weights) - ) - - return type(spline)(**spline_dict) - - -def revolved( - spline, axis=None, center=None, angle=None, n_knot_spans=None, degree=True -): - """Revolve spline around an axis and extend its parametric dimension. - - Parameters - ---------- - spline : GustafSpline - Basis-Spline to be revolved - axis : np.ndarray - Axis of revolution - center : np.ndarray - Center of revolution - angle : float - angle of the revolution. - n_knot_spans : int - number of non-zero knot-elements for result-spline (if applicable) - degree : bool - use degrees instead of radiant - - Returns - ------- - spline : GustafSpline - """ - - from gustaf.spline.base import GustafSpline - - # Check input type - if not issubclass(type(spline), GustafSpline): - raise NotImplementedError("Revolutions only works for splines") - - # Check axis - if axis is not None: - # Transform into numpy array - axis = np.asarray(axis).ravel() - # Check Axis dimension - if spline.control_points.shape[1] > axis.shape[0]: - raise ValueError( - "Dimension Mismatch between extrusion axis and spline." - ) - elif spline.control_points.shape[1] < axis.shape[0]: - utils.log.debug( - "Control Point dimension is smaller than axis dimension," - " filling with zeros" - ) - expansion_dimension = axis.shape[0] - spline.dim - cps = np.hstack( - ( - spline.control_points, - np.zeros( - (len(spline.control_points), expansion_dimension) - ), - ) - ) - else: - cps = np.copy(spline.control_points) - - # Make sure axis is normalized - - axis_norm = np.linalg.norm(axis) - if not np.isclose(axis_norm, 0, atol=settings.TOLERANCE): - axis = axis / axis_norm - else: - raise ValueError("Axis-norm is too close to zero.") - else: - cps = np.copy(spline.control_points) - if spline.control_points.shape[1] == 3: - raise ValueError("No rotation axis given") - - # Set Problem dimension - problem_dimension = cps.shape[1] - - # Make sure axis is ignored for 2D - if problem_dimension == 2: - axis = None - - # Update angle - if angle is None: - spline._logd("No angle given for the revolution. Using 360 degrees.") - angle = 360 - - if degree: - angle = np.radians(angle) - - # Init center - if center is not None: - center = np.asarray(center).ravel() - # Check Axis dimension - if not (problem_dimension == center.shape[0]): - raise ValueError( - "Dimension Mismatch between axis and center of rotation." - ) - cps -= center - - # The parametric dimension is independent of the revolution but the - # rotation-matrix is only implemented for 2D and 3D problems - if not (cps.shape[1] == 2 or cps.shape[1] == 3): - raise NotImplementedError( - "Sorry," "revolutions only implemented for 2D and 3D splines" - ) - - # Angle must be (0, pi) non including - # Rotation is always performed in half steps - PI = np.pi - minimum_n_knot_spans = int( - np.ceil(np.abs((angle + settings.TOLERANCE) / PI)) - ) - if (n_knot_spans) is None or (n_knot_spans < minimum_n_knot_spans): - n_knot_spans = minimum_n_knot_spans - - if "Bezier" in spline.name: - if n_knot_spans > 1: - raise ValueError( - "Revolutions are only supported for angles up to 180 " - "degrees for Bezier type splines as they consist of only " - "one knot span" - ) - - # Determine auxiliary values - rot_a = angle / (2 * n_knot_spans) - half_counter_angle = PI / 2 - rot_a - weight = np.sin(half_counter_angle) - factor = 1 / weight - - # Determine rotation matrix - rotation_matrix = utils.arr.rotation_matrix_around_axis( - axis=axis, rotation=rot_a, degree=False - ).T - - # Start Extrusion - spline_dict = dict() - - spline_dict["degrees"] = np.concatenate((spline.degrees, [2])) - - spline_dict["control_points"] = cps - end_points = cps - for i_segment in range(n_knot_spans): - # Rotate around axis - mid_points = np.matmul(end_points, rotation_matrix) - end_points = np.matmul(mid_points, rotation_matrix) - # Move away from axis using dot-product tricks - if problem_dimension == 3: - mp_scale = axis * np.dot(mid_points, axis).reshape(-1, 1) - mid_points = (mid_points - mp_scale) * factor + mp_scale - else: - mid_points *= factor - spline_dict["control_points"] = np.concatenate( - (spline_dict["control_points"], mid_points, end_points) - ) - - if spline.has_knot_vectors: - kv = [0, 0, 0] - [kv.extend([i + 1, i + 1]) for i in range(n_knot_spans - 1)] - spline_dict["knot_vectors"] = spline.knot_vectors + [ - kv + [n_knot_spans] * 3 - ] - if spline.is_rational: - mid_weights = spline.weights * weight - spline_dict["weights"] = spline.weights - for i_segment in range(n_knot_spans): - spline_dict["weights"] = np.concatenate( - (spline_dict["weights"], mid_weights, spline.weights) - ) - else: - utils.log.debug( - "True revolutions are only possible for rational spline types.", - "Creating Approximation.", - ) - - if center is not None: - spline_dict["control_points"] += center - - return type(spline)(**spline_dict) - - -def from_bounds(parametric_bounds, physical_bounds): - """Creates a minimal spline with given parametric bounds, physical bounds. - Physical bounds can have less or equal number of - dimension as parametric bounds. (Greater is not supported) - - Parameters - ----------- - parametric_bounds: (2, n) array-like - physical_bounds: (2, n) array-like - - Returns - -------- - spline: BSpline - """ - physical_bounds = np.asanyarray(physical_bounds).reshape(2, -1) - parametric_bounds = np.asanyarray(parametric_bounds).reshape(2, -1) - - # get correctly sized bez box - phys_size = physical_bounds[1] - physical_bounds[0] - - # minimal bspline - bspline_box = box(*phys_size).bspline # kvs are in [0, 1] - bspline_box.cps += physical_bounds[0] # apply offset - - # update parametric bounds - for i, kv in enumerate(bspline_box.kvs): - # apply scale and offset - new_kv = (kv * parametric_bounds[1][i]) + parametric_bounds[0][i] - bspline_box.kvs[i] = new_kv - - return bspline_box - - -def parametric_view(spline, axes=True): - """Create parametric view of given spline. Previously called - `naive_spline()`. Degrees are always 1 and knot multiplicity is not - preserved. Returns BSpline, as BSpline and NURBS should look the same as - parametric view. - Will take shallow copy of underlying data of spline_data and show_options - from original spline. - - Parameters - ----------- - spline: BSpline or NURBS - axes: bool - If True, will configure axes settings, it is supported. - - Returns - -------- - para_spline: BSpline - """ - p_bounds = spline.parametric_bounds - para_spline = from_bounds( - parametric_bounds=p_bounds, physical_bounds=p_bounds - ) - - # loop through knot vectors and insert missing knots - for i, kv in enumerate(spline.unique_knots): - if len(kv) > 2: - para_spline.insert_knots(i, kv[1:-1]) - - # take shallow copy - para_spline._spline_data._saved = spline.spline_data._saved.copy() - para_spline._show_options._options[ - para_spline._show_options._backend - ] = spline.show_options._options[spline._show_options._backend].copy() - - if axes and "axes" in spline.show_options.valid_keys(): - # configure axes - bs = p_bounds - bs_diff_001 = (bs[1] - bs[0]) * 0.001 - lower_b = bs[0] - bs_diff_001 - upper_b = bs[1] + bs_diff_001 - axes_config = dict( - xtitle="u", - ytitle="v", - xrange=[lower_b[0], upper_b[0]], - yrange=[lower_b[1], upper_b[1]], - tip_size=0, - xminor_ticks=3, - yminor_ticks=3, - xygrid=False, - yzgrid=False, - ) - if spline.para_dim == 3: - axes_config.update(ztitle="w") - axes_config.update(zrange=[lower_b[2], upper_b[2]]) - axes_config.update(zminor_ticks=3) - axes_config.update(zxgrid=False) - - para_spline.show_options["axes"] = axes_config - # it is a view, so cps won't be realistic - para_spline.show_options["control_points"] = False - para_spline.show_options["lighting"] = "off" - - return para_spline - - -def line(points): - """Create a spline with the provided points as control points. - - Parameters - ---------- - points: (n, d) numpy.ndarray - npoints x ndims array of control points - - Returns - ------- - line: BSpline - Spline degree [1]. - """ - from gustaf import BSpline - - # lines have degree 1 - degree = 1 - - cps = np.array(points) - nknots = cps.shape[0] + degree + 1 - - knots = np.concatenate( - ( - np.full(degree, 0.0), - np.linspace(0.0, 1.0, nknots - 2 * degree), - np.full(degree, 1.0), - ) - ) - - spline = BSpline( - control_points=cps, knot_vectors=[knots], degrees=[degree] - ) - - return spline - - -def arc( - radius=1.0, - angle=90.0, - n_knot_spans=-1, - start_angle=0.0, - degree=True, -): - """Creates a 1-D arc as Rational Bezier or NURBS with given radius and - angle. The arc lies in the x-y plane and rotates around the z-axis. - - Parameters - ---------- - radius : float, optional - radius of the arc, defaults to 1 - angle : float, optional - angle of the section of the arc, defaults to 90 degrees - n_knot_spans : int - Number of knot spans, by default minimum number for angle is used. - start_angle : float, optional - starting point of the angle, by default 0. - degree: bool, optional - degrees for angle used, by default True - - Returns - ------- - arc: NURBS or RationalBezier - """ - from gustaf import RationalBezier - - # Define point spline of degree 0 at starting point of the arc - if degree: - start_angle = np.radians(start_angle) - angle = np.radians(angle) - start_point = [radius * np.cos(start_angle), radius * np.sin(start_angle)] - point_spline = RationalBezier( - degrees=[0], control_points=[start_point], weights=[1.0] - ) - # Bezier splines only support angles lower than 180 degrees - if abs(angle) >= np.pi or n_knot_spans > 1: - point_spline = point_spline.nurbs - - # Revolve - set degree to False, since all the angles are converted to rad - arc_attrib = point_spline.create.revolved( - angle=angle, n_knot_spans=n_knot_spans, degree=False - ).todict() - # Remove the first parametric dimensions, which is only a point and - # only used for the revolution - arc_attrib["degrees"] = list(arc_attrib["degrees"])[1:] - if point_spline.has_knot_vectors: - arc_attrib["knot_vectors"] = list(arc_attrib["knot_vectors"])[1:] - - return type(point_spline)(**arc_attrib) - - -def circle(radius=1.0, n_knot_spans=3): - """Circle (parametric dim = 1) with radius r in the x-y plane around the - origin. The spline has an open knot vector and degree 2. - - Parameters - ---------- - radius : float, optional - radius, defaults to one - n_knots_spans : int, optional - number of knot spans, defaults to 3 - - Returns - ------- - circle: NURBS - """ - return arc(radius=radius, angle=360, n_knot_spans=n_knot_spans) - - -def box(*lengths): - """ND box (hyper rectangle). - - Parameters - ---------- - *lengths: list(float) - - Returns - ------- - nd_box: Bezier - """ - from gustaf import Bezier - - # may dim check here? - # starting point - nd_box = Bezier(degrees=[1], control_points=[[0], [lengths[0]]]) - # use extrude - for i, l in enumerate(lengths[1:]): - nd_box = nd_box.create.extruded([0] * int(i + 1) + [l]) - - return nd_box - - -def plate(radius=1.0): - """Creates a biquadratic 2-D spline in the shape of a plate with given - radius. - - Parameters - ---------- - radius : float, optional - Radius of the plate, defaults to one - - Returns - ------- - plate: RationalBezier - """ - from gustaf.spline import RationalBezier - - degrees = [2, 2] - control_points = ( - np.array( - [ - [-0.5, -0.5], - [0.0, -1.0], - [0.5, -0.5], - [-1.0, 0.0], - [0.0, 0.0], - [1.0, 0.0], - [-0.5, 0.5], - [0.0, 1.0], - [0.5, 0.5], - ] - ) - * radius - ) - weights = np.tile([1.0, 1 / np.sqrt(2)], 5)[:-1] - - return RationalBezier( - degrees=degrees, control_points=control_points, weights=weights - ) - - -def disk( - outer_radius, - inner_radius=None, - angle=360.0, - n_knot_spans=4, - degree=True, -): - """Surface spline describing a potentially hollow disk with quadratic - degree along curved dimension and linear along thickness. The angle - describes the returned part of the disk. - - Parameters - ---------- - outer_radius : float - Outer radius of the disk - inner_radius : float, optional - Inner radius of the disk, in case of hollow disk, by default 0. - angle : float, optional - Rotational angle, by default 360. describing a complete revolution - n_knot_spans : int, optional - Number of knot spans, by default 4 - - Returns - ------- - disk: NURBS - Surface NURBS of degrees (1,2) - """ - - from gustaf.spline import NURBS - - if inner_radius is None: - inner_radius = 0.0 - - cps = np.array([[inner_radius, 0.0], [outer_radius, 0.0]]) - weights = np.ones([cps.shape[0]]) - knots = np.repeat([0.0, 1.0], 2) - - return NURBS( - control_points=cps, knot_vectors=[knots], degrees=[1], weights=weights - ).create.revolved(angle=angle, n_knot_spans=n_knot_spans, degree=degree) - - -def torus( - torus_radius, - section_outer_radius, - section_inner_radius=None, - torus_angle=None, - section_angle=None, - section_n_knot_spans=4, - torus_n_knot_spans=4, - degree=True, -): - """Creates a volumetric NURBS spline describing a torus revolved around the - x-axis. Possible cross-sections are plate, disk (yielding a tube) and - section of a disk. - - Parameters - ---------- - torus_radius : float - Radius of the torus - section_outer_radius : float - Radius of the section of the torus - section_inner_radius : float, optional - Inner radius in case of hollow torus, by default 0. - torus_angle : float, optional - Rotational angle of the torus, by default None, giving a complete - revolution - section_angle : float, optional - Rotational angle, by default None, yielding a complete revolution - section_n_knot_spans : float, optional - Number of knot spans along the cross-section, by default 4 - torus_n_knot_spans : float, optional - Number of knot spans along the torus, by default 4 - - Returns - ------- - torus: NURBS - Volumetric spline in the shape of a torus with degrees (1,2,2) - """ - - if torus_angle is None: - torus_angle = 2 * np.pi - degree = False - - if section_angle is None: - section_angle = 2 * np.pi - section_angle_flag = False - degree = False - else: - section_angle_flag = True - - if section_inner_radius is None: - section_inner_radius = 0 - section_inner_radius_flag = False - else: - section_inner_radius_flag = True - - # Create the cross-section - if not section_angle_flag and not section_inner_radius_flag: - cross_section = plate(section_outer_radius) - # For more than 180 degree only NURBS can be used - if abs(torus_angle) >= np.pi: - cross_section = cross_section.nurbs - else: - cross_section = disk( - outer_radius=section_outer_radius, - inner_radius=section_inner_radius, - n_knot_spans=section_n_knot_spans, - angle=section_angle, - degree=degree, - ) - - # Create a surface spline representing a disk and move it from the origin - cross_section.control_points[:, 1] += torus_radius - - return cross_section.create.revolved( - axis=[1.0, 0, 0], - center=np.zeros(3), - angle=torus_angle, - n_knot_spans=torus_n_knot_spans, - degree=degree, - ) - - -def sphere( - outer_radius, - inner_radius=None, - angle=360.0, - n_knot_spans=-1, - degree=True, -): - """Creates a volumetric spline describing a sphere with radius R. - - Parameters - ---------- - outer_radius : float - Outer radius of the sphere - inner_radius : float, optional - Inner radius of the potentially hollow sphere. - angle : float - Rotational angle around x-axis, by default each 360 - (describing a complete revolution) - n_knot_spans : int - Number of knot spans - - Returns - ------- - sphere: NURBS - Volumetric NURBS with degrees (1,2,2) - """ - - if inner_radius is None: - sphere = plate(outer_radius).nurbs.create.revolved( - axis=[1, 0, 0], - center=[0, 0, 0], - angle=angle, - n_knot_spans=n_knot_spans, - degree=degree, - ) - else: - inner_radius = float(inner_radius) - sphere = disk(outer_radius, inner_radius).nurbs.create.revolved( - angle=angle, n_knot_spans=n_knot_spans, degree=degree - ) - return sphere - - -def cone( - outer_radius, - height, - inner_radius=None, - volumetric=True, - angle=360.0, - degree=True, -): - """Creates a cone with circular base. - - Parameters - ---------- - radius : float - Radius of the base - height : float - Height of the cone - volumetric : bool, optional - Parameter whether surface or volume spline, by default True - angle : float - Rotation angle in degrees, only used for solid model - - Returns - ------- - cone: NURBS - Volumetric or surface NURBS describing a cone - """ - - if volumetric: - ground = disk( - outer_radius, inner_radius=inner_radius, angle=angle, degree=degree - ) - else: - ground = circle(outer_radius) - - # Extrude in z - cone = ground.create.extruded([0, 0, height]) - # Move all upper control points to one - cone.control_points[np.isclose(cone.control_points[:, -1], height)] = [ - 0, - 0, - height, - ] - - return cone - - -def pyramid(width, length, height): - """Creates a volumetric spline in the shape of a pyramid with linear degree - in every direction. - - Parameters - ---------- - width : float - Dimension of base in x-axis - length : float - Dimension of base in y-axis - height : float - Height in z-direction - - Returns - ------- - pyramid: BSpline - Volumetric linear spline in the shape of a pyramid - """ - - # Create box - p = box(width, length, height) - - # Collapse all upper points on one control point - p.control_points[4:, :] = [width / 2, length / 2, height] - - return p - - -class Creator: - """Helper class to build new splines from existing geometries. - - Examples - --------- - >>> my_spline = - >>> spline_faces = my_spline.create.extrude(vector=[3,1,3]) - """ - - def __init__(self, spl): - self.spline = spl - - def extruded(self, *args, **kwargs): - return extruded(self.spline, *args, **kwargs) - - def revolved(self, *args, **kwargs): - return revolved(self.spline, *args, **kwargs) - - def parametric_view(self, *args, **kwargs): - return parametric_view(self.spline, *args, **kwargs) diff --git a/gustaf/spline/extract.py b/gustaf/spline/extract.py deleted file mode 100644 index 8db2a9a7f..000000000 --- a/gustaf/spline/extract.py +++ /dev/null @@ -1,569 +0,0 @@ -"""gustaf/spline/extract.py. - -Extract operations. Both discrete and spline extraction. -""" - -import itertools - -import numpy as np - -from gustaf import settings, utils -from gustaf.edges import Edges -from gustaf.faces import Faces -from gustaf.vertices import Vertices -from gustaf.volumes import Volumes - - -def edges( - spline, - resolution=100, - extract_dim=None, - extract_knot=None, - all_knots=False, -): - """Extract edges (lines) from a given spline. Only entity you can extract - without dimension limit. - - Parameters - ----------- - spline: Spline - resolution: int - extract_dim: int - Parametric dimension to extract. - extract_knot: list - (spline.para_dim - 1,) shaped knot location along extract_dim - all_knots: bool - Switch to allow all knot-line extraction. - - Returns - -------- - edges: Edges - """ - if not all_knots: - resolution = int(resolution) - - if spline.para_dim == 1: - return Edges( - vertices=spline.sample(resolution), - edges=utils.connec.range_to_edges( - (0, resolution), - closed=False, - ), - ) - - else: - # This should be possible for spline of any dimension. - # As long as it satisfies the following condition - if extract_knot is not None: - if len(extract_knot) != spline.para_dim - 1: - raise ValueError( - "Must satisfy len(extract_knot) == spline.para_dim -1." - ) - - # This may take awhile. - if all_knots: - temp_edges = [] # edges' is not a valid syntax - unique_knots = np.array(spline.unique_knots, dtype=object) - for i in range(spline.para_dim): - mask = np.ones(spline.para_dim, dtype=bool) - mask[i] = False - # gather knots along current knot - extract_knot_queries = list( - itertools.product(*unique_knots[mask]) - ) - - for ekq in extract_knot_queries: - temp_edges.append( - edges(spline, resolution[i], i, ekq, False) - ) - - return Edges.concat(temp_edges) - - # Get parametric points to extract - queries = np.empty( - (resolution, spline.para_dim), - dtype="float64", # hardcoded for splinelibpy - order="C", # hardcoded for splinelibpy - ) - # get ~extract_dim - not_ed = np.arange(spline.para_dim).tolist() - not_ed.pop(extract_dim) - queries[:, not_ed] = extract_knot - - # get knot extrema - uniq_knots = spline.unique_knots[extract_dim] - min_knot_position = min(uniq_knots) - max_knot_position = max(uniq_knots) - - queries[:, extract_dim] = np.linspace( - min_knot_position, - max_knot_position, - resolution, - ) - - return Edges( - vertices=spline.evaluate(queries), - edges=utils.connec.range_to_edges( - (0, resolution), - closed=False, - ), - ) - - -def faces( - spline, - resolutions, - watertight=True, -): - """Extract faces from spline. Valid iff para_dim is one of the followings: - {2, 3}. In case of {3}, it will return only surfaces. If internal faces are - desired, used `spline.extract.volumes().faces()`. Note that dimension - higher than 3 is not showable. - - Parameters - ----------- - spline: BSpline or NURBS - resolutions: int or list - watertight: bool - Default is True. Only related to para_dim = 3 splines. If False, - overlapping vertices at boundary edges won't be merged. - - Returns - -------- - faces: faces - """ - resolutions = utils.arr.enforce_len(resolutions, spline.para_dim) - - if spline.para_dim == 2: - return Faces( - vertices=spline.sample(resolutions), - faces=utils.connec.make_quad_faces(resolutions), - ) - - elif spline.para_dim == 3: - # TODO: use spline extraction routine to first extract - # spline, extract faces, merge vertices. - - # Spline to surfaces - vertices = [] - faces = [] - offset = 0 - # accommodate bezier Splines - u_kvs = spline.unique_knots - - for i in range(spline.para_dim): - extract = i - # Get extracting dimension - extract_along = [0, 1, 2] - extract_along.pop(extract) - - # Extract range - extract_range = [ - [ - min(u_kvs[extract_along[0]]), - max(u_kvs[extract_along[0]]), - ], - [ - min(u_kvs[extract_along[1]]), - max(u_kvs[extract_along[1]]), - ], - ] - - extract_list = [ - min(u_kvs[extract]), - max(u_kvs[extract]), - ] - - # surface point queries (spq) - spq = np.linspace( - extract_range[0][0], - extract_range[0][1], - resolutions[extract_along[0]], - ).reshape(-1, 1) - - # expand horizontally and init with 1 - spq = np.hstack((spq, np.ones((len(spq), 1)))) - spq = np.vstack( - np.linspace( - spq * [1, extract_range[1][0]], - spq * [1, extract_range[1][1]], - resolutions[extract_along[1]], - ) - ) - - # expand horizontally and init with 1 - spq = np.hstack((spq, np.ones((len(spq), 1)))) - spq = np.vstack( - np.linspace( - spq * [1, 1, extract_list[0]], - spq * [1, 1, extract_list[1]], - 2, - ) - ) - - surface_point_queries = utils.arr.make_c_contiguous( - spq, - dtype="float64", - ) - sorted_ids = np.argsort( - [extract_along[0], extract_along[1], extract] - ) - surface_point_queries = surface_point_queries[:, sorted_ids] - - vertices.append( - spline.evaluate( - surface_point_queries[ - : int(surface_point_queries.shape[0] / 2) - ] - ) - ) - - if len(faces) != 0: - offset = faces[-1].max() + 1 - - tmp_faces = utils.connec.make_quad_faces( - [ - resolutions[extract_along[0]], - resolutions[extract_along[1]], - ] - ) - - faces.append(tmp_faces + int(offset)) - - vertices.append( - spline.evaluate( - surface_point_queries[ - int(surface_point_queries.shape[0] / 2) : - ] - ) - ) - - offset = faces[-1].max() + 1 - - faces.append(tmp_faces + int(offset)) - - # make faces and merge vertices before returning - f = Faces(vertices=np.vstack(vertices), faces=np.vstack(faces)) - - if watertight: - f.merge_vertices() - - return f - - else: - raise ValueError("Invalid spline to make faces.") - - -def volumes(spline, resolutions): - """Extract volumes from spline. Valid iff spline.para_dim == 3. - - Parameters - ----------- - spline: BSpline or NURBS - resolutions: - - Returns - -------- - volumes: Volumes - """ - if spline.para_dim != 3: - raise ValueError( - "Volume extraction from a spline is only valid for " - "para_dim: 3 dim: 3 splines." - ) - - return Volumes( - vertices=spline.sample(resolutions), - volumes=utils.connec.make_hexa_volumes(resolutions), - ) - - -def control_points(spline): - """Extracts control points and return as vertices. Same can be achieved by - doing `gustaf.Vertices(spline.control_points)` - - Parameters - ----------- - spline: BSpline or NURBS - - Returns - -------- - cps_as_Vertices: Vertices - """ - return Vertices(spline.control_points) - - -def control_edges(spline): - """Extract control edges (mesh). Valid iff para_dim is 1. - - Parameters - ----------- - edges: BSpline or NURBS - - Returns - -------- - edges: Edges - """ - if spline.para_dim != 1: - raise ValueError("Invalid spline type!") - - return Edges( - vertices=spline.control_points, - edges=utils.connec.range_to_edges( - len(spline.control_points), closed=False - ), - ) - - -def control_faces(spline): - """Extract control face (mesh). Valid iff para_dim is 2. - - Parameters - ----------- - spline: BSpline or NURBS - - Returns - -------- - faces: Faces - """ - if spline.para_dim != 2: - raise ValueError("Invalid spline type!") - - return Faces( - vertices=spline.control_points, - faces=utils.connec.make_quad_faces(spline.control_mesh_resolutions), - ) - - -def control_volumes(spline): - """Extract control volumes (mesh). Valid iff para_dim is 3. - - Parameters - ----------- - spline: BSpline or NURBS - - Returns - -------- - volumes: Volumes - """ - if spline.para_dim != 3: - raise ValueError("Invalid spline type!") - - return Volumes( - vertices=spline.control_points, - volumes=utils.connec.make_hexa_volumes( - spline.control_mesh_resolutions - ), - ) - - -def control_mesh(spline): - """Calls control_edges, control_faces, control_volumes based on current - spline. - - Parameters - ----------- - None - - Returns - -------- - control_mesh: Edges or Faces or Volumes - """ - if spline.para_dim == 1: - return control_edges(spline) - elif spline.para_dim == 2: - return control_faces(spline) - elif spline.para_dim == 3: - return control_volumes(spline) - else: - raise ValueError( - "Invalid para_dim to extract control_mesh. " "Supports 1 to 3." - ) - - -def spline(spline, para_dim, split_plane): - """Extract a sub spline from a given representation. - - Parameters - ---------- - para_dim : int - parametric dimension to be extract ted - split_plane : float / tuple - interval or value in parametric space to be extracted from the spline - representation - - Returns - ------- - spline - """ - from gustaf.spline.base import GustafSpline - - # Check type - if not issubclass(type(spline), GustafSpline): - raise TypeError("Unknown spline representation passed to sub spline") - - # Check arguments for sanity - if para_dim > spline.para_dim: - raise ValueError( - "Requested parametric dimension exceeds spline's parametric" - " dimensionality." - ) - if isinstance(split_plane, list): - if not ( - (len(split_plane) == 2) - and (isinstance(split_plane[0], float)) - and (isinstance(split_plane[0], float)) - ): - raise ValueError( - "Range must be float or tuple of floats with length 2" - ) - elif not isinstance(split_plane, float): - raise ValueError( - "Range must be float or tuple of floats with length 2" - ) - else: - # Convert float to tuple to facilitate - split_plane = list([split_plane]) - - # Check if is bezier-type - is_bezier = "Bezier" in spline.whatami - is_rational = "weights" in spline.required_properties - if is_bezier: - if is_rational: - spline_copy = spline.nurbs - else: - spline_copy = spline.bspline - else: - spline_copy = spline.copy() - - for _ in range(spline_copy.degrees[para_dim]): - # Will do nothing if spline already has sufficient number of knots - # at given position - spline_copy.insert_knots(para_dim, split_plane) - - # Start extraction - cps_res = spline_copy.control_mesh_resolutions - # start and end id. indices correspond to [first dim][first appearance] - start_id = np.where( - abs(spline_copy.knot_vectors[para_dim] - split_plane[0]) - < settings.TOLERANCE - )[0][0] - end_id = np.where( - abs(spline_copy.knot_vectors[para_dim] - split_plane[-1]) - < settings.TOLERANCE - )[0][0] - para_dim_ids = np.arange(np.prod(cps_res)) - for i_pd in range(para_dim): - para_dim_ids -= para_dim_ids % cps_res[i_pd] - para_dim_ids = para_dim_ids // cps_res[i_pd] - # indices are shifted by one - para_dim_ids = para_dim_ids % cps_res[para_dim] + 1 - - # Return new_spline - spline_info = {} - spline_info["control_points"] = spline_copy.cps[ - (para_dim_ids >= start_id) & (para_dim_ids <= end_id) - ] - spline_info["degrees"] = spline_copy.degrees.tolist() - if start_id == end_id: - spline_info["degrees"].pop(para_dim) - if not is_bezier: - spline_info["knot_vectors"] = spline_copy.knot_vectors.copy() - if start_id == end_id: - spline_info["knot_vectors"].pop(para_dim) - else: - start_knot = spline_copy.knot_vectors[para_dim][start_id] - knots_in_between = spline_copy.knot_vectors[para_dim][ - start_id : (end_id + spline_copy.degrees[para_dim]) - ] - end_knot = spline_copy.knot_vectors[para_dim][ - (end_id + spline_copy.degrees[para_dim] - 1) - ] - - spline_info["knot_vectors"][para_dim] = np.concatenate( - ([start_knot], knots_in_between, [end_knot]) - ) - - if is_rational: - spline_info["weights"] = spline_copy.weights[ - (para_dim_ids >= start_id) & (para_dim_ids <= end_id) - ] - - return type(spline)(**spline_info) - - -class Extractor: - """Helper class to allow direct extraction from spline obj (BSpline or - NURBS). Internal use only. - - Examples - --------- - >>> my_spline = - >>> spline_faces = my_spline.extract.faces() - """ - - def __init__(self, spl): - self._spline = spl - - def edges(self, *args, **kwargs): - return edges(self._spline, *args, **kwargs) - - def faces(self, *args, **kwargs): - return faces(self._spline, *args, **kwargs) - - def volumes(self, *args, **kwargs): - return volumes(self._spline, *args, **kwargs) - - def control_points(self): - return control_points(self._spline) - - def control_edges(self): - return control_edges(self._spline) - - def control_faces(self): - return control_faces(self._spline) - - def control_volumes(self): - return control_volumes(self._spline) - - def control_mesh(self): - return control_mesh(self._spline) - - def beziers(self): - if not self._spline.has_knot_vectors: - return [self._spline] - return self._spline.extract_bezier_patches() - - def boundaries(self, *args, **kwargs): - return self._spline.extract_boundaries(*args, **kwargs) - - def spline(self, splitting_plane=None, interval=None): - """Extract a spline from a spline. - - Use a (number of) splitting planes to extract a subsection from the - parametric domain of it. - - Parameters - ---------- - splitting_plane : int / dictionary (int : (float)) - if integer : parametric dimension to be extracted - if dictionary : list of splitting planes and ranges to be passed - interval : float / tuple - interval or value in parametric space to be extracted from the - spline representation - Returns - ------- - spline - """ - if isinstance(splitting_plane, dict): - if interval is not None: - raise ValueError("Arguments incompatible expect dictionary") - splitting_plane = dict( - sorted(splitting_plane.items(), key=lambda x: x[0])[::-1] - ) - spline_copy = self._spline.copy() - for key, item in splitting_plane.items(): - spline_copy = spline(spline_copy, key, item) - return spline_copy - else: - return spline(self._spline, splitting_plane, interval) diff --git a/gustaf/spline/ffd.py b/gustaf/spline/ffd.py deleted file mode 100644 index fd6a3aaa5..000000000 --- a/gustaf/spline/ffd.py +++ /dev/null @@ -1,500 +0,0 @@ -"""gustaf/gustaf/ffd.py. - -Freeform Deformation! - - -Adaptation of previous implementation in internal python package gustav by -Jaewook Lee. -""" -from typing import Any, List, Optional, Union - -import numpy as np - -from gustaf import settings -from gustaf._base import GustafBase -from gustaf._typing import MESH_TYPES, is_mesh -from gustaf.show import show_vedo -from gustaf.spline.base import NURBS, Bezier, BSpline, RationalBezier -from gustaf.spline.create import from_bounds - -SPLINE_TYPES = Union[Bezier, RationalBezier, BSpline, NURBS] - - -def is_spline(candidate: Any) -> bool: - """This function checks if the candidate is a spline. - - Parameters - ----------- - candidate: Any - object to check for being a spline. - - Returns - -------- - is_mesh: bool - Is the given object a sline. - """ - return isinstance(candidate, SPLINE_TYPES.__args__) - - -class FFD(GustafBase): - def __init__( - self, - mesh: Optional[MESH_TYPES] = None, - spline: Optional[SPLINE_TYPES] = None, - ): - """ - Free-form deformation is a method used to deform an object by a - deformation function. In our case the object is given via a mesh, the - currently supported mesh-types are given by the variable - :py:const:`gustaf._typing.MESH_TYPES`, and the deformations function - by a spline, supported splines are given by the variable - :py:const.:`gustaf._typing.SPLINE_TYPES`. The splines parametric - dimension will be scaled in to a unit-hypercube as well as the - original meshes vertices. The outline of the resulting mesh is given - by the physical space of the spline. - - The FFD class provides functions to modify the spline by completely - overwriting the spline whole spline or parts of it. To obtain the - deformed mesh mapped into the latest spline, retrieve the mesh - attribute. - - Please not that even though an object of the class can be initiated - without a mesh, it is not possible to compute the deformation without - one. Please ensure that at least a mesh is defined before retrieving - the (deformed) mesh. If only a mesh is provided a default spline where - the geometric dimensions have the bounds of the mesh is defined. - - A previously available partial FFD is currently not implemented, and - is planned to be implemented in a separate class (LocalFFD). - - Parameters - ---------- - mesh: Optional[MESH_TYPES] - Mesh used in the FFD. Defaults to None. - spline: Optional[SPLINE_TYPES] - Spline used in the FFD. Defaults to None. - - Class Attributes - ---------------- - _spline: SPLINE_TYPES - Internal current spline - _mesh: MESH_TYPES - unscaled base mesh - _q_vertices: np.ndarray (n, dim) - Scaled vertices of the base mesh - - Returns - ------- - None - """ - # Use property definitions to store the values - self._spline: SPLINE_TYPES = None - self._mesh: MESH_TYPES = None - self._o_mesh: MESH_TYPES = None - self._q_vertices: np.ndarray = None - - if spline is not None: - self.spline = spline - if mesh is not None: - self.mesh = mesh - - @property - def mesh(self) -> MESH_TYPES: - """Returns copy of current mesh. Before copying, it applies - deformation. - - Returns - ------- - MESH_TYPES - Current Mesh with the deformation according to the current spline. - """ - self._deform() - return self._mesh.copy() - - @mesh.setter - def mesh(self, mesh: MESH_TYPES): - """Sets mesh. If it is first time, the copy of it will be saved as - original mesh. If spline is already defined and in transformed status, - it applies transformation directly. - - Parameters - ----------- - mesh: MESH_TYPES - Mesh used for the FFD - - Returns - -------- - None - """ - if not is_mesh(mesh): - raise ValueError( - "Mesh definition does not conform. Please provide a " - "correct mesh definition." - ) - if self._spline is None: - # Define a default spline if mesh is given but no spline - par_dim = mesh.vertices.shape[1] - self.spline = from_bounds( - [[0] * par_dim, [1] * par_dim], mesh.bounds() - ) - self._logi("Setting mesh.") - self._logi("Mesh Info:") - self._logi(f" Vertices: {mesh.vertices.shape}.") - self._logi(f" Bounds: {mesh.bounds()}.") - self._o_mesh = mesh.copy() # we keep original copy for visualization - self._mesh = mesh.copy() # another copy for current status. - - self._check_dimensions() - - self._scale_mesh_vertices() - if self._spline: - self._spline._data["gustaf_ffd_computed"] = False - - @property - def spline(self) -> SPLINE_TYPES: - """Returns a copy of the spline. Please use the setter to explicitly - make changes to the spline. - - Parameters - ----------- - None - - Returns - -------- - self._spline: Spline - """ - return self._spline - - @spline.setter - def spline(self, spline: SPLINE_TYPES): - """Sets spline. The spline parametric range bounds will be converted - into the bounds [0,1]^para_dim. - - Parameters - ----------- - spline: SPLINE_TYPES - New Spline for the next deformation - - Returns - -------- - None - """ - if not is_spline(spline): - raise ValueError( - "Spline definition does not conform. Please provide a " - "correct spline definition." - ) - self._spline = spline - - def _check_dimensions(self) -> bool: - """Checks if the dimension of the spline and the mesh match and - - Returns: - bool: _description_ - """ - messages = [] - # Checks dimensions and ranges critical for a correct FFD calculation - if self._spline and not self._spline.para_dim == self._spline.dim: - messages.append( - f"The parametric ({self._spline.para_dim}) and geometric " - f"({self._spline.dim}) dimensions of the " - "spline are not the same." - ) - if ( - self._spline - and self._mesh - and not self._spline.dim == self._mesh.vertices.shape[1] - ): - messages.append( - "The geometric dimensions of the spline " - f"({self._spline.dim}) and the dimension of the mesh" - f"({self._mesh.vertices.shape[1]}) are not the same." - ) - if len(messages) > 0: - raise RuntimeError( - "Can not perform FFD due to spline and mesh " - "dimension mismatch. The following dimension mismatches:" - f"{messages}." - ) - - def _scale_mesh_vertices(self): - """Scales the mesh vertices into the dimension of a hypercube and save - them in self._q_vertices.""" - self._logd("Fitting mesh into spline's parametric space.") - - self._q_vertices = self._mesh.vertices.copy() - - original_mesh_bounds = self._mesh.bounds() - - # save mesh offset and scale for reasons - self._mesh_offset = original_mesh_bounds[0] - self._mesh_scale = 1 / ( - original_mesh_bounds[1] - original_mesh_bounds[0] - ) - - # scale and offset vertices coordinates - self._q_vertices -= self._mesh_offset - self._q_vertices *= self._mesh_scale - - self._logd("Successfully scaled and transformed mesh vertices!") - - def _deform(self): - """Deforms mesh if spline or mesh changes were detected since last - calculation. Meant for internal use. - - Parameters - ----------- - None - - Returns - -------- - None - """ - if self._mesh is None or self._spline is None: - raise RuntimeError( - "Can't perform deformation for the FFD, since either the " - "spline or(and) the mesh are not yet defined. " - "Please set either spline or mesh." - ) - if self._spline._data.get("gustaf_ffd_computed", False): - return None - - spline = self._spline.copy() - if spline.has_knot_vectors: - spline.normalize_knot_vectors() - - self._check_dimensions() - - self._logd("Applying FFD: Transforming vertices") - - # Here, we take _q_vertices, due to possible scale/offset. - self._mesh.vertices = spline.evaluate(self._q_vertices) - self._logd("FFD successful.") - - self._spline._data["gustaf_ffd_computed"] = True - - @property - def control_points(self): - """Returns current spline's control points. The control points can be - directly updated with this. - - Returns - -------- - self._spline.control_points: np.ndarray - """ - return self._spline.control_points - - @control_points.setter - def control_points( - self, control_points: Union[List[List[float]], np.ndarray] - ): - """Sets control points and deforms mesh. - - Parameters - ----------- - control_points: np.ndarray - - Returns - -------- - None - """ - if self._spline is None: - raise ValueError( - "Please set a spline before setting new control points." - ) - if self._spline.control_points.shape != np.array(control_points).shape: - raise ValueError( - "Given control points' shape does not match current ones!" - ) - self._spline.control_points = control_points.copy() - self._logd("Set new control points.") - - def elevate_degree(self, *args, **kwargs): - """Wrapper for Spline.elevate_degree. - - Parameters - ----------- - *args: - **kwargs: - - Returns - -------- - None - """ - if self._spline is None: - raise ValueError("Please set a spline before using this function.") - if "knot_vectors" not in self._spline.required_properties: - raise NotImplementedError( - "Can not perform knot insertion on Bezier spline." - ) - self._spline.elevate_degree(*args, **kwargs) - - def insert_knots(self, parametric_dimension, knots): - """Wrapper for Spline.insert_knots. - - Parameters - ----------- - *args: - **kwargs: - - Returns - -------- - None - """ - if self._spline is None: - raise ValueError("Please set a spline before using this function.") - if "knot_vectors" not in self._spline.required_properties: - raise NotImplementedError( - "Can not perform knot insertion on Bezier spline." - ) - self._spline.insert_knots(parametric_dimension, knots) - - def remove_knots(self, parametric_dimension, knots, tolerance=1e-8): - """Wrapper for Spline.remove_knots. - - Parameters - ----------- - *args: - **kwargs: - - Returns - -------- - None - """ - if self._spline is None: - raise ValueError("Please set a spline before using this function.") - if "knot_vectors" not in self._spline.required_properties: - raise NotImplementedError( - "Can not perform knot insertion on Bezier spline." - ) - self._spline.remove_knots( - parametric_dimension, knots, tolerance=tolerance - ) - - def reduce_degree(self, *args, **kwargs): - """Wrapper for Spline.reduce_degree. - - Parameters - ----------- - *args: - **kwargs: - - Returns - -------- - None - """ - if self._spline is None: - raise ValueError("Please set a spline before using this function.") - if "knot_vectors" not in self._spline.required_properties: - raise NotImplementedError( - "Can not perform knot insertion on Bezier spline." - ) - self._spline.reduce_degree(*args, **kwargs) - - def show(self, **kwargs) -> Any: - """Visualize. Shows the deformed mesh and the current spline. Currently - visualization is limited to vedo. - - Parameters - ---------- - title: str - Title of the vedo window. Defaults to "gustaf - FFD". - return_showable: bool - If true returns a dict of the showable items. Defaults to False. - return_discrete: bool - Return dict of gustaf discrete objects, for example, - {Vertices, Edges, Faces}, instead of opening a window. - Defaults to False. - kwargs: Any - Arbitrary keyword arguments. These are passed onto the vedo - functions. Please be aware, that no checking of these are performed - in this function. - - Returns - ------- - Any: - Returns, if applicable, the vedo plotter. 'close=False' as argument - to get the plotter. - """ - if self._spline is None and self._mesh is None: - raise ValueError("Please set a mesh before calling show()") - backend = kwargs.pop("backend", None) - return_showable = kwargs.pop("return_showable", False) - return_discrete = kwargs.pop("return_discrete", False) - title = kwargs.pop("title", "gustaf - FFD") - - if return_discrete and return_showable: - raise ValueError( - "Either one of following params can be True: " - "{return_discrete, return_showable} " - "You've set both True." - ) - - if backend is None: - backend = settings.VISUALIZATION_BACKEND - - # prepare originals - o_mesh = self._o_mesh.copy() - # prepare deformed - d_mesh = self.mesh # copies - - things_to_show = dict() - # let's show faces at most, since volumes can take awhile - if o_mesh.kind == "volume": - # only outer faces. overwrite - o_mesh = o_mesh.to_faces(unique=False) - o_mesh.update_faces(o_mesh.single_faces()) - d_mesh = d_mesh.to_faces(unique=False) - d_mesh.update_faces(d_mesh.single_faces()) - - # update meshes - things_to_show.update(original_mesh=o_mesh) - things_to_show.update(original_description="Original Mesh") - things_to_show.update(deformed_mesh=d_mesh) - things_to_show.update(deformed_description="Deformed Mesh with Spline") - - # update spline - things_to_show.update(deformed_spline=self.spline) - - if return_discrete or not backend.startswith("vedo"): - # spline is strictly not discrete. - return things_to_show - - if return_showable: - # let's turn everything into showable and return - for k, v in things_to_show.items(): - if isinstance(v, GustafBase): - things_to_show[k] = v.showable() - - return things_to_show - - # current workaround to set spline's surface alpha correctly - # TODO: support this situation better - spl = things_to_show.pop("deformed_spline") - spl_showable = spl.showable(surface_alpha=0.3) - - return show_vedo( - [ - things_to_show[k] - for k in things_to_show.keys() - if k.startswith("original") - ], - [ - *[ - things_to_show[k] - for k in things_to_show.keys() - if k.startswith("deformed") - ], - *spl_showable.values(), - ], - title=title, - ) - - def showable(self, **kwargs): - """Returns a dictionary of showable items to describe the FFD at the - current state. - - See show() for more information. This function redirects to it - directly with the return_showable keyword set to True. - """ - return self.show(return_showable=True, **kwargs) diff --git a/gustaf/spline/microstructure/__init__.py b/gustaf/spline/microstructure/__init__.py deleted file mode 100644 index 65f17e7ee..000000000 --- a/gustaf/spline/microstructure/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""gustaf/spline/microstructure/__init__.py. - -Interface for tools and generators creating simple microstructures. -""" - -from gustaf.spline.microstructure import microstructure, tiles -from gustaf.spline.microstructure.microstructure import Microstructure - -__all__ = ["tiles", "microstructure", "Microstructure"] diff --git a/gustaf/spline/microstructure/microstructure.py b/gustaf/spline/microstructure/microstructure.py deleted file mode 100644 index 26535857f..000000000 --- a/gustaf/spline/microstructure/microstructure.py +++ /dev/null @@ -1,562 +0,0 @@ -import itertools - -import numpy as np - -from gustaf._base import GustafBase -from gustaf.show import show_vedo -from gustaf.spline import base - - -class Microstructure(GustafBase): - """Helper class to facilitatae the construction of microstructures.""" - - def __init__( - self, - deformation_function=None, - tiling=None, - microtile=None, - parametrization_function=None, - ): - """Helper class to facilitatae the construction of microstructures. - - Parameters - ---------- - deformation_function : spline - Outer function that describes the contour of the microstructured - geometry - tiling : list of integers - microtiles per parametric dimension - microtile : spline or list of splines - Representation of the building block defined in the unit cube - parametrization_function : Callable (optional) - Function to describe spline parameters - """ - if deformation_function is not None: - self.deformation_function = deformation_function - - if tiling is not None: - self.tiling = tiling - - if microtile is not None: - self.microtile = microtile - - if parametrization_function is not None: - self.parametrization_function = parametrization_function - - @property - def deformation_function(self): - """Deformation function defining the outer geometry (contour) of the - microstructure. - - Parameters - ---------- - None - - Returns - ------- - deformation_function : spline - """ - if hasattr(self, "_deformation_function"): - return self._deformation_function - else: - return None - - @deformation_function.setter - def deformation_function(self, deformation_function): - """Deformation function setter defining the outer geometry of the - microstructure. Must be spline type and as such inherit from - gustaf.GustafSpline. - - Parameters - ---------- - deformation_function : spline - - Returns - ------- - None - """ - if not issubclass(type(deformation_function), base.GustafSpline): - raise ValueError( - "Deformation function must be Gustaf-Spline." - " e.g. gustaf.NURBS" - ) - self._deformation_function = deformation_function - self._sanity_check() - - @property - def tiling(self): - """Number of microtiles per parametric dimension. - - Parameters - ---------- - None - - Returns - ------- - tiling : list - """ - if hasattr(self, "_tiling"): - return self._tiling - else: - return None - - @tiling.setter - def tiling(self, tiling): - """Setter for the tiling attribute, defining the number of microtiles - per parametric dimension. - - Parameters - ---------- - tiling : int / list - Number of tiles for each dimension respectively - Returns - ------- - None - """ - if not isinstance(tiling, list): - if not isinstance(tiling, int): - raise ValueError( - "Tiling mus be either list of integers of integer " "value" - ) - self._tiling = tiling - # Is defaulted to False using function arguments - self._sanity_check() - self._logd(f"Successfully set tiling to : {self.tiling}") - - @property - def microtile(self): - """Microtile that is either a spline, a list of splines, or a class - that provides a `create_tile` function.""" - if hasattr(self, "_microtile"): - return self._microtile - else: - return None - - @microtile.setter - def microtile(self, microtile): - """Setter for microtile. - - Microtile must be either a spline, a list of splines, or a class that - provides (at least) a `create_tile` function and a `dim` member. - - Parameters - ---------- - microtile : spline / list / user-object - arbitrary long list of splines that define the microtile - - Returns - ------- - None - """ - # place single tiles into a list to provide common interface - if isinstance(microtile, list) or issubclass( - type(microtile), base.GustafSpline - ): - microtile = self._make_microtilable(microtile) - # Assign Microtile object to member variable - self._microtile = microtile - - self._sanity_check() - - @property - def parametrization_function(self): - """Function, that - if required - parametrizes the microtiles. - - In order to use said function, the Microtile needs to provide a couple - of attributes: - - - evaluation_points - a list of points defined in the unit cube - that will be evaluated in the parametrization function to provide - the required set of data points - - parameter_space_dimension - dimensionality of the parametrization - function and number of design variables for said microtile - - Parameters - ---------- - None - - Returns - ------- - parametrization_function : Callable - Function that descibes the local tile parameters - """ - if hasattr(self, "_parametrization_function"): - return self._parametrization_function - else: - return None - - @parametrization_function.setter - def parametrization_function(self, parametrization_function): - if not callable(parametrization_function): - raise TypeError("parametrization_function must be callable") - self._parametrization_function = parametrization_function - self._sanity_check() - - def create(self, closing_face=None, knot_span_wise=None, **kwargs): - """Create a Microstructure. - - Parameters - ---------- - closing_face : string - If not None, Microtile must provide a function `closing_tile` - Represents coordinate to be a closed surface {"x", "y", "z"} - knot_span_wise : bool - Insertion per knotspan vs. total number per paradim - **kwargs - will be passed to `create_tile` function - - Returns - ------- - Microstructure : list - finished microstructure based on object requirements - """ - # Check if all information is gathered - if not self._sanity_check(): - raise ValueError("Not enough information provided, abort") - - # Set default values - if knot_span_wise is None: - knot_span_wise = True - - # check if user wants closed structure - closing_face_dim = {"x": 0, "y": 1, "z": 2}.get(closing_face) - is_closed = closing_face_dim is not None - if not is_closed and (closing_face is not None): - raise ValueError( - "Invalid format for closing_face argument, (handed: " - f"{closing_face}), must be one of" - "{'x', 'y', 'z'}" - ) - - if is_closed: - is_closed = True - if closing_face_dim >= self._deformation_function.para_dim: - raise ValueError( - "closing face must be smaller than the deformation " - "function's parametric dimension" - ) - if self._parametrization_function is None: - raise ValueError( - "Faceclosure is currently only implemented for " - "parametrized microstructures" - ) - - # Prepare the deformation function - # Transform into a non-uniform splinetype and make sure to work on copy - if hasattr(self._deformation_function, "bspline"): - deformation_function_copy = self._deformation_function.bspline - else: - deformation_function_copy = self._deformation_function.nurbs - # Create Spline that will be used to iterate over parametric space - ukvs = deformation_function_copy.unique_knots - if knot_span_wise: - for i_pd in range(deformation_function_copy.para_dim): - if self.tiling[i_pd] == 1: - continue - inv_t = 1 / self.tiling[i_pd] - new_knots = [ - j * inv_t * (ukvs[i_pd][i] - ukvs[i_pd][i - 1]) - for i in range(1, len(ukvs[i_pd])) - for j in range(1, self.tiling[i_pd]) - ] - # insert knots in both the deformation function - deformation_function_copy.insert_knots(i_pd, new_knots) - else: - self._logd( - "New knots will be inserted one by one with the objective" - " to evenly distribute tiles within the parametric domain" - ) - for i_pd in range(deformation_function_copy.para_dim): - n_current_spans = len(ukvs[i_pd]) - 1 - if self.tiling[i_pd] == n_current_spans: - continue - elif self.tiling[i_pd] < n_current_spans: - self._logw( - f"The requested tiling can not be provided, as " - f"there are too many knotspans in the deformation" - f" function. The tiling in parametric dimension " - f"{i_pd} will be set to {n_current_spans}" - ) - self.tiling[i_pd] = n_current_spans - else: - # Determine new knots - n_k_span = np.zeros(n_current_spans, dtype=int) - span_measure = np.diff(ukvs[i_pd]) - for _ in range(self.tiling[i_pd] - n_current_spans): - add_knot = np.argmax(span_measure) - n_k_span[add_knot] += 1 - span_measure[add_knot] *= n_k_span[add_knot] / ( - n_k_span[add_knot] + 1 - ) - - new_knots = [] - for i, nks in enumerate(n_k_span): - new_knots.extend( - np.linspace( - ukvs[i_pd][i], ukvs[i_pd][i + 1], nks + 2 - )[1:-1] - ) - deformation_function_copy.insert_knots(i_pd, new_knots) - - # Bezier Extraction for composition - def_fun_patches = deformation_function_copy.extract.beziers() - - # Calculate parametric space representation for parametrized - # microstructures - is_parametrized = self.parametrization_function is not None - if is_parametrized: - para_space_dimensions = [[u[0], u[-1]] for u in ukvs] - def_fun_para_space = base.Bezier( - degrees=[1] * deformation_function_copy.para_dim, - control_points=np.array( - list(itertools.product(*para_space_dimensions[::-1])) - )[:, ::-1], - ).bspline - for i_pd in range(deformation_function_copy.para_dim): - if self.tiling[i_pd] != 1: - def_fun_para_space.insert_knots( - i_pd, - deformation_function_copy.unique_knots[i_pd][1:-1], - ) - def_fun_para_space = def_fun_para_space.extract.beziers() - - # Determine element resolution - element_resolutions = [ - len(c) - 1 for c in deformation_function_copy.unique_knots - ] - - # Start actual composition - self._microstructure = [] - if is_parametrized: - for i, (def_fun, def_fun_para) in enumerate( - zip(def_fun_patches, def_fun_para_space) - ): - # Evaluate tile parameters - positions = def_fun_para.evaluate( - self._microtile.evaluation_points - ) - tile_parameters = self._parametrization_function(positions) - - # Check if center or closing tile - if is_closed: - # check index - index = i - for ipd in range(closing_face_dim): - index -= index % element_resolutions[ipd] - index /= element_resolutions[ipd] - index = index % element_resolutions[closing_face_dim] - if index == 0: - # Closure at minimum id - tile = self._microtile.closing_tile( - parameters=tile_parameters, - closure=closing_face + "_min", - **kwargs, - ) - elif (index + 1) == element_resolutions[closing_face_dim]: - # Closure at minimum id - tile = self._microtile.closing_tile( - parameters=tile_parameters, - closure=closing_face + "_max", - **kwargs, - ) - else: - tile = self._microtile.create_tile( - parameters=tile_parameters, **kwargs - ) - else: - tile = self._microtile.create_tile( - parameters=tile_parameters, **kwargs - ) - - # Perform composition - for tile_patch in tile: - self._microstructure.append(def_fun.compose(tile_patch)) - # Not parametrized - else: - # Tile can be computed once (prevent to many evaluations) - tile = self._microtile.create_tile(**kwargs) - for def_fun in def_fun_patches: - for t in tile: - self._microstructure.append(def_fun.compose(t)) - - # return copy of precomputed member - return self._microstructure.copy() - - def show(self, use_saved=False, return_gustaf=False, **kwargs): - """ - Shows microstructure. Consists of deformation_function, microtile, and - microstructure. Supported only by vedo. - - Parameters - ---------- - use_saved: bool - return_gustaf: bool - **kwargs: kwargs - Will be passed to show function - - Returns - ------- - gustaf_obj: dict - keys are deformation_function, microtile, and microstructure. - Iff return_gustaf is True. - plt: vedo.Plotter - """ - if use_saved: - if hasattr(self, "_microstructure"): - microstructure = self._microstructure - else: - raise ValueError("No previous microstructure saved") - else: - # Create on the fly - microstructure = self.create(**kwargs) - - # Precompute splines - microtile = self.microtile.create_tile(**kwargs) - deformation_function = self.deformation_function - - if return_gustaf: - return dict( - deformation_function=deformation_function, - microtile=microtile, - microstructure=microstructure, - ) - - # Show in vedo - return show_vedo( - ["Deformation Function", deformation_function], - ["Microtile", microtile], - ["Composed Microstructure", microstructure], - **kwargs, - ) - - def _sanity_check(self): - """Check all members and consistency of user data. - - Parameters - ---------- - updated_properties : bool - Sets the updated_properties variable to value, which indicates, - wheither the microstructure needs to be rebuilt - - Returns - ------- - passes: bool - """ - if ( - (self.deformation_function is None) - or (self.microtile is None) - or (self.tiling is None) - ): - self._logd( - "Current information not sufficient," - " awaiting further assignments" - ) - return False - # Check if microtile object fulfils requirements - if not hasattr(self._microtile, "create_tile"): - raise ValueError( - "Microtile class does not provide the necessary " - "attribute `create_tile`, that is required for " - "microstructure construction" - ) - if not hasattr(self._microtile, "dim"): - raise ValueError( - "Microtile class does not provide the necessary " - "attribute `dim`, defining the dimensionality of " - "the created tile" - ) - - # Check if parametric dimensions are consistent - if not self.deformation_function.para_dim == self._microtile.dim: - raise ValueError( - "Microtile dimension must match parametric dimension of " - "deformation function to enable composition" - ) - - # Check if tiling is consistent - if isinstance(self.tiling, int): - self.tiling = [self.tiling] * self.deformation_function.para_dim - if len(self.tiling) != self.deformation_function.para_dim: - raise ValueError( - "Tiling list must have one entry per parametric dimension" - " of the deformation function" - ) - if self.parametrization_function is not None: - self._logd("Checking compatibility of parametrization function") - if not hasattr(self._microtile, "evaluation_points"): - raise ValueError( - "Microtile class does not provide the necessary " - "attribute `evaluation_points`, that is required for" - " a parametrized microstructure construction" - ) - if not hasattr(self._microtile, "parameter_space_dimension"): - raise ValueError( - "Microtile class does not provide the necessary " - "attribute `parameter_space_dimension`, that is " - "required for a parametrized microstructure " - "construction" - ) - result = self._parametrization_function( - self._microtile.evaluation_points - ) - if not isinstance(result, tuple): - raise ValueError( - "Function outline of parametrization function must be " - "`f(np.ndarray)->tuple`" - ) - if not len(result) == self._microtile.parameter_space_dimension: - raise ValueError( - "Return type of Parametrization function is " - "insufficient, check documentation of Microtile for " - "dimensionality" - ) - # Complete check - return True - - def _make_microtilable(self, microtile): - """Creates a Microtile object on the fly if user only provides (a list - of) splines. Internal use only. - - Parameters - ---------- - microtile : spline / list - Microtile definition of a spline - """ - return _UserTile(microtile) - - -class _UserTile: - def __init__(self, microtile): - """ - On the fly created class of a user tile - Parameters - ---------- - microtile : spline , list - """ - # Assign microtiles - self._user_tile = [] - - if not isinstance(microtile, list): - microtile = [microtile] - - for m in microtile: - if not issubclass(type(m), base.GustafSpline): - raise ValueError( - "Microtiles must be (list of) " - "gustaf.GustafSplines. e.g. gustaf.NURBS" - ) - # Extract beziers for every non Bezier patch else this just - # returns itself - self._user_tile.extend(m.extract.beziers()) - self._dim = microtile[0].dim - for m in microtile: - if m.dim != self._dim: - raise ValueError("Dimensions of spline lists inconsistent") - - @property - def dim(self): - return self._dim - - def create_tile(self, **kwargs): - """Create a tile on the fly.""" - return self._user_tile.copy() diff --git a/gustaf/spline/microstructure/tiles/__init__.py b/gustaf/spline/microstructure/tiles/__init__.py deleted file mode 100644 index 9b211deb2..000000000 --- a/gustaf/spline/microstructure/tiles/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -"""gustaf/spline/microstructure/tiles/__init__.py. - -Interface for tools and generators creating simple microstructures. -""" - -from gustaf.spline.microstructure.tiles import ( - crosstile2d, - crosstile3d, - inversecrosstile3d, - tilebase, -) -from gustaf.spline.microstructure.tiles.crosstile2d import CrossTile2D -from gustaf.spline.microstructure.tiles.crosstile3d import CrossTile3D -from gustaf.spline.microstructure.tiles.inversecrosstile3d import ( - InverseCrossTile3D, -) -from gustaf.spline.microstructure.tiles.tilebase import TileBase - -__all__ = [ - "tilebase", - "crosstile3d", - "crosstile2d", - "inversecrosstile3d", - "TileBase", - "CrossTile3D", - "CrossTile2D", - "InverseCrossTile3D", -] diff --git a/gustaf/spline/microstructure/tiles/crosstile2d.py b/gustaf/spline/microstructure/tiles/crosstile2d.py deleted file mode 100644 index e96ff97de..000000000 --- a/gustaf/spline/microstructure/tiles/crosstile2d.py +++ /dev/null @@ -1,436 +0,0 @@ -import numpy as np - -from gustaf.spline import base -from gustaf.spline.microstructure.tiles.tilebase import TileBase - - -class CrossTile2D(TileBase): - def __init__(self): - """Simple crosstile with linear-quadratic branches and a trilinear - center spline.""" - self._dim = 2 - self._evaluation_points = np.array( - [ - [0.0, 0.5], - [1.0, 0.5], - [0.5, 0.0], - [0.5, 1.0], - ] - ) - self._parameter_space_dimension = 1 - - def closing_tile( - self, - parameters=None, - closure=None, - boundary_width=0.1, - filling_height=0.5, - **kwargs, - ): - """Create a closing tile to match with closed surface. - - Parameters - ---------- - parameters : tuple(np.ndarray) - radii of fitting cylinder at evaluation points - closure : int - parametric dimension that needs to be closed. Positiv values mean - that minimum parametric dimension is requested. That means, - i.e. -2 closes the tile at maximum z-coordinate. - (must currently be either -2 or 2) - boundary_width : float - with of the boundary surronding branch - filling_height : float - portion of the height that is filled in parametric domain - - Returns - ------- - list_of_splines : list - """ - # Check parameters - if closure is None: - raise ValueError("No closing direction given") - - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = tuple([np.ones(6) * 0.2]) - parameters = parameters[0] - if not (np.all(parameters > 0) and np.all(parameters < 0.5)): - raise ValueError("Thickness out of range (0, .5)") - - if not (0.0 < float(boundary_width) < 0.5): - raise ValueError("Boundary Width is out of range") - - if not (0.0 < float(filling_height) < 1.0): - raise ValueError("Filling must be in (0,1)") - - # Constant auxiliary values - inv_boundary_width = 1.0 - boundary_width - inv_filling_height = 1.0 - filling_height - ctps_mid_height_top = (1 + filling_height) * 0.5 - ctps_mid_height_bottom = 1.0 - ctps_mid_height_top - v_one_half = 0.5 - v_one = 1.0 - v_zero = 0.0 - - spline_list = [] - if closure == "x_min": - # Minimum x position - branch_thickness = parameters[1] - - block0_ctps = np.array( - [ - [v_zero, v_zero], - [filling_height, v_zero], - [v_zero, boundary_width], - [filling_height, boundary_width], - ] - ) - - block1_ctps = np.array( - [ - [v_zero, boundary_width], - [filling_height, boundary_width], - [v_zero, inv_boundary_width], - [filling_height, inv_boundary_width], - ] - ) - - block2_ctps = np.array( - [ - [v_zero, inv_boundary_width], - [filling_height, inv_boundary_width], - [v_zero, v_one], - [filling_height, v_one], - ] - ) - - branch_ctps = np.array( - [ - [filling_height, boundary_width], - [ctps_mid_height_top, v_one_half - branch_thickness], - [v_one, v_one_half - branch_thickness], - [filling_height, inv_boundary_width], - [ctps_mid_height_top, v_one_half + branch_thickness], - [v_one, v_one_half + branch_thickness], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block0_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block1_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block2_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[2, 1], control_points=branch_ctps) - ) - return spline_list - elif closure == "x_max": - # Maximum x position - branch_thickness = parameters[0] - - block0_ctps = np.array( - [ - [inv_filling_height, v_zero], - [v_one, v_zero], - [inv_filling_height, boundary_width], - [v_one, boundary_width], - ] - ) - - block1_ctps = np.array( - [ - [inv_filling_height, boundary_width], - [v_one, boundary_width], - [inv_filling_height, inv_boundary_width], - [v_one, inv_boundary_width], - ] - ) - - block2_ctps = np.array( - [ - [inv_filling_height, inv_boundary_width], - [v_one, inv_boundary_width], - [inv_filling_height, v_one], - [v_one, v_one], - ] - ) - - branch_ctps = np.array( - [ - [0, v_one_half - branch_thickness], - [ctps_mid_height_bottom, v_one_half - branch_thickness], - [inv_filling_height, boundary_width], - [v_zero, v_one_half + branch_thickness], - [ctps_mid_height_bottom, v_one_half + branch_thickness], - [inv_filling_height, inv_boundary_width], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block0_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block1_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block2_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[2, 1], control_points=branch_ctps) - ) - return spline_list - elif closure == "y_min": - # Minimum y position - branch_thickness = parameters[3] - - block0_ctps = np.array( - [ - [v_zero, v_zero], - [boundary_width, v_zero], - [v_zero, filling_height], - [boundary_width, filling_height], - ] - ) - - block1_ctps = np.array( - [ - [boundary_width, v_zero], - [inv_boundary_width, v_zero], - [boundary_width, filling_height], - [inv_boundary_width, filling_height], - ] - ) - - block2_ctps = np.array( - [ - [inv_boundary_width, v_zero], - [v_one, v_zero], - [inv_boundary_width, filling_height], - [v_one, filling_height], - ] - ) - - branch_ctps = np.array( - [ - [boundary_width, filling_height], - [inv_boundary_width, filling_height], - [v_one_half - branch_thickness, ctps_mid_height_top], - [v_one_half + branch_thickness, ctps_mid_height_top], - [v_one_half - branch_thickness, v_one], - [v_one_half + branch_thickness, v_one], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block0_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block1_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block2_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 2], control_points=branch_ctps) - ) - return spline_list - elif closure == "y_max": - # Maximum y position - branch_thickness = parameters[2] - - block0_ctps = np.array( - [ - [v_zero, inv_filling_height], - [boundary_width, inv_filling_height], - [v_zero, v_one], - [boundary_width, v_one], - ] - ) - - block1_ctps = np.array( - [ - [boundary_width, inv_filling_height], - [inv_boundary_width, inv_filling_height], - [boundary_width, v_one], - [inv_boundary_width, v_one], - ] - ) - - block2_ctps = np.array( - [ - [inv_boundary_width, inv_filling_height], - [v_one, inv_filling_height], - [inv_boundary_width, v_one], - [v_one, v_one], - ] - ) - - branch_ctps = np.array( - [ - [v_one_half - branch_thickness, v_zero], - [v_one_half + branch_thickness, v_zero], - [v_one_half - branch_thickness, ctps_mid_height_bottom], - [v_one_half + branch_thickness, ctps_mid_height_bottom], - [boundary_width, inv_filling_height], - [inv_boundary_width, inv_filling_height], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block0_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block1_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=block2_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 2], control_points=branch_ctps) - ) - return spline_list - else: - raise NotImplementedError( - "Requested closing dimension is not supported" - ) - - def create_tile(self, parameters=None, center_expansion=1.0, **kwargs): - """Create a microtile based on the parameters that describe the branch - thicknesses. - - Thickness parameters are used to describe the inner radius of the - outward facing branches - - Parameters - ---------- - parameters : tuple(np.array) - only first entry is used, defines the internal radii of the - branches - center_expansion : float - thickness of center is expanded by a factor - Returns - ------- - microtile_list : list(splines) - """ - - if not isinstance(center_expansion, float): - raise ValueError("Invalid Type") - if not ((center_expansion > 0.5) and (center_expansion < 1.5)): - raise ValueError("Center Expansion must be in (.5,1.5)") - max_radius = min(0.5, (0.5 / center_expansion)) - # set to default if nothing is given - if parameters is None: - self._logd("Setting branch thickness to default 0.2") - parameters = tuple([np.ones(4) * 0.2]) - [x_min_r, x_max_r, y_min_r, y_max_r] = parameters[0].tolist() - for radius in [x_min_r, x_max_r, y_min_r, y_max_r]: - if not isinstance(radius, float): - raise ValueError("Invalid type") - if not (radius > 0 and radius < max_radius): - raise ValueError( - f"Radii must be in (0,{max_radius}) for " - f"center_expansion {center_expansion}" - ) - - # center radius - center_r = ( - (x_min_r + x_max_r + y_min_r + y_max_r) / 4.0 * center_expansion - ) - hd_center = 0.5 * (0.5 + center_r) - one_half = 0.5 - - # Init return value - spline_list = [] - - # Create the center-tile - center_points = np.array( - [ - [-center_r, -center_r], - [center_r, -center_r], - [-center_r, center_r], - [center_r, center_r], - ] - ) + np.array([one_half, one_half]) - - y_min_ctps = np.array( - [ - [-y_min_r, -one_half], - [y_min_r, -one_half], - [-y_min_r, -hd_center], - [y_min_r, -hd_center], - [-center_r, -center_r], - [center_r, -center_r], - ] - ) + np.array([one_half, one_half]) - - y_max_ctps = np.array( - [ - [-center_r, center_r], - [center_r, center_r], - [-y_max_r, hd_center], - [y_max_r, hd_center], - [-y_max_r, one_half], - [y_max_r, one_half], - ] - ) + np.array([one_half, one_half]) - - x_min_ctps = np.array( - [ - [-one_half, -x_min_r], - [-hd_center, -x_min_r], - [-center_r, -center_r], - [-one_half, x_min_r], - [-hd_center, x_min_r], - [-center_r, center_r], - ] - ) + np.array([one_half, one_half]) - - x_max_ctps = np.array( - [ - [center_r, -center_r], - [hd_center, -x_max_r], - [one_half, -x_max_r], - [center_r, center_r], - [hd_center, x_max_r], - [one_half, x_max_r], - ] - ) + np.array([one_half, one_half]) - - spline_list.append( - base.Bezier(degrees=[1, 1], control_points=center_points) - ) - - spline_list.append( - base.Bezier(degrees=[2, 1], control_points=x_min_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[2, 1], control_points=x_max_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 2], control_points=y_min_ctps) - ) - - spline_list.append( - base.Bezier(degrees=[1, 2], control_points=y_max_ctps) - ) - - return spline_list diff --git a/gustaf/spline/microstructure/tiles/crosstile3d.py b/gustaf/spline/microstructure/tiles/crosstile3d.py deleted file mode 100644 index d2f7508c7..000000000 --- a/gustaf/spline/microstructure/tiles/crosstile3d.py +++ /dev/null @@ -1,560 +0,0 @@ -import numpy as np - -from gustaf.spline import base -from gustaf.spline.microstructure.tiles.tilebase import TileBase - - -class CrossTile3D(TileBase): - def __init__(self): - """Simple crosstile with linear-quadratic branches and a trilinear - center spline.""" - self._dim = 3 - self._evaluation_points = np.array( - [ - [0.0, 0.5, 0.5], - [1.0, 0.5, 0.5], - [0.5, 0.0, 0.5], - [0.5, 1.0, 0.5], - [0.5, 0.5, 0.0], - [0.5, 0.5, 1.0], - ] - ) - self._parameter_space_dimension = 1 - - def closing_tile( - self, - parameters=None, - closure=None, - boundary_width=0.1, - filling_height=0.5, - **kwargs, - ): - """Create a closing tile to match with closed surface. - - Parameters - ---------- - parameters : tuple(np.ndarray) - radii of fitting cylinder at evaluation points - closure : str - parametric dimension that needs to be closed. - Must be {"z_min", "z_max"} - boundary_width : float - with of the boundary surronding branch - filling_height : float - portion of the height that is filled in parametric domain - - Returns - ------- - list_of_splines : list - """ - # Check parameters - if closure is None: - raise ValueError("No closing direction given") - - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = tuple([np.ones(6) * 0.2]) - parameters = parameters[0] - if not (np.all(parameters > 0) and np.all(parameters < 0.5)): - raise ValueError("Thickness out of range (0, .5)") - - if not (0.0 < float(boundary_width) < 0.5): - raise ValueError("Boundary Width is out of range") - - if not (0.0 < float(filling_height) < 1.0): - raise ValueError("Filling must be in (0,1)") - - inv_boundary_width = 1.0 - boundary_width - inv_filling_height = 1.0 - filling_height - center_width = 1.0 - 2 * boundary_width - ctps_mid_height_top = (1 + filling_height) * 0.5 - ctps_mid_height_bottom = 1.0 - ctps_mid_height_top - r_center = center_width * 0.5 - - spline_list = [] - if closure == "z_min": - # The branch is located at zmin of current tile - branch_thickness = parameters[5] - ctps_corner = np.array( - [ - [0.0, 0.0, 0.0], - [boundary_width, 0.0, 0.0], - [0.0, boundary_width, 0.0], - [boundary_width, boundary_width, 0.0], - [0.0, 0.0, filling_height], - [boundary_width, 0.0, filling_height], - [0.0, boundary_width, filling_height], - [boundary_width, boundary_width, filling_height], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1, 1], control_points=ctps_corner) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=( - ctps_corner + np.array([0.0, inv_boundary_width, 0.0]) - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=( - ctps_corner + np.array([inv_boundary_width, 0.0, 0.0]) - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=( - ctps_corner - + np.array( - [inv_boundary_width, inv_boundary_width, 0.0] - ) - ), - ) - ) - - center_ctps = np.array( - [ - [boundary_width, boundary_width, 0.0], - [inv_boundary_width, boundary_width, 0.0], - [boundary_width, inv_boundary_width, 0.0], - [inv_boundary_width, inv_boundary_width, 0.0], - [boundary_width, boundary_width, filling_height], - [inv_boundary_width, boundary_width, filling_height], - [boundary_width, inv_boundary_width, filling_height], - [inv_boundary_width, inv_boundary_width, filling_height], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1, 1], control_points=center_ctps) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.maximum( - center_ctps - np.array([center_width, 0, 0]), 0 - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.maximum( - center_ctps - np.array([0, center_width, 0]), 0 - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.minimum( - center_ctps + np.array([center_width, 0, 0]), 1.0 - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.minimum( - center_ctps + np.array([0, center_width, 0]), 1.0 - ), - ) - ) - branch_ctps = np.array( - [ - [-r_center, -r_center, filling_height], - [r_center, -r_center, filling_height], - [-r_center, r_center, filling_height], - [r_center, r_center, filling_height], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_top, - ], - [branch_thickness, -branch_thickness, ctps_mid_height_top], - [-branch_thickness, branch_thickness, ctps_mid_height_top], - [branch_thickness, branch_thickness, ctps_mid_height_top], - [-branch_thickness, -branch_thickness, 1.0], - [branch_thickness, -branch_thickness, 1.0], - [-branch_thickness, branch_thickness, 1.0], - [branch_thickness, branch_thickness, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier(degrees=[1, 1, 2], control_points=branch_ctps) - ) - - return spline_list - elif closure == "z_max": - # The branch is located at zmax of current tile - branch_thickness = parameters[4] - ctps_corner = np.array( - [ - [0.0, 0.0, inv_filling_height], - [boundary_width, 0.0, inv_filling_height], - [0.0, boundary_width, inv_filling_height], - [boundary_width, boundary_width, inv_filling_height], - [0.0, 0.0, 1.0], - [boundary_width, 0.0, 1.0], - [0.0, boundary_width, 1.0], - [boundary_width, boundary_width, 1.0], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1, 1], control_points=ctps_corner) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=( - ctps_corner + np.array([0.0, inv_boundary_width, 0.0]) - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=( - ctps_corner + np.array([inv_boundary_width, 0.0, 0.0]) - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=( - ctps_corner - + np.array( - [inv_boundary_width, inv_boundary_width, 0.0] - ) - ), - ) - ) - - center_ctps = np.array( - [ - [boundary_width, boundary_width, inv_filling_height], - [inv_boundary_width, boundary_width, inv_filling_height], - [boundary_width, inv_boundary_width, inv_filling_height], - [ - inv_boundary_width, - inv_boundary_width, - inv_filling_height, - ], - [boundary_width, boundary_width, 1.0], - [inv_boundary_width, boundary_width, 1.0], - [boundary_width, inv_boundary_width, 1.0], - [inv_boundary_width, inv_boundary_width, 1.0], - ] - ) - - spline_list.append( - base.Bezier(degrees=[1, 1, 1], control_points=center_ctps) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.maximum( - center_ctps - np.array([center_width, 0, 0]), 0 - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.maximum( - center_ctps - np.array([0, center_width, 0]), 0 - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.minimum( - center_ctps + np.array([center_width, 0, 0]), 1.0 - ), - ) - ) - - spline_list.append( - base.Bezier( - degrees=[1, 1, 1], - control_points=np.minimum( - center_ctps + np.array([0, center_width, 0]), 1.0 - ), - ) - ) - - branch_ctps = np.array( - [ - [-branch_thickness, -branch_thickness, 0.0], - [branch_thickness, -branch_thickness, 0.0], - [-branch_thickness, branch_thickness, 0.0], - [branch_thickness, branch_thickness, 0.0], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [ - branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [-r_center, -r_center, inv_filling_height], - [r_center, -r_center, inv_filling_height], - [-r_center, r_center, inv_filling_height], - [r_center, r_center, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier(degrees=[1, 1, 2], control_points=branch_ctps) - ) - - return spline_list - else: - raise NotImplementedError( - "Requested closing dimension is not supported" - ) - - def create_tile(self, parameters=None, center_expansion=1.0, **kwargs): - """Create a microtile based on the parameters that describe the branch - thicknesses. - - Thickness parameters are used to describe the inner radius of the - outward facing branches - - Parameters - ---------- - parameters : tuple(np.array) - only first entry is used, defines the internal radii of the - branches - center_expansion : float - thickness of center is expanded by a factor - - Returns - ------- - microtile_list : list(splines) - """ - - if not isinstance(center_expansion, float): - raise ValueError("Invalid Type") - if not ((center_expansion > 0.5) and (center_expansion < 1.5)): - raise ValueError("Center Expansion must be in (.5,1.5)") - max_radius = min(0.5, (0.5 / center_expansion)) - # set to default if nothing is given - if parameters is None: - self._logd("Setting branch thickness to default 0.2") - parameters = tuple([np.ones(6) * 0.2]) - [x_min_r, x_max_r, y_min_r, y_max_r, z_min_r, z_max_r] = parameters[ - 0 - ].tolist() - for radius in [x_min_r, x_max_r, y_min_r, y_max_r, z_min_r, z_max_r]: - if not isinstance(radius, float): - raise ValueError("Invalid type") - if not (radius > 0 and radius < max_radius): - raise ValueError( - f"Radii must be in (0,{max_radius}) for " - f"center_expansion {center_expansion}" - ) - - # center radius - center_r = ( - (x_min_r + x_max_r + y_min_r + y_max_r + z_min_r + z_max_r) - / 6.0 - * center_expansion - ) - hd_center = 0.5 * (0.5 + center_r) - - # Create the center-tile - center_points = np.array( - [ - [-center_r, -center_r, -center_r], - [center_r, -center_r, -center_r], - [-center_r, center_r, -center_r], - [center_r, center_r, -center_r], - [-center_r, -center_r, center_r], - [center_r, -center_r, center_r], - [-center_r, center_r, center_r], - [center_r, center_r, center_r], - ] - ) - - center_spline = base.Bezier( - degrees=[1, 1, 1], control_points=center_points + [0.5, 0.5, 0.5] - ) - - # X-Axis branches - # X-Min-Branch - aux_x_min = min(x_min_r, center_r) - x_min_ctps = np.array( - [ - [-0.5, -x_min_r, -x_min_r], - [-hd_center, -aux_x_min, -aux_x_min], - center_points[0, :], - [-0.5, x_min_r, -x_min_r], - [-hd_center, aux_x_min, -aux_x_min], - center_points[2, :], - [-0.5, -x_min_r, x_min_r], - [-hd_center, -aux_x_min, aux_x_min], - center_points[4, :], - [-0.5, x_min_r, x_min_r], - [-hd_center, aux_x_min, aux_x_min], - center_points[6, :], - ] - ) - x_min_spline = base.Bezier( - degrees=[2, 1, 1], control_points=x_min_ctps + [0.5, 0.5, 0.5] - ) - # X-Min-Branch - aux_x_max = min(x_max_r, center_r) - x_max_ctps = np.array( - [ - center_points[1, :], - [hd_center, -aux_x_max, -aux_x_max], - [0.5, -x_max_r, -x_max_r], - center_points[3, :], - [hd_center, aux_x_max, -aux_x_max], - [0.5, x_max_r, -x_max_r], - center_points[5, :], - [hd_center, -aux_x_max, aux_x_max], - [0.5, -x_max_r, x_max_r], - center_points[7, :], - [hd_center, aux_x_max, aux_x_max], - [0.5, x_max_r, x_max_r], - ] - ) - x_max_spline = base.Bezier( - degrees=[2, 1, 1], control_points=x_max_ctps + [0.5, 0.5, 0.5] - ) - - # Y-Axis branches - # Y-Min-Branch - aux_y_min = min(y_min_r, center_r) - y_min_ctps = np.array( - [ - [-y_min_r, -0.5, -y_min_r], - [y_min_r, -0.5, -y_min_r], - [-aux_y_min, -hd_center, -aux_y_min], - [aux_y_min, -hd_center, -aux_y_min], - center_points[0, :], - center_points[1, :], - [-y_min_r, -0.5, y_min_r], - [y_min_r, -0.5, y_min_r], - [-aux_y_min, -hd_center, aux_y_min], - [aux_y_min, -hd_center, aux_y_min], - center_points[4, :], - center_points[5, :], - ] - ) - y_min_spline = base.Bezier( - degrees=[1, 2, 1], control_points=y_min_ctps + [0.5, 0.5, 0.5] - ) - # Y-Min-Branch - aux_y_max = min(y_max_r, center_r) - y_max_ctps = np.array( - [ - center_points[2, :], - center_points[3, :], - [-aux_y_max, hd_center, -aux_y_max], - [aux_y_max, hd_center, -aux_y_max], - [-y_max_r, 0.5, -y_max_r], - [y_max_r, 0.5, -y_max_r], - center_points[6, :], - center_points[7, :], - [-aux_y_max, hd_center, aux_y_max], - [aux_y_max, hd_center, aux_y_max], - [-y_max_r, 0.5, y_max_r], - [y_max_r, 0.5, y_max_r], - ] - ) - y_max_spline = base.Bezier( - degrees=[1, 2, 1], control_points=y_max_ctps + [0.5, 0.5, 0.5] - ) - - # Y-Axis branches - # Y-Min-Branch - aux_z_min = min(z_min_r, center_r) - z_min_ctps = np.array( - [ - [-z_min_r, -z_min_r, -0.5], - [z_min_r, -z_min_r, -0.5], - [-z_min_r, z_min_r, -0.5], - [z_min_r, z_min_r, -0.5], - [-aux_z_min, -aux_z_min, -hd_center], - [aux_z_min, -aux_z_min, -hd_center], - [-aux_z_min, aux_z_min, -hd_center], - [aux_z_min, aux_z_min, -hd_center], - center_points[0, :], - center_points[1, :], - center_points[2, :], - center_points[3, :], - ] - ) - z_min_spline = base.Bezier( - degrees=[1, 1, 2], control_points=z_min_ctps + [0.5, 0.5, 0.5] - ) - # Y-Min-Branch - aux_z_max = min(z_max_r, center_r) - z_max_ctps = np.array( - [ - center_points[4, :], - center_points[5, :], - center_points[6, :], - center_points[7, :], - [-aux_z_max, -aux_z_max, hd_center], - [aux_z_max, -aux_z_max, hd_center], - [-aux_z_max, aux_z_max, hd_center], - [aux_z_max, aux_z_max, hd_center], - [-z_max_r, -z_max_r, 0.5], - [z_max_r, -z_max_r, 0.5], - [-z_max_r, z_max_r, 0.5], - [z_max_r, z_max_r, 0.5], - ] - ) - z_max_spline = base.Bezier( - degrees=[1, 1, 2], control_points=z_max_ctps + [0.5, 0.5, 0.5] - ) - - return [ - center_spline, - x_min_spline, - x_max_spline, - y_min_spline, - y_max_spline, - z_min_spline, - z_max_spline, - ] diff --git a/gustaf/spline/microstructure/tiles/inversecrosstile3d.py b/gustaf/spline/microstructure/tiles/inversecrosstile3d.py deleted file mode 100644 index 57f798785..000000000 --- a/gustaf/spline/microstructure/tiles/inversecrosstile3d.py +++ /dev/null @@ -1,1563 +0,0 @@ -import numpy as np - -from gustaf.spline import base -from gustaf.spline.microstructure.tiles.tilebase import TileBase - - -class InverseCrossTile3D(TileBase): - """Class that provides necessary functions to create inverse microtile, - that can be used to describe the domain within a microstructure.""" - - def __init__(self): - """Simple inverse crosstile to tile with linear-quadratic branches and - a trilinear center spline.""" - self._dim = 3 - self._evaluation_points = np.array( - [ - [0.0, 0.5, 0.5], - [1.0, 0.5, 0.5], - [0.5, 0.0, 0.5], - [0.5, 1.0, 0.5], - [0.5, 0.5, 0.0], - [0.5, 0.5, 1.0], - ] - ) - self._parameter_space_dimension = 1 - - def closing_tile( - self, - parameters=None, - closure=None, - boundary_width=0.1, - filling_height=0.5, - seperator_distance=None, - **kwargs, - ): - """Create a closing tile to match with closed surface. - - Parameters - ---------- - parameters : tuple(np.ndarray) - radii of fitting cylinder at evaluation points - closure : str - parametric dimension that needs to be closed. - Must be one of {"z_min", "z_max"} - boundary_width : float - with of the boundary surronding branch - filling_height : float - portion of the height that is filled in parametric domain - - Returns - ------- - list_of_splines : list - """ - # Check parameters - if closure is None: - raise ValueError("No closing direction given") - - if seperator_distance is None: - raise ValueError( - "Seperator Distance is missing. The value is required to " - "create watertight connections with neighboring elements." - " The value should be greater than the biggest branch " - "radius" - ) - - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = tuple([np.ones(6) * 0.2]) - parameters = parameters[0] - if not (np.all(parameters > 0) and np.all(parameters < 0.5)): - raise ValueError("Thickness out of range (0, .5)") - - if not (0.0 < float(boundary_width) < 0.5): - raise ValueError("Boundary Width is out of range") - - if not (0.0 < float(filling_height) < 1.0): - raise ValueError("Filling must be in (0,1)") - - # Precompute auxiliary values - inv_filling_height = 1.0 - filling_height - ctps_mid_height_top = (1 + filling_height) * 0.5 - ctps_mid_height_bottom = 1.0 - ctps_mid_height_top - center_width = 1.0 - 2 * boundary_width - r_center = center_width * 0.5 - half_r_center = (r_center + 0.5) * 0.5 - aux_column_width = 0.5 - 2 * (0.5 - seperator_distance) - - spline_list = [] - if closure == "z_min": - branch_thickness = parameters[5] - branch_neighbor_x_min_ctps = np.array( - [ - [-0.5, -r_center, filling_height], - [-half_r_center, -r_center, filling_height], - [-r_center, -r_center, filling_height], - [-0.5, r_center, filling_height], - [-half_r_center, r_center, filling_height], - [-r_center, r_center, filling_height], - [-0.5, -aux_column_width, ctps_mid_height_top], - [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_top, - ], - [-0.5, aux_column_width, ctps_mid_height_top], - [ - -seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [-branch_thickness, branch_thickness, ctps_mid_height_top], - [-0.5, -aux_column_width, 1.0], - [-seperator_distance, -aux_column_width, 1.0], - [-branch_thickness, -branch_thickness, 1.0], - [-0.5, aux_column_width, 1.0], - [-seperator_distance, aux_column_width, 1.0], - [-branch_thickness, branch_thickness, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_min_ctps, - ) - ) - - branch_neighbor_x_max_ctps = np.array( - [ - [r_center, -r_center, filling_height], - [half_r_center, -r_center, filling_height], - [0.5, -r_center, filling_height], - [r_center, r_center, filling_height], - [half_r_center, r_center, filling_height], - [0.5, r_center, filling_height], - [branch_thickness, -branch_thickness, ctps_mid_height_top], - [ - seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], - [0.5, -aux_column_width, ctps_mid_height_top], - [branch_thickness, branch_thickness, ctps_mid_height_top], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [0.5, aux_column_width, ctps_mid_height_top], - [branch_thickness, -branch_thickness, 1.0], - [seperator_distance, -aux_column_width, 1.0], - [0.5, -aux_column_width, 1.0], - [branch_thickness, branch_thickness, 1.0], - [seperator_distance, aux_column_width, 1.0], - [0.5, aux_column_width, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_max_ctps, - ) - ) - - branch_neighbor_y_min_ctps = np.array( - [ - [-r_center, -0.5, filling_height], - [r_center, -0.5, filling_height], - [-r_center, -half_r_center, filling_height], - [r_center, -half_r_center, filling_height], - [-r_center, -r_center, filling_height], - [r_center, -r_center, filling_height], - [-aux_column_width, -0.5, ctps_mid_height_top], - [aux_column_width, -0.5, ctps_mid_height_top], - [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [ - aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_top, - ], - [branch_thickness, -branch_thickness, ctps_mid_height_top], - [-aux_column_width, -0.5, 1.0], - [aux_column_width, -0.5, 1.0], - [-aux_column_width, -seperator_distance, 1.0], - [aux_column_width, -seperator_distance, 1.0], - [-branch_thickness, -branch_thickness, 1.0], - [branch_thickness, -branch_thickness, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_min_ctps, - ) - ) - - branch_neighbor_y_max_ctps = np.array( - [ - [-r_center, r_center, filling_height], - [r_center, r_center, filling_height], - [-r_center, half_r_center, filling_height], - [r_center, half_r_center, filling_height], - [-r_center, 0.5, filling_height], - [r_center, 0.5, filling_height], - [-branch_thickness, branch_thickness, ctps_mid_height_top], - [branch_thickness, branch_thickness, ctps_mid_height_top], - [ - -aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], - [ - aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], - [-aux_column_width, 0.5, ctps_mid_height_top], - [aux_column_width, 0.5, ctps_mid_height_top], - [-branch_thickness, branch_thickness, 1.0], - [branch_thickness, branch_thickness, 1.0], - [-aux_column_width, seperator_distance, 1.0], - [aux_column_width, seperator_distance, 1.0], - [-aux_column_width, 0.5, 1.0], - [aux_column_width, 0.5, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_max_ctps, - ) - ) - - branch_x_min_y_min_ctps = np.array( - [ - [-0.5, -0.5, filling_height], - [-half_r_center, -0.5, filling_height], - [-r_center, -0.5, filling_height], - [-0.5, -half_r_center, filling_height], - [-half_r_center, -half_r_center, filling_height], - [-r_center, -half_r_center, filling_height], - [-0.5, -r_center, filling_height], - [-half_r_center, -r_center, filling_height], - [-r_center, -r_center, filling_height], - [-0.5, -0.5, ctps_mid_height_top], - [-seperator_distance, -0.5, ctps_mid_height_top], - [-aux_column_width, -0.5, ctps_mid_height_top], - [-0.5, -seperator_distance, ctps_mid_height_top], - [ - -seperator_distance, - -seperator_distance, - ctps_mid_height_top, - ], - [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [-0.5, -aux_column_width, ctps_mid_height_top], - [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_top, - ], - [-0.5, -0.5, 1.0], - [-seperator_distance, -0.5, 1.0], - [-aux_column_width, -0.5, 1.0], - [-0.5, -seperator_distance, 1.0], - [-seperator_distance, -seperator_distance, 1.0], - [-aux_column_width, -seperator_distance, 1.0], - [-0.5, -aux_column_width, 1.0], - [-seperator_distance, -aux_column_width, 1.0], - [-branch_thickness, -branch_thickness, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_min_ctps - ) - ) - - branch_x_min_y_max_ctps = np.array( - [ - [-0.5, r_center, filling_height], - [-half_r_center, r_center, filling_height], - [-r_center, r_center, filling_height], - [-0.5, half_r_center, filling_height], - [-half_r_center, half_r_center, filling_height], - [-r_center, half_r_center, filling_height], - [-0.5, 0.5, filling_height], - [-half_r_center, 0.5, filling_height], - [-r_center, 0.5, filling_height], - [-0.5, aux_column_width, ctps_mid_height_top], - [ - -seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [-branch_thickness, branch_thickness, ctps_mid_height_top], - [-0.5, seperator_distance, ctps_mid_height_top], - [ - -seperator_distance, - seperator_distance, - ctps_mid_height_top, - ], - [ - -aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], - [-0.5, 0.5, ctps_mid_height_top], - [-seperator_distance, 0.5, ctps_mid_height_top], - [-aux_column_width, 0.5, ctps_mid_height_top], - [-0.5, aux_column_width, 1.0], - [-seperator_distance, aux_column_width, 1.0], - [-branch_thickness, branch_thickness, 1.0], - [-0.5, seperator_distance, 1.0], - [-seperator_distance, seperator_distance, 1.0], - [-aux_column_width, seperator_distance, 1.0], - [-0.5, 0.5, 1.0], - [-seperator_distance, 0.5, 1.0], - [-aux_column_width, 0.5, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_max_ctps - ) - ) - - branch_x_max_y_min_ctps = np.array( - [ - [r_center, -0.5, filling_height], - [half_r_center, -0.5, filling_height], - [0.5, -0.5, filling_height], - [r_center, -half_r_center, filling_height], - [half_r_center, -half_r_center, filling_height], - [0.5, -half_r_center, filling_height], - [r_center, -r_center, filling_height], - [half_r_center, -r_center, filling_height], - [0.5, -r_center, filling_height], - [aux_column_width, -0.5, ctps_mid_height_top], - [seperator_distance, -0.5, ctps_mid_height_top], - [0.5, -0.5, ctps_mid_height_top], - [ - aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [ - seperator_distance, - -seperator_distance, - ctps_mid_height_top, - ], - [0.5, -seperator_distance, ctps_mid_height_top], - [branch_thickness, -branch_thickness, ctps_mid_height_top], - [ - seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], - [0.5, -aux_column_width, ctps_mid_height_top], - [aux_column_width, -0.5, 1.0], - [seperator_distance, -0.5, 1.0], - [0.5, -0.5, 1.0], - [aux_column_width, -seperator_distance, 1.0], - [seperator_distance, -seperator_distance, 1.0], - [0.5, -seperator_distance, 1.0], - [branch_thickness, -branch_thickness, 1.0], - [seperator_distance, -aux_column_width, 1.0], - [0.5, -aux_column_width, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_min_ctps - ) - ) - - branch_x_max_y_max_ctps = np.array( - [ - [r_center, r_center, filling_height], - [half_r_center, r_center, filling_height], - [0.5, r_center, filling_height], - [r_center, half_r_center, filling_height], - [half_r_center, half_r_center, filling_height], - [0.5, half_r_center, filling_height], - [r_center, 0.5, filling_height], - [half_r_center, 0.5, filling_height], - [0.5, 0.5, filling_height], - [branch_thickness, branch_thickness, ctps_mid_height_top], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [0.5, aux_column_width, ctps_mid_height_top], - [ - aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], - [ - seperator_distance, - seperator_distance, - ctps_mid_height_top, - ], - [0.5, seperator_distance, ctps_mid_height_top], - [aux_column_width, 0.5, ctps_mid_height_top], - [seperator_distance, 0.5, ctps_mid_height_top], - [0.5, 0.5, ctps_mid_height_top], - [branch_thickness, branch_thickness, 1.0], - [seperator_distance, aux_column_width, 1.0], - [0.5, aux_column_width, 1.0], - [aux_column_width, seperator_distance, 1.0], - [seperator_distance, seperator_distance, 1.0], - [0.5, seperator_distance, 1.0], - [aux_column_width, 0.5, 1.0], - [seperator_distance, 0.5, 1.0], - [0.5, 0.5, 1.0], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_max_ctps - ) - ) - - return spline_list - - elif closure == "z_max": - branch_thickness = parameters[4] - branch_neighbor_x_min_ctps = np.array( - [ - [-0.5, -aux_column_width, 0.0], - [-seperator_distance, -aux_column_width, 0.0], - [-branch_thickness, -branch_thickness, 0.0], - [-0.5, aux_column_width, 0.0], - [-seperator_distance, aux_column_width, 0.0], - [-branch_thickness, branch_thickness, 0.0], - [-0.5, -aux_column_width, ctps_mid_height_bottom], - [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, aux_column_width, ctps_mid_height_bottom], - [ - -seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, -r_center, inv_filling_height], - [-half_r_center, -r_center, inv_filling_height], - [-r_center, -r_center, inv_filling_height], - [-0.5, r_center, inv_filling_height], - [-half_r_center, r_center, inv_filling_height], - [-r_center, r_center, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_min_ctps, - ) - ) - - branch_neighbor_x_max_ctps = np.array( - [ - [branch_thickness, -branch_thickness, 0.0], - [seperator_distance, -aux_column_width, 0.0], - [0.5, -aux_column_width, 0.0], - [branch_thickness, branch_thickness, 0.0], - [seperator_distance, aux_column_width, 0.0], - [0.5, aux_column_width, 0.0], - [ - branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, -aux_column_width, ctps_mid_height_bottom], - [ - branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, aux_column_width, ctps_mid_height_bottom], - [r_center, -r_center, inv_filling_height], - [half_r_center, -r_center, inv_filling_height], - [0.5, -r_center, inv_filling_height], - [r_center, r_center, inv_filling_height], - [half_r_center, r_center, inv_filling_height], - [0.5, r_center, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_max_ctps, - ) - ) - - branch_neighbor_y_min_ctps = np.array( - [ - [-aux_column_width, -0.5, 0.0], - [aux_column_width, -0.5, 0.0], - [-aux_column_width, -seperator_distance, 0.0], - [aux_column_width, -seperator_distance, 0.0], - [-branch_thickness, -branch_thickness, 0.0], - [branch_thickness, -branch_thickness, 0.0], - [-aux_column_width, -0.5, ctps_mid_height_bottom], - [aux_column_width, -0.5, ctps_mid_height_bottom], - [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], - [ - aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [ - branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [-r_center, -0.5, inv_filling_height], - [r_center, -0.5, inv_filling_height], - [-r_center, -half_r_center, inv_filling_height], - [r_center, -half_r_center, inv_filling_height], - [-r_center, -r_center, inv_filling_height], - [r_center, -r_center, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_min_ctps, - ) - ) - - branch_neighbor_y_max_ctps = np.array( - [ - [-branch_thickness, branch_thickness, 0.0], - [branch_thickness, branch_thickness, 0.0], - [-aux_column_width, seperator_distance, 0.0], - [aux_column_width, seperator_distance, 0.0], - [-aux_column_width, 0.5, 0.0], - [aux_column_width, 0.5, 0.0], - [ - -branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - -aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [ - aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [-aux_column_width, 0.5, ctps_mid_height_bottom], - [aux_column_width, 0.5, ctps_mid_height_bottom], - [-r_center, r_center, inv_filling_height], - [r_center, r_center, inv_filling_height], - [-r_center, half_r_center, inv_filling_height], - [r_center, half_r_center, inv_filling_height], - [-r_center, 0.5, inv_filling_height], - [r_center, 0.5, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_max_ctps, - ) - ) - - branch_x_min_y_min_ctps = np.array( - [ - [-0.5, -0.5, 0.0], - [-seperator_distance, -0.5, 0.0], - [-aux_column_width, -0.5, 0.0], - [-0.5, -seperator_distance, 0.0], - [-seperator_distance, -seperator_distance, 0.0], - [-aux_column_width, -seperator_distance, 0.0], - [-0.5, -aux_column_width, 0.0], - [-seperator_distance, -aux_column_width, 0.0], - [-branch_thickness, -branch_thickness, 0.0], - [-0.5, -0.5, ctps_mid_height_bottom], - [-seperator_distance, -0.5, ctps_mid_height_bottom], - [-aux_column_width, -0.5, ctps_mid_height_bottom], - [-0.5, -seperator_distance, ctps_mid_height_bottom], - [ - -seperator_distance, - -seperator_distance, - ctps_mid_height_bottom, - ], - [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], - [-0.5, -aux_column_width, ctps_mid_height_bottom], - [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, -0.5, inv_filling_height], - [-half_r_center, -0.5, inv_filling_height], - [-r_center, -0.5, inv_filling_height], - [-0.5, -half_r_center, inv_filling_height], - [-half_r_center, -half_r_center, inv_filling_height], - [-r_center, -half_r_center, inv_filling_height], - [-0.5, -r_center, inv_filling_height], - [-half_r_center, -r_center, inv_filling_height], - [-r_center, -r_center, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_min_ctps - ) - ) - - branch_x_max_y_max_ctps = np.array( - [ - [branch_thickness, branch_thickness, 0.0], - [seperator_distance, aux_column_width, 0.0], - [0.5, aux_column_width, 0.0], - [aux_column_width, seperator_distance, 0.0], - [seperator_distance, seperator_distance, 0.0], - [0.5, seperator_distance, 0.0], - [aux_column_width, 0.5, 0.0], - [seperator_distance, 0.5, 0.0], - [0.5, 0.5, 0.0], - [ - branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, aux_column_width, ctps_mid_height_bottom], - [ - aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - seperator_distance, - ctps_mid_height_bottom, - ], - [0.5, seperator_distance, ctps_mid_height_bottom], - [aux_column_width, 0.5, ctps_mid_height_bottom], - [seperator_distance, 0.5, ctps_mid_height_bottom], - [0.5, 0.5, ctps_mid_height_bottom], - [r_center, r_center, inv_filling_height], - [half_r_center, r_center, inv_filling_height], - [0.5, r_center, inv_filling_height], - [r_center, half_r_center, inv_filling_height], - [half_r_center, half_r_center, inv_filling_height], - [0.5, half_r_center, inv_filling_height], - [r_center, 0.5, inv_filling_height], - [half_r_center, 0.5, inv_filling_height], - [0.5, 0.5, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_max_ctps - ) - ) - - branch_x_max_y_min_ctps = np.array( - [ - [aux_column_width, -0.5, 0.0], - [seperator_distance, -0.5, 0.0], - [0.5, -0.5, 0.0], - [aux_column_width, -seperator_distance, 0.0], - [seperator_distance, -seperator_distance, 0.0], - [0.5, -seperator_distance, 0.0], - [branch_thickness, -branch_thickness, 0.0], - [seperator_distance, -aux_column_width, 0.0], - [0.5, -aux_column_width, 0.0], - [aux_column_width, -0.5, ctps_mid_height_bottom], - [seperator_distance, -0.5, ctps_mid_height_bottom], - [0.5, -0.5, ctps_mid_height_bottom], - [ - aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - -seperator_distance, - ctps_mid_height_bottom, - ], - [0.5, -seperator_distance, ctps_mid_height_bottom], - [ - branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, -aux_column_width, ctps_mid_height_bottom], - [r_center, -0.5, inv_filling_height], - [half_r_center, -0.5, inv_filling_height], - [0.5, -0.5, inv_filling_height], - [r_center, -half_r_center, inv_filling_height], - [half_r_center, -half_r_center, inv_filling_height], - [0.5, -half_r_center, inv_filling_height], - [r_center, -r_center, inv_filling_height], - [half_r_center, -r_center, inv_filling_height], - [0.5, -r_center, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_min_ctps - ) - ) - - branch_x_min_y_max_ctps = np.array( - [ - [-0.5, aux_column_width, 0.0], - [-seperator_distance, aux_column_width, 0.0], - [-branch_thickness, branch_thickness, 0.0], - [-0.5, seperator_distance, 0.0], - [-seperator_distance, seperator_distance, 0.0], - [-aux_column_width, seperator_distance, 0.0], - [-0.5, 0.5, 0.0], - [-seperator_distance, 0.5, 0.0], - [-aux_column_width, 0.5, 0.0], - [-0.5, aux_column_width, ctps_mid_height_bottom], - [ - -seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, seperator_distance, ctps_mid_height_bottom], - [ - -seperator_distance, - seperator_distance, - ctps_mid_height_bottom, - ], - [ - -aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [-0.5, 0.5, ctps_mid_height_bottom], - [-seperator_distance, 0.5, ctps_mid_height_bottom], - [-aux_column_width, 0.5, ctps_mid_height_bottom], - [-0.5, r_center, inv_filling_height], - [-half_r_center, r_center, inv_filling_height], - [-r_center, r_center, inv_filling_height], - [-0.5, half_r_center, inv_filling_height], - [-half_r_center, half_r_center, inv_filling_height], - [-r_center, half_r_center, inv_filling_height], - [-0.5, 0.5, inv_filling_height], - [-half_r_center, 0.5, inv_filling_height], - [-r_center, 0.5, inv_filling_height], - ] - ) + np.array([0.5, 0.5, 0.0]) - - spline_list.append( - base.Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_max_ctps - ) - ) - - return spline_list - else: - raise ValueError("Corner Type not supported") - - def create_tile( - self, - parameters=None, - seperator_distance=None, - center_expansion=1.0, - **kwargs, - ): - """Create an inverse microtile based on the parameters that describe - the branch thicknesses. - - Thickness parameters are used to describe the inner radius of the - outward facing branches - - Parameters - ---------- - parameters : tuple(np.array) - only first entry is used, defines the internal radii of the - branches - seperator_distance : float - position of the control points for higher order elements - center_expansion : float - thickness of center is expanded by a factor - - Returns - ------- - microtile_list : list(splines) - """ - - if not isinstance(center_expansion, float): - raise ValueError("Invalid Type") - if not ((center_expansion > 0.5) and (center_expansion < 1.5)): - raise ValueError("Center Expansion must be in (.5, 1.5)") - - if seperator_distance is None: - raise ValueError( - "Seperator Distance is missing. The value is required to " - "create watertight connections with neighboring elements." - " The value should be greater than the biggest branch " - "radius" - ) - - # Check if all radii are in allowed range - max_radius = min(0.5, (0.5 / center_expansion)) - max_radius = min(max_radius, seperator_distance) - - # set to default if nothing is given - if parameters is None: - self._logd("Setting branch thickness to default 0.2") - parameters = tuple([np.ones(6) * 0.2]) - [x_min_r, x_max_r, y_min_r, y_max_r, z_min_r, z_max_r] = parameters[ - 0 - ].tolist() - - for radius in [x_min_r, x_max_r, y_min_r, y_max_r, z_min_r, z_max_r]: - if not isinstance(radius, float): - raise ValueError("Invalid type") - if not (radius > 0 and radius < max_radius): - raise ValueError( - f"Radii must be in (0,{max_radius}) for " - f"center_expansion {center_expansion}" - ) - - # center radius - center_r = ( - (x_min_r + x_max_r + y_min_r + y_max_r + z_min_r + z_max_r) - / 6.0 - * center_expansion - ) - - # Auxiliary values for smooothing (mid-branch thickness) - aux_x_min = min(x_min_r, center_r) - aux_x_max = min(x_max_r, center_r) - aux_y_min = min(y_min_r, center_r) - aux_y_max = min(y_max_r, center_r) - aux_z_min = min(z_min_r, center_r) - aux_z_max = min(z_max_r, center_r) - # Branch midlength - hd_center = 0.5 * (0.5 + center_r) - - # - if seperator_distance is None: - seperator_distance = 0.45 - aux_column_width = 0.5 - 2 * (0.5 - seperator_distance) - - # Init return type - spline_list = [] - - # Start with branch interconnections - x_min_y_min = np.array( - [ - [-0.5, -0.5, -aux_column_width], - [-seperator_distance, -0.5, -aux_column_width], - [-y_min_r, -0.5, -y_min_r], - [-0.5, -seperator_distance, -aux_column_width], - [-hd_center, -hd_center, -aux_column_width], - [-aux_y_min, -hd_center, -aux_y_min], - [-0.5, -x_min_r, -x_min_r], - [-hd_center, -aux_x_min, -aux_x_min], - [-center_r, -center_r, -center_r], - [-0.5, -0.5, aux_column_width], - [-seperator_distance, -0.5, aux_column_width], - [-y_min_r, -0.5, y_min_r], - [-0.5, -seperator_distance, aux_column_width], - [-hd_center, -hd_center, aux_column_width], - [-aux_y_min, -hd_center, aux_y_min], - [-0.5, -x_min_r, x_min_r], - [-hd_center, -aux_x_min, aux_x_min], - [-center_r, -center_r, center_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 1], control_points=x_min_y_min) - ) - - x_max_y_min = np.array( - [ - [y_min_r, -0.5, -y_min_r], - [seperator_distance, -0.5, -aux_column_width], - [0.5, -0.5, -aux_column_width], - [aux_y_min, -hd_center, -aux_y_min], - [hd_center, -hd_center, -aux_column_width], - [0.5, -seperator_distance, -aux_column_width], - [center_r, -center_r, -center_r], - [hd_center, -aux_x_max, -aux_x_max], - [0.5, -x_max_r, -x_max_r], - [y_min_r, -0.5, y_min_r], - [seperator_distance, -0.5, aux_column_width], - [0.5, -0.5, aux_column_width], - [aux_y_min, -hd_center, aux_y_min], - [hd_center, -hd_center, aux_column_width], - [0.5, -seperator_distance, aux_column_width], - [center_r, -center_r, center_r], - [hd_center, -aux_x_max, aux_x_max], - [0.5, -x_max_r, x_max_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 1], control_points=x_max_y_min) - ) - - x_min_y_max = np.array( - [ - [-0.5, x_min_r, -x_min_r], - [-hd_center, aux_x_min, -aux_x_min], - [-center_r, center_r, -center_r], - [-0.5, seperator_distance, -aux_column_width], - [-hd_center, hd_center, -aux_column_width], - [-aux_y_max, hd_center, -aux_y_max], - [-0.5, 0.5, -aux_column_width], - [-seperator_distance, 0.5, -aux_column_width], - [-y_max_r, 0.5, -y_max_r], - [-0.5, x_min_r, x_min_r], - [-hd_center, aux_x_min, aux_x_min], - [-center_r, center_r, center_r], - [-0.5, seperator_distance, aux_column_width], - [-hd_center, hd_center, aux_column_width], - [-aux_y_max, hd_center, aux_y_max], - [-0.5, 0.5, aux_column_width], - [-seperator_distance, 0.5, aux_column_width], - [-y_max_r, 0.5, y_max_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 1], control_points=x_min_y_max) - ) - - x_max_y_max = np.array( - [ - [center_r, center_r, -center_r], - [hd_center, aux_x_max, -aux_x_max], - [0.5, x_max_r, -x_max_r], - [aux_y_max, hd_center, -aux_y_max], - [hd_center, hd_center, -aux_column_width], - [0.5, seperator_distance, -aux_column_width], - [y_max_r, 0.5, -y_max_r], - [seperator_distance, 0.5, -aux_column_width], - [0.5, 0.5, -aux_column_width], - [center_r, center_r, center_r], - [hd_center, aux_x_max, aux_x_max], - [0.5, x_max_r, x_max_r], - [aux_y_max, hd_center, aux_y_max], - [hd_center, hd_center, aux_column_width], - [0.5, seperator_distance, aux_column_width], - [y_max_r, 0.5, y_max_r], - [seperator_distance, 0.5, aux_column_width], - [0.5, 0.5, aux_column_width], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 1], control_points=x_max_y_max) - ) - - x_min_z_min = np.array( - [ - [-0.5, -aux_column_width, -0.5], - [-seperator_distance, -aux_column_width, -0.5], - [-z_min_r, -z_min_r, -0.5], - [-0.5, aux_column_width, -0.5], - [-seperator_distance, aux_column_width, -0.5], - [-z_min_r, z_min_r, -0.5], - [-0.5, -aux_column_width, -seperator_distance], - [-hd_center, -aux_column_width, -hd_center], - [-aux_z_min, -aux_z_min, -hd_center], - [-0.5, aux_column_width, -seperator_distance], - [-hd_center, aux_column_width, -hd_center], - [-aux_z_min, aux_z_min, -hd_center], - [-0.5, -x_min_r, -x_min_r], - [-hd_center, -aux_x_min, -aux_x_min], - [-center_r, -center_r, -center_r], - [-0.5, x_min_r, -x_min_r], - [-hd_center, aux_x_min, -aux_x_min], - [-center_r, center_r, -center_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 1, 2], control_points=x_min_z_min) - ) - - x_max_z_min = np.array( - [ - [z_min_r, -z_min_r, -0.5], - [seperator_distance, -aux_column_width, -0.5], - [0.5, -aux_column_width, -0.5], - [z_min_r, z_min_r, -0.5], - [seperator_distance, aux_column_width, -0.5], - [0.5, aux_column_width, -0.5], - [aux_z_min, -aux_z_min, -hd_center], - [hd_center, -aux_column_width, -hd_center], - [0.5, -aux_column_width, -seperator_distance], - [aux_z_min, aux_z_min, -hd_center], - [hd_center, aux_column_width, -hd_center], - [0.5, aux_column_width, -seperator_distance], - [center_r, -center_r, -center_r], - [hd_center, -aux_x_max, -aux_x_max], - [0.5, -x_max_r, -x_max_r], - [center_r, center_r, -center_r], - [hd_center, aux_x_max, -aux_x_max], - [0.5, x_max_r, -x_max_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 1, 2], control_points=x_max_z_min) - ) - - x_min_z_max = np.array( - [ - [-0.5, -x_min_r, x_min_r], - [-hd_center, -aux_x_min, aux_x_min], - [-center_r, -center_r, center_r], - [-0.5, x_min_r, x_min_r], - [-hd_center, aux_x_min, aux_x_min], - [-center_r, center_r, center_r], - [-0.5, -aux_column_width, seperator_distance], - [-hd_center, -aux_column_width, hd_center], - [-aux_z_max, -aux_z_max, hd_center], - [-0.5, aux_column_width, seperator_distance], - [-hd_center, aux_column_width, hd_center], - [-aux_z_max, aux_z_max, hd_center], - [-0.5, -aux_column_width, 0.5], - [-seperator_distance, -aux_column_width, 0.5], - [-z_max_r, -z_max_r, 0.5], - [-0.5, aux_column_width, 0.5], - [-seperator_distance, aux_column_width, 0.5], - [-z_max_r, z_max_r, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 1, 2], control_points=x_min_z_max) - ) - - x_max_z_max = np.array( - [ - [center_r, -center_r, center_r], - [hd_center, -aux_x_max, aux_x_max], - [0.5, -x_max_r, x_max_r], - [center_r, center_r, center_r], - [hd_center, aux_x_max, aux_x_max], - [0.5, x_max_r, x_max_r], - [aux_z_max, -aux_z_max, hd_center], - [hd_center, -aux_column_width, hd_center], - [0.5, -aux_column_width, seperator_distance], - [aux_z_max, aux_z_max, hd_center], - [hd_center, aux_column_width, hd_center], - [0.5, aux_column_width, seperator_distance], - [z_max_r, -z_max_r, 0.5], - [seperator_distance, -aux_column_width, 0.5], - [0.5, -aux_column_width, 0.5], - [z_max_r, z_max_r, 0.5], - [seperator_distance, aux_column_width, 0.5], - [0.5, aux_column_width, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 1, 2], control_points=x_max_z_max) - ) - - y_min_z_min = np.array( - [ - [-aux_column_width, -0.5, -0.5], - [aux_column_width, -0.5, -0.5], - [-aux_column_width, -seperator_distance, -0.5], - [aux_column_width, -seperator_distance, -0.5], - [-z_min_r, -z_min_r, -0.5], - [z_min_r, -z_min_r, -0.5], - [-aux_column_width, -0.5, -seperator_distance], - [aux_column_width, -0.5, -seperator_distance], - [-aux_column_width, -hd_center, -hd_center], - [aux_column_width, -hd_center, -hd_center], - [-aux_z_min, -aux_z_min, -hd_center], - [aux_z_min, -aux_z_min, -hd_center], - [-y_min_r, -0.5, -y_min_r], - [y_min_r, -0.5, -y_min_r], - [-aux_y_min, -hd_center, -aux_y_min], - [aux_y_min, -hd_center, -aux_y_min], - [-center_r, -center_r, -center_r], - [center_r, -center_r, -center_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[1, 2, 2], control_points=y_min_z_min) - ) - - y_max_z_min = np.array( - [ - [-z_min_r, z_min_r, -0.5], - [z_min_r, z_min_r, -0.5], - [-aux_column_width, seperator_distance, -0.5], - [aux_column_width, seperator_distance, -0.5], - [-aux_column_width, 0.5, -0.5], - [aux_column_width, 0.5, -0.5], - [-aux_z_min, aux_z_min, -hd_center], - [aux_z_min, aux_z_min, -hd_center], - [-aux_column_width, hd_center, -hd_center], - [aux_column_width, hd_center, -hd_center], - [-aux_column_width, 0.5, -seperator_distance], - [aux_column_width, 0.5, -seperator_distance], - [-center_r, center_r, -center_r], - [center_r, center_r, -center_r], - [-aux_y_max, hd_center, -aux_y_max], - [aux_y_max, hd_center, -aux_y_max], - [-y_max_r, 0.5, -y_max_r], - [y_max_r, 0.5, -y_max_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[1, 2, 2], control_points=y_max_z_min) - ) - - y_min_z_max = np.array( - [ - [-y_min_r, -0.5, y_min_r], - [y_min_r, -0.5, y_min_r], - [-aux_y_min, -hd_center, aux_y_min], - [aux_y_min, -hd_center, aux_y_min], - [-center_r, -center_r, center_r], - [center_r, -center_r, center_r], - [-aux_column_width, -0.5, seperator_distance], - [aux_column_width, -0.5, seperator_distance], - [-aux_column_width, -hd_center, hd_center], - [aux_column_width, -hd_center, hd_center], - [-aux_z_max, -aux_z_max, hd_center], - [aux_z_max, -aux_z_max, hd_center], - [-aux_column_width, -0.5, 0.5], - [aux_column_width, -0.5, 0.5], - [-aux_column_width, -seperator_distance, 0.5], - [aux_column_width, -seperator_distance, 0.5], - [-z_max_r, -z_max_r, 0.5], - [z_max_r, -z_max_r, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[1, 2, 2], control_points=y_min_z_max) - ) - - y_max_z_max = np.array( - [ - [-center_r, center_r, center_r], - [center_r, center_r, center_r], - [-aux_y_max, hd_center, aux_y_max], - [aux_y_max, hd_center, aux_y_max], - [-y_max_r, 0.5, y_max_r], - [y_max_r, 0.5, y_max_r], - [-aux_z_max, aux_z_max, hd_center], - [aux_z_max, aux_z_max, hd_center], - [-aux_column_width, hd_center, hd_center], - [aux_column_width, hd_center, hd_center], - [-aux_column_width, 0.5, seperator_distance], - [aux_column_width, 0.5, seperator_distance], - [-z_max_r, z_max_r, 0.5], - [z_max_r, z_max_r, 0.5], - [-aux_column_width, seperator_distance, 0.5], - [aux_column_width, seperator_distance, 0.5], - [-aux_column_width, 0.5, 0.5], - [aux_column_width, 0.5, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[1, 2, 2], control_points=y_max_z_max) - ) - - x_min_y_min_z_min = np.array( - [ - [-0.5, -0.5, -0.5], - [-seperator_distance, -0.5, -0.5], - [-aux_column_width, -0.5, -0.5], - [-0.5, -seperator_distance, -0.5], - [-seperator_distance, -seperator_distance, -0.5], - [-aux_column_width, -seperator_distance, -0.5], - [-0.5, -aux_column_width, -0.5], - [-seperator_distance, -aux_column_width, -0.5], - [-z_min_r, -z_min_r, -0.5], - [-0.5, -0.5, -seperator_distance], - [-seperator_distance, -0.5, -seperator_distance], - [-aux_column_width, -0.5, -seperator_distance], - [-0.5, -seperator_distance, -seperator_distance], - [-hd_center, -hd_center, -hd_center], - [-aux_column_width, -hd_center, -hd_center], - [-0.5, -aux_column_width, -seperator_distance], - [-hd_center, -aux_column_width, -hd_center], - [-aux_z_min, -aux_z_min, -hd_center], - [-0.5, -0.5, -aux_column_width], - [-seperator_distance, -0.5, -aux_column_width], - [-y_min_r, -0.5, -y_min_r], - [-0.5, -seperator_distance, -aux_column_width], - [-hd_center, -hd_center, -aux_column_width], - [-aux_y_min, -hd_center, -aux_y_min], - [-0.5, -x_min_r, -x_min_r], - [-hd_center, -aux_x_min, -aux_x_min], - [-center_r, -center_r, -center_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_min_y_min_z_min) - ) - - x_max_y_min_z_min = np.array( - [ - [aux_column_width, -0.5, -0.5], - [seperator_distance, -0.5, -0.5], - [0.5, -0.5, -0.5], - [aux_column_width, -seperator_distance, -0.5], - [seperator_distance, -seperator_distance, -0.5], - [0.5, -seperator_distance, -0.5], - [z_min_r, -z_min_r, -0.5], - [seperator_distance, -aux_column_width, -0.5], - [0.5, -aux_column_width, -0.5], - [aux_column_width, -0.5, -seperator_distance], - [seperator_distance, -0.5, -seperator_distance], - [0.5, -0.5, -seperator_distance], - [aux_column_width, -hd_center, -hd_center], - [hd_center, -hd_center, -hd_center], - [0.5, -seperator_distance, -seperator_distance], - [aux_z_min, -aux_z_min, -hd_center], - [hd_center, -aux_column_width, -hd_center], - [0.5, -aux_column_width, -seperator_distance], - [y_min_r, -0.5, -y_min_r], - [seperator_distance, -0.5, -aux_column_width], - [0.5, -0.5, -aux_column_width], - [aux_y_min, -hd_center, -aux_y_min], - [hd_center, -hd_center, -aux_column_width], - [0.5, -seperator_distance, -aux_column_width], - [center_r, -center_r, -center_r], - [hd_center, -aux_x_max, -aux_x_max], - [0.5, -x_max_r, -x_max_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_max_y_min_z_min) - ) - - x_min_y_max_z_min = np.array( - [ - [-0.5, aux_column_width, -0.5], - [-seperator_distance, aux_column_width, -0.5], - [-z_min_r, z_min_r, -0.5], - [-0.5, seperator_distance, -0.5], - [-seperator_distance, seperator_distance, -0.5], - [-aux_column_width, seperator_distance, -0.5], - [-0.5, 0.5, -0.5], - [-seperator_distance, 0.5, -0.5], - [-aux_column_width, 0.5, -0.5], - [-0.5, aux_column_width, -seperator_distance], - [-hd_center, aux_column_width, -hd_center], - [-aux_z_min, aux_z_min, -hd_center], - [-0.5, seperator_distance, -seperator_distance], - [-hd_center, hd_center, -hd_center], - [-aux_column_width, hd_center, -hd_center], - [-0.5, 0.5, -seperator_distance], - [-seperator_distance, 0.5, -seperator_distance], - [-aux_column_width, 0.5, -seperator_distance], - [-0.5, x_min_r, -x_min_r], - [-hd_center, aux_x_min, -aux_x_min], - [-center_r, center_r, -center_r], - [-0.5, seperator_distance, -aux_column_width], - [-hd_center, hd_center, -aux_column_width], - [-aux_y_max, hd_center, -aux_y_max], - [-0.5, 0.5, -aux_column_width], - [-seperator_distance, 0.5, -aux_column_width], - [-y_max_r, 0.5, -y_max_r], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_min_y_max_z_min) - ) - - x_max_y_max_z_min = np.array( - [ - [z_min_r, z_min_r, -0.5], - [seperator_distance, aux_column_width, -0.5], - [0.5, aux_column_width, -0.5], - [aux_column_width, seperator_distance, -0.5], - [seperator_distance, seperator_distance, -0.5], - [0.5, seperator_distance, -0.5], - [aux_column_width, 0.5, -0.5], - [seperator_distance, 0.5, -0.5], - [0.5, 0.5, -0.5], - [aux_z_min, aux_z_min, -hd_center], - [hd_center, aux_column_width, -hd_center], - [0.5, aux_column_width, -seperator_distance], - [aux_column_width, hd_center, -hd_center], - [hd_center, hd_center, -hd_center], - [0.5, seperator_distance, -seperator_distance], - [aux_column_width, 0.5, -seperator_distance], - [seperator_distance, 0.5, -seperator_distance], - [0.5, 0.5, -seperator_distance], - [center_r, center_r, -center_r], - [hd_center, aux_x_max, -aux_x_max], - [0.5, x_max_r, -x_max_r], - [aux_y_max, hd_center, -aux_y_max], - [hd_center, hd_center, -aux_column_width], - [0.5, seperator_distance, -aux_column_width], - [y_max_r, 0.5, -y_max_r], - [seperator_distance, 0.5, -aux_column_width], - [0.5, 0.5, -aux_column_width], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_max_y_max_z_min) - ) - - x_min_y_min_z_max = np.array( - [ - [-0.5, -0.5, aux_column_width], - [-seperator_distance, -0.5, aux_column_width], - [-y_min_r, -0.5, y_min_r], - [-0.5, -seperator_distance, aux_column_width], - [-hd_center, -hd_center, aux_column_width], - [-aux_y_min, -hd_center, aux_y_min], - [-0.5, -x_min_r, x_min_r], - [-hd_center, -aux_x_min, aux_x_min], - [-center_r, -center_r, center_r], - [-0.5, -0.5, seperator_distance], - [-seperator_distance, -0.5, seperator_distance], - [-aux_column_width, -0.5, seperator_distance], - [-0.5, -seperator_distance, seperator_distance], - [-hd_center, -hd_center, hd_center], - [-aux_column_width, -hd_center, hd_center], - [-0.5, -aux_column_width, seperator_distance], - [-hd_center, -aux_column_width, hd_center], - [-aux_z_max, -aux_z_max, hd_center], - [-0.5, -0.5, 0.5], - [-seperator_distance, -0.5, 0.5], - [-aux_column_width, -0.5, 0.5], - [-0.5, -seperator_distance, 0.5], - [-seperator_distance, -seperator_distance, 0.5], - [-aux_column_width, -seperator_distance, 0.5], - [-0.5, -aux_column_width, 0.5], - [-seperator_distance, -aux_column_width, 0.5], - [-z_max_r, -z_max_r, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_min_y_min_z_max) - ) - - x_max_y_min_z_max = np.array( - [ - [y_min_r, -0.5, y_min_r], - [seperator_distance, -0.5, aux_column_width], - [0.5, -0.5, aux_column_width], - [aux_y_min, -hd_center, aux_y_min], - [hd_center, -hd_center, aux_column_width], - [0.5, -seperator_distance, aux_column_width], - [center_r, -center_r, center_r], - [hd_center, -aux_x_max, aux_x_max], - [0.5, -x_max_r, x_max_r], - [aux_column_width, -0.5, seperator_distance], - [seperator_distance, -0.5, seperator_distance], - [0.5, -0.5, seperator_distance], - [aux_column_width, -hd_center, hd_center], - [hd_center, -hd_center, hd_center], - [0.5, -seperator_distance, seperator_distance], - [aux_z_max, -aux_z_max, hd_center], - [hd_center, -aux_column_width, hd_center], - [0.5, -aux_column_width, seperator_distance], - [aux_column_width, -0.5, 0.5], - [seperator_distance, -0.5, 0.5], - [0.5, -0.5, 0.5], - [aux_column_width, -seperator_distance, 0.5], - [seperator_distance, -seperator_distance, 0.5], - [0.5, -seperator_distance, 0.5], - [z_max_r, -z_max_r, 0.5], - [seperator_distance, -aux_column_width, 0.5], - [0.5, -aux_column_width, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_max_y_min_z_max) - ) - - x_min_y_max_z_max = np.array( - [ - [-0.5, x_min_r, x_min_r], - [-hd_center, aux_x_min, aux_x_min], - [-center_r, center_r, center_r], - [-0.5, seperator_distance, aux_column_width], - [-hd_center, hd_center, aux_column_width], - [-aux_y_max, hd_center, aux_y_max], - [-0.5, 0.5, aux_column_width], - [-seperator_distance, 0.5, aux_column_width], - [-y_max_r, 0.5, y_max_r], - [-0.5, aux_column_width, seperator_distance], - [-hd_center, aux_column_width, hd_center], - [-aux_z_max, aux_z_max, hd_center], - [-0.5, seperator_distance, seperator_distance], - [-hd_center, hd_center, hd_center], - [-aux_column_width, hd_center, hd_center], - [-0.5, 0.5, seperator_distance], - [-seperator_distance, 0.5, seperator_distance], - [-aux_column_width, 0.5, seperator_distance], - [-0.5, aux_column_width, 0.5], - [-seperator_distance, aux_column_width, 0.5], - [-z_max_r, z_max_r, 0.5], - [-0.5, seperator_distance, 0.5], - [-seperator_distance, seperator_distance, 0.5], - [-aux_column_width, seperator_distance, 0.5], - [-0.5, 0.5, 0.5], - [-seperator_distance, 0.5, 0.5], - [-aux_column_width, 0.5, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_min_y_max_z_max) - ) - - x_max_y_max_z_max = np.array( - [ - [center_r, center_r, center_r], - [hd_center, aux_x_max, aux_x_max], - [0.5, x_max_r, x_max_r], - [aux_y_max, hd_center, aux_y_max], - [hd_center, hd_center, aux_column_width], - [0.5, seperator_distance, aux_column_width], - [y_max_r, 0.5, y_max_r], - [seperator_distance, 0.5, aux_column_width], - [0.5, 0.5, aux_column_width], - [aux_z_max, aux_z_max, hd_center], - [hd_center, aux_column_width, hd_center], - [0.5, aux_column_width, seperator_distance], - [aux_column_width, hd_center, hd_center], - [hd_center, hd_center, hd_center], - [0.5, seperator_distance, seperator_distance], - [aux_column_width, 0.5, seperator_distance], - [seperator_distance, 0.5, seperator_distance], - [0.5, 0.5, seperator_distance], - [z_max_r, z_max_r, 0.5], - [seperator_distance, aux_column_width, 0.5], - [0.5, aux_column_width, 0.5], - [aux_column_width, seperator_distance, 0.5], - [seperator_distance, seperator_distance, 0.5], - [0.5, seperator_distance, 0.5], - [aux_column_width, 0.5, 0.5], - [seperator_distance, 0.5, 0.5], - [0.5, 0.5, 0.5], - ] - ) + np.array([0.5, 0.5, 0.5]) - - spline_list.append( - base.Bezier(degrees=[2, 2, 2], control_points=x_max_y_max_z_max) - ) - - return spline_list diff --git a/gustaf/spline/microstructure/tiles/tilebase.py b/gustaf/spline/microstructure/tiles/tilebase.py deleted file mode 100644 index 93fe5d081..000000000 --- a/gustaf/spline/microstructure/tiles/tilebase.py +++ /dev/null @@ -1,63 +0,0 @@ -from gustaf.spline import base - - -class TileBase(base.GustafBase): - """ - Base class for tile objects - """ - - def __init__(self): - """ - Init Values to None - """ - self._dim = None - self._evaluation_points = None - self._parameter_space_dimension = None - - @property - def parameter_space_dimension(self): - """Number of parameters per evaluation point.""" - if self._parameter_space_dimension is None: - raise TypeError( - "Inherited Tile-types need to provide " - "_parameter_space_dimension, see documentation." - ) - return self._parameter_space_dimension - - @property - def evaluation_points(self): - """Positions in the parametrization function to be evaluated when tile - " "is constructed prior to composition. - - Parameters - ---------- - None - - Returns - ------- - evaluation_points : np.ndarray(6,3) - """ - if self._evaluation_points is None: - raise TypeError( - "Inherited Tile-types need to provide _evaluation_points, see " - "documentation." - ) - return self._evaluation_points - - @property - def dim(self): - """Returns dimensionality in physical space of the Microtile. - - Parameters - ---------- - None - - Returns - ------- - dim : int - """ - if self._dim is None: - raise TypeError( - "Inherited Tile-types need to provide _dim, see documentation." - ) - return self._dim diff --git a/gustaf/spline/proximity.py b/gustaf/spline/proximity.py deleted file mode 100644 index 734f11c84..000000000 --- a/gustaf/spline/proximity.py +++ /dev/null @@ -1,65 +0,0 @@ -"""gustaf/spline/proximity.py. - -Closest what? -""" - - -def closest_control_points( - spline, - query_points, - return_distances=False, -): - """Returns indices of closest control points. - - Parameters - ----------- - spline: BSpline or NURBS - query_points: (n, spline.dim) np.ndarray - float - return_distances: bool - - tolerance: float - Default is settings.Tolerance. Only relevant iff nearest_only==False - - Returns - -------- - indices: (n,) np.ndarray - distances: (n, spline.dim) np.ndarray - iff return_distances==True - """ - from scipy.spatial import cKDTree as KDTree - - kdt = KDTree(spline.control_points) - - dist, ids = kdt.query(query_points) - - if return_distances: - return ids, dist - - else: - return ids - - -def closest_parametric_coordinate( - spline, - query_points, -): - """Finds closest points using.""" - pass - - -class Proximity: - """Helper class to allow direct proximity queries for spline obj (BSpline - or NURBS). Internal use only. - - Examples - --------- - >>> my_spline = - >>> closest_cp_ids = my_spline.proximity.closest_control_points(queries) - """ - - def __init__(self, spl): - self.spline = spl - - def closest_control_points(self, *args, **kwargs): - return closest_control_points(self.spline, *args, **kwargs) diff --git a/gustaf/spline/visualize.py b/gustaf/spline/visualize.py deleted file mode 100644 index da499a1e8..000000000 --- a/gustaf/spline/visualize.py +++ /dev/null @@ -1,367 +0,0 @@ -"""gustaf/spline/visualize.py. - -Spline visualization module. Supports visualization of following spline -(para_dim, dim) pairs: (1, 2), (1, 3), (2, 2), (2, 3), (3, 3) -""" -import numpy as np - -from gustaf import Vertices, settings -from gustaf.helpers import options -from gustaf.utils import log -from gustaf.utils.arr import enforce_len - - -class SplineShowOption(options.ShowOption): - """ - Show options for splines. - """ - - __slots__ = () - - # if we start to support more backends, most of this options should become - # some sort of spline common. - _valid_options = options.make_valid_options( - *options.vedo_common_options, - options.Option( - "vedo", - "control_points", - "Show spline's control points." - "Options propagates to control mesh, unless it is specified.", - (bool,), - ), - options.Option( - "vedo", - "control_mesh", - "Show spline's control mesh.", - (bool,), - ), - options.Option("vedo", "knots", "Show spline's knots.", (bool,)), - options.Option( - "vedo", - "fitting_queries", - "Shows fitting queries if they are locally saved in splines.", - (bool,), - ), - options.Option( - "vedo", - "control_points_c", - "Color of control_points in {rgb, RGB, str of (hex, name), int}", - (str, tuple, list, int), - ), - options.Option( - "vedo", - "control_mesh_c", - "Color of control_mesh in {rgb, RGB, str of (hex, name), int}", - (str, tuple, list, int), - ), - options.Option( - "vedo", - "control_mesh_lw", - "Transparency of control points in range [0, 1].", - (int), - ), - options.Option( - "vedo", - "control_points_alpha", - "Transparency of control points in range [0, 1].", - (float, int), - ), - options.Option( - "vedo", - "control_point_ids", - "Show ids of control_points.", - (bool,), - ), - options.Option( - "vedo", - "resolutions", - "Sampling resolution for spline.", - (int, list, tuple, np.ndarray), - ), - options.Option( - "vedo", - "arrow_data_on", - "Specify parametric coordinates to place arrow_data.", - (list, tuple, np.ndarray), - ), - ) - - _helps = "GustafSpline" - - def __init__(self, helpee): - """ - Parameters - ---------- - helpee: GustafSpline - """ - self._helpee = helpee - # checks if helpee inherits from GustafSpline - if self._helps not in str(type(helpee).__mro__): - raise TypeError( - f"This show option if for {self._helps}.", - f"Given helpee is {type(helpee)}.", - ) - self._options = dict() - self._backend = settings.VISUALIZATION_BACKEND - self._options[self._backend] = dict() - - -def make_showable(spline): - return eval(f"_{spline.show_options._backend}_showable(spline)") - - -def _vedo_showable(spline): - """ - Goes through common procedures for preparing showable splines. - - Parameters - ---------- - None - - Returns - ------- - gus_dict: dict - dict of sampled spline as gustaf objects - """ - # get spline and knots - gus_primitives = eval(f"_vedo_showable_para_dim_{spline.para_dim}(spline)") - - # apply spline color - sampled_spline = gus_primitives["spline"] - default_color = "green" if spline.para_dim > 1 else "black" - sampled_spline.show_options["c"] = spline.show_options.get( - "c", default_color - ) - sampled_spline.show_options["alpha"] = spline.show_options.get("alpha", 1) - sampled_spline.show_options["lighting"] = spline.show_options.get( - "lighting", "glossy" - ) - - # axis? - sampled_spline.show_options["axes"] = spline.show_options.get( - "axes", False - ) - - # with color representable, scalar field data - res = enforce_len( - spline.show_options.get("resolutions", 100), spline.para_dim - ) - data_name = spline.show_options.get("data_name", None) - sampled_spline_data = spline.spline_data.as_scalar( - data_name, res, default=None - ) - if data_name is not None and sampled_spline_data is not None: - # transfer data - sampled_spline.vertex_data[data_name] = sampled_spline_data - - # transfer options - maybe vectorized query? - keys = ("vmin", "vmax", "scalarbar", "cmap", "cmap_alpha") - spline.show_options.copy_valid_options( - sampled_spline.show_options, keys - ) - - # mark that we want to see this data - sampled_spline.show_options["data_name"] = data_name - - elif data_name is not None and sampled_spline_data is None: - log.debug(f"No spline_data named ({data_name}) for {spline}. Skipping") - - # with arrow representable, vector data - adata_name = spline.show_options.get("arrow_data", None) - adapted_adata = spline.spline_data.get(adata_name, None) - if adata_name is not None and adapted_adata is not None: - # if location is specified, this will be a separate Vertices obj with - # configured arrow_data - has_locations = adapted_adata.has_locations - adata_on = "arrow_data_on" in spline.show_options.keys() - create_vertices = has_locations or adata_on - - # this case causes conflict of interest. raise - if has_locations and adata_on: - raise ValueError( - f"arrow_data-({adata_name}) has fixed location, " - "but and `arrow_data_on` is set.", - ) - - if create_vertices: - # prepare corresponding queries - if adapted_adata.has_locations: - queries = adapted_adata.locations - on = None - else: - queries = spline.show_options["arrow_data_on"] - on = queries - - # bound / dim check - bounds = spline.parametric_bounds - if queries.shape[1] != len(bounds[0]): - raise ValueError( - "Dimension mismatch: arrow_data locations-" - f"{queries.shape[1]} / para_dim-{spline.para_dim}." - ) - # tolerance padding. may still cause issues in splinepy. - # in that case, we will have to scale queries. - lb_diff = queries.min(axis=0) - bounds[0] + settings.TOLERANCE - ub_diff = queries.max(axis=0) - bounds[1] - settings.TOLERANCE - if any(lb_diff < 0) or any(ub_diff > 0): - raise ValueError( - f"Specified locations of ({adata_name}) are out side the " - f"parametric bounds ({bounds}) by [{lb_diff}, {ub_diff}]." - ) - - # get arrow - adata = spline.spline_data.as_arrow(adata_name, on=on) - - # create vertices that can be shown as arrows - loc_vertices = Vertices(spline.evaluate(queries), copy=False) - loc_vertices.vertex_data[adata_name] = adata - - # transfer options - keys = ("arrow_data_scale", "arrow_data_color", "arrow_data") - spline.show_options.copy_valid_options( - loc_vertices.show_options, keys - ) - - # add to primitives - gus_primitives["arrow_data"] = loc_vertices - - else: # sample arrows and append to sampled spline. - sampled_spline.vertex_data[ - adata_name - ] = spline.spline_data.as_arrow(adata_name, resolutions=res) - # transfer options - keys = ("arrow_data_scale", "arrow_data_color", "arrow_data") - spline.show_options.copy_valid_options( - sampled_spline.show_options, keys - ) - - # double check on same obj ref - # merge vertices here, so that we return a watertight mesh - gus_primitives["spline"] = sampled_spline.merge_vertices() - - # control_points & control_points_alpha - show_cps = spline.show_options.get("control_points", True) - if show_cps: - # control points (vertices) - cps = spline.extract.control_points() - cps.show_options["c"] = spline.show_options.get( - "control_points_c", "red" - ) - cps.show_options["r"] = 10 - cps.show_options["alpha"] = spline.show_options.get( - "control_points_alpha", 1.0 - ) - # add - gus_primitives["control_points"] = cps - - if spline.show_options.get("control_point_ids", True): - # a bit redundant, but nicely separable - cp_ids = spline.extract.control_points() - cp_ids.show_options["labels"] = np.arange(len(cp_ids.vertices)) - cp_ids.show_options["label_options"] = {"font": "VTK"} - gus_primitives["control_point_ids"] = cp_ids - - if spline.show_options.get("control_mesh", show_cps): - # pure control mesh - c_mesh = spline.extract.control_mesh() # either edges or faces - if spline.para_dim != 1: - c_mesh = c_mesh.to_edges(unique=True) - - c_mesh.show_options["c"] = spline.show_options.get( - "control_mesh_c", "red" - ) - c_mesh.show_options["lw"] = spline.show_options.get( - "control_mesh_lw", 4 - ) - c_mesh.show_options["alpha"] = spline.show_options.get( - "control_points_alpha", 0.8 - ) - # add - gus_primitives["control_mesh"] = c_mesh - - # fitting queries - if hasattr(spline, "_fitting_queries") and spline.show_options.get( - "fitting_queries", True - ): - fqs = Vertices(spline._fitting_queries) - fqs.show_options["c"] = "blue" - fqs.show_options["r"] = 10 - gus_primitives["fitting_queries"] = fqs - - return gus_primitives - - -def _vedo_showable_para_dim_1(spline): - """Assumes showability check has been already performed. - - Parameters - ---------- - spline: GustafSpline - - Returns - ------- - gus_primitives: dict - keys are {spline, knots} - """ - gus_primitives = dict() - res = enforce_len( - spline.show_options.get("resolutions", 100), spline.para_dim - ) - sp = spline.extract.edges(res[0]) - - # specify curve width - sp.show_options["lw"] = 8 - # add spline - gus_primitives["spline"] = sp - - # place knots - if spline.show_options.get("knots", True): - uks = np.asanyarray(spline.unique_knots[0]).reshape(-1, 1) - knots = Vertices(spline.evaluate(uks)) - knots.show_options["labels"] = ["x"] * len(uks) - knots.show_options["label_options"] = { - "justify": "center", - "c": "green", - } - gus_primitives["knots"] = knots - - return gus_primitives - - -def _vedo_showable_para_dim_2(spline): - """ - Assumes showability check has been already performed - - Parameters - ---------- - spline: GustafSpline - - Returns - ------- - gus_primitives: dict - keys are {spline, knots} - """ - gus_primitives = dict() - res = enforce_len( - spline.show_options.get("resolutions", 100), spline.para_dim - ) - sp = spline.extract.faces(res, watertight=False) # always watertight - gus_primitives["spline"] = sp - - # knots - if spline.show_options.get("knots", True): - knot_lines = spline.extract.edges(res, all_knots=True) - knot_lines.show_options["c"] = "black" - knot_lines.show_options["lw"] = 3 - gus_primitives["knots"] = knot_lines - - return gus_primitives - - -def _vedo_showable_para_dim_3(spline): - """ - Currently same as _vedo_showable_para_dim_2 - """ - # we keep the watertight to False, since we may wanna plot some data with - # same values. (issue #120) - return _vedo_showable_para_dim_2(spline) From 9d76cf48d0d2af46f30b72ed61df01c55309a817 Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Wed, 21 Jun 2023 14:55:05 +0200 Subject: [PATCH 03/11] show() checks hasattr(_, "showable") gustaf instance check is not feasible --- gustaf/show.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gustaf/show.py b/gustaf/show.py index 4668d1407..b8053e90b 100644 --- a/gustaf/show.py +++ b/gustaf/show.py @@ -7,7 +7,6 @@ import numpy as np from gustaf import settings, utils -from gustaf._base import GustafBase # @linux it raises error if vedo is imported inside the function. try: @@ -164,7 +163,7 @@ def cam_tuple_to_list(dict_cam): if not isinstance(sl, list): sl = [sl] for k, item in enumerate(sl): - if isinstance(item, GustafBase): + if hasattr(item, "showable"): tmp_showable = item.showable(backend="vedo", **kwargs) # splines return dict # - maybe it is time to do some typing.. From 3e4c599030dc45405aea5091d30232c57236b3bf Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Wed, 21 Jun 2023 15:02:06 +0200 Subject: [PATCH 04/11] remove splines from setup --- setup.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/setup.py b/setup.py index bd867726e..a78b814d9 100644 --- a/setup.py +++ b/setup.py @@ -19,14 +19,11 @@ "gustaf", "gustaf.utils", "gustaf.io", - "gustaf.spline", "gustaf.create", "gustaf.helpers", - "gustaf.spline.microstructure", - "gustaf.spline.microstructure.tiles", ], install_requires=["numpy"], - extras_require={"all": ["vedo>=2023.4.3", "scipy", "meshio", "splinepy"]}, + extras_require={"all": ["vedo>=2023.4.3", "scipy", "meshio"]}, classifiers=[ "Development Status :: 2 - Pre-Alpha", "License :: OSI Approved :: MIT License", From 4d1a4e06b71aab6ccbf98a1f744aaa5f8c5aa032 Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Wed, 21 Jun 2023 15:08:31 +0200 Subject: [PATCH 05/11] rm spline tests --- tests/conftest.py | 155 ---------------------- tests/test_spline/test_create.py | 126 ------------------ tests/test_spline/test_ffd.py | 216 ------------------------------- 3 files changed, 497 deletions(-) delete mode 100644 tests/test_spline/test_create.py delete mode 100644 tests/test_spline/test_ffd.py diff --git a/tests/conftest.py b/tests/conftest.py index 02c32eb89..bcd3a74ec 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -139,161 +139,6 @@ def volumes_hexa(vertices_3d, hexa_connec): return gus.Volumes(vertices_3d, hexa_connec) -@pytest.fixture -def control_points_2d(): - CPS_2D = np.array( - [ - [0, 0], - [1, 0], - [2, 0], - [3, 0], - [4, 0], - [0, 1], - [1, 2], - [2, 1], - [3, 2], - [4, 1], - ], - dtype=np.float64, - ) - return CPS_2D - - -@pytest.fixture -def control_points_3d(): - control_points = list() - for i in range(3): - for j in range(3): - for k in range(3): - control_points.append([i * 0.5, j * 0.5, k * 0.5]) - return control_points - - -@pytest.fixture -def control_points_3d_deformed(control_points_3d): - # change a control point so that there is a deformation - control_points_3d[22] = [1.5, 0.5, 0.5] - control_points_3d[2] = [0.25, 0.25, 0.75] - return control_points_3d - - -@pytest.fixture -def knot_vector_3d(): - kv_3d = [ - [0, 0, 0, 4, 4, 4], - [0, 0, 0, 1, 1, 1], - [5, 5, 5, 10, 10, 10], - ] - return kv_3d - - -@pytest.fixture -def degrees_3d(): - d_3d = [2, 2, 2] - return d_3d - - -@pytest.fixture -def bspline_3d(control_points_3d, knot_vector_3d, degrees_3d): - spline_3d = gus.BSpline( - degrees_3d, - knot_vector_3d, - control_points_3d, - ) - return spline_3d - - -@pytest.fixture -def bspline_3d_deformed( - control_points_3d_deformed, knot_vector_3d, degrees_3d -): - spline_3d = gus.BSpline( - degrees_3d, - knot_vector_3d, - control_points_3d_deformed, - ) - return spline_3d - - -@pytest.fixture -def bspline_para_1_dim_2(): - spline_miss_match = gus.BSpline( - [2], [[0, 0, 0, 1, 1, 1]], [[0, 0], [1, 1], [2, 0]] - ) - return spline_miss_match - - -@pytest.fixture -def knot_vector_2d(): - KVS_2D = [[0, 0, 0, 0.3, 0.7, 1, 1, 1], [0, 0, 1, 1]] - return KVS_2D - - -@pytest.fixture -def degrees_2d_nu(): - DEGREES_2D_NU = [2, 1] - return DEGREES_2D_NU - - -@pytest.fixture -def degrees_2d_u(): - DEGREES_2D_U = [4, 1] - return DEGREES_2D_U - - -@pytest.fixture -def weights_2d(): - WEIGHTS_2D = np.array( - [ - [1.0], - [0.8], - [1.0], - [0.8], - [1.0], - [1.0], - [0.8], - [1.0], - [0.8], - [1.0], - ], - dtype=np.float64, - ) - return WEIGHTS_2D - - -@pytest.fixture -def bspline_2d(control_points_2d, degrees_2d_nu, knot_vector_2d): - return gus.BSpline( - control_points=control_points_2d, - degrees=degrees_2d_nu, - knot_vectors=knot_vector_2d, - ) - - -@pytest.fixture -def nurbs_2d(control_points_2d, degrees_2d_nu, knot_vector_2d, weights_2d): - return gus.NURBS( - control_points=control_points_2d, - degrees=degrees_2d_nu, - knot_vectors=knot_vector_2d, - weights=weights_2d, - ) - - -@pytest.fixture -def bezier_2d(control_points_2d, degrees_2d_u): - return gus.Bezier(control_points=control_points_2d, degrees=degrees_2d_u) - - -@pytest.fixture -def rationalbezier_2d(control_points_2d, degrees_2d_u, weights_2d): - return gus.RationalBezier( - control_points=control_points_2d, - degrees=degrees_2d_u, - weights=weights_2d, - ) - - @pytest.fixture def provide_data_to_unittest( request, diff --git a/tests/test_spline/test_create.py b/tests/test_spline/test_create.py deleted file mode 100644 index caeceac09..000000000 --- a/tests/test_spline/test_create.py +++ /dev/null @@ -1,126 +0,0 @@ -import numpy as np -import pytest - -import gustaf as gus - - -@pytest.mark.parametrize("values", [0, 1, {"hallo": 1}, [1, 2, 3], "test"]) -def test_extrude_error_on_no_spline_given(values): - with pytest.raises(NotImplementedError): - gus.spline.create.extruded(values) - - -@pytest.mark.parametrize( - "spline_name", ["bspline_2d", "nurbs_2d", "bezier_2d", "rationalbezier_2d"] -) -@pytest.mark.parametrize( - "axis,error", - [ - (None, True), - (1, True), - ([1], True), - ({"hallo": 1}, True), - ("hallo", True), - (np.random.rand(3), False), - ], -) -def test_extrude(spline_name: str, axis, error, request): - # get the correct spline from the provided fixtures - spline: gus.spline.base.GustafSpline = request.getfixturevalue(spline_name) - if error: - with pytest.raises(ValueError): - spline.create.extruded(extrusion_vector=axis) - else: - extrudate = spline.create.extruded(extrusion_vector=axis) - x, y, z = np.random.rand(3).tolist() - assert np.allclose( - extrudate.evaluate([[x, y, z]]), - np.hstack((spline.evaluate([[x, y]]), np.zeros((1, 1)))) - + z * axis, - ) - - -@pytest.mark.parametrize("values", [0, 1, {"hallo": 1}, [1, 2, 3], "test"]) -def test_revolved_error_on_no_spline_given(values): - with pytest.raises(NotImplementedError): - gus.spline.create.revolved(values) - - -@pytest.mark.parametrize( - "spline_name", ["bspline_2d", "nurbs_2d", "bezier_2d", "rationalbezier_2d"] -) -@pytest.mark.parametrize( - "axis,center,angle,n_knot_span,degree,error", - [ - # (None, None, None, None, True, True), - tuple([1]) + tuple([None] * 3) + tuple([True, True]), - tuple([[1]]) + tuple([None] * 3) + tuple([True, True]), - tuple([[0, 0, 1e-18]]) + tuple([None] * 3) + tuple([True, True]), - tuple([{"hallo": 1}]) + tuple([None] * 3) + tuple([True, True]), - tuple(["hallo"]) + tuple([None] * 3) + tuple([True, True]), - # (np.random.rand(3))+tuple([None]*4)+tuple((False)) - ], -) -def test_revolved_3d( - spline_name: str, axis, center, angle, n_knot_span, degree, error, request -): - # get the correct spline from the provided fixtures - spline: gus.spline.base.GustafSpline = request.getfixturevalue(spline_name) - if error: - with pytest.raises(ValueError): - spline.create.revolved(axis, center, angle, n_knot_span, degree) - else: - if angle is None: - angle = 360 - cc, ss = np.cos(angle), np.sin(angle) - r = np.array([[cc, -ss, 0], [ss, cc, 0], [0, 0, 1]]) - revolved = spline.create.revolved( - axis, center, angle, n_knot_span, degree - ) - - dim_bumped_cps = np.zeros((spline.control_points.shape[0], 1)) - - ref_sol = np.matmul( - np.hstack((spline.control_points, dim_bumped_cps)), r.T - ) - - assert np.allclose( - revolved.control_points[-10:, :], - ref_sol, - ), f"{spline.whatami} failed revolution" - - # Test Revolution Routine - - -def test_create_revolution(): - """ - Test revolution routines for different input types and arguments - """ - # Make some lines - bezier_line = gus.Bezier(control_points=[[1, 0], [2, 1]], degrees=[1]) - nurbs_line = bezier_line.nurbs - - # Revolve always around z-axis - # init rotation matrix - r_angle = np.random.rand() - r_center = np.array([1, 0]) - cc, ss = np.cos(r_angle), np.sin(r_angle) - R2 = np.array([[cc, -ss], [ss, cc]]) - - # Test 2D Revolutions of lines - for spline_g in (bezier_line, nurbs_line): - assert np.allclose( - spline_g.create.revolved( - angle=r_angle, degree=False - ).control_points[-2:, :], - np.matmul(spline_g.control_points, R2.T), - ) - - # Test 2D Revolutions of lines around center - for spline_g in (bezier_line, nurbs_line): - assert np.allclose( - spline_g.create.revolved( - angle=r_angle, center=r_center, degree=False - ).control_points[-2:, :], - np.matmul(spline_g.control_points - r_center, R2.T) + r_center, - ), f"{spline_g.whatami} failed revolution around center" diff --git a/tests/test_spline/test_ffd.py b/tests/test_spline/test_ffd.py deleted file mode 100644 index 340d83131..000000000 --- a/tests/test_spline/test_ffd.py +++ /dev/null @@ -1,216 +0,0 @@ -from typing import List - -import numpy as np -import pytest - -from gustaf.spline.ffd import FFD - - -@pytest.mark.parametrize( - "init_values, throw_error", - [ - ([], False), # test empty input - (["jallo"], True), - ([1], True), - (["fixture_bspline_2d"], True), - (["fixture_faces_tri"], False), - (["fixture_faces_quad"], False), - (["fixture_faces_tri", "fixture_bspline_3d"], False), - (["fixture_faces_tri", "fixture_bspline_3d_deformed"], False), - (["fixture_faces_tri", 1], True), - (["fixture_faces_tri", "testing"], True), - ], -) -def test_init_error(init_values: List, throw_error, request): - for idx, value in enumerate(init_values): - if isinstance(value, str): - if "fixture_" in value: - init_values[idx] = request.getfixturevalue(value[8:]) - if throw_error: - with pytest.raises(ValueError): - FFD(*init_values) - else: - FFD(*init_values) - - -def test_mesh_with_empty_init(faces_quad): - a = FFD() - - a.mesh = faces_quad - - assert np.allclose(a.mesh.vertices, faces_quad.vertices) - - assert np.allclose(a.spline.dim, faces_quad.vertices.shape[1]) - - assert np.allclose(a.spline.dim, a.spline.para_dim) - - assert a._spline._data.get("gustaf_ffd_computed", False) - - a.mesh - - -def test_spline_with_empty_init(bspline_2d): - a = FFD() - - a.spline = bspline_2d - - with pytest.raises(RuntimeError): - a.mesh - - assert np.allclose(a.control_points, bspline_2d.control_points) - - -def test_control_point_setter_with_empty_init(bspline_2d): - a = FFD() - - # can not set control points if spline is not set - with pytest.raises(ValueError): - a.control_points = [1, 2, 3, 4] - - a.spline = bspline_2d - - # can not set control points with other shape than previous - with pytest.raises(ValueError): - a.control_points = [[1, 2, 3, 2, 3], [1, 2, 3, 2, 3], [1, 2, 3, 2, 3]] - - a.control_points = bspline_2d.control_points * 2 - - assert np.allclose(a.control_points, bspline_2d.control_points) - - -def test_check_dimensions(faces_quad, bspline_para_1_dim_2): - a = FFD() - - # spline dimensions do not match (will be checked at mesh setter) - a.spline = bspline_para_1_dim_2 - - # spline and mesh have different dimensions - with pytest.raises(RuntimeError) as err: - a.mesh = faces_quad - - assert "geometric" in str(err) # spline para to dim miss match - assert "mesh" in str(err) # mesh dimension to spline miss match - - -def test_mesh(bspline_2d): - a = FFD() - - a.spline = bspline_2d - - with pytest.raises(RuntimeError): - a.mesh - - -@pytest.mark.parametrize( - "spline_str, value_error, notimplemented_error", - ( - (None, True, False), - ("bspline_para_1_dim_2", False, False), - ("bezier_2d", False, True), - ), -) -def test_elevate_degree( - spline_str, value_error, notimplemented_error, request -): - a = FFD() - - if spline_str: - spline = request.getfixturevalue(spline_str) - a.spline = spline.copy() - if value_error: - with pytest.raises(ValueError): - a.elevate_degree() - elif notimplemented_error: - with pytest.raises(NotImplementedError): - a.elevate_degree() - else: - spline.elevate_degree(0) - a.elevate_degree(0) - assert np.allclose(spline.degrees, a.spline.degrees) - assert np.allclose(spline.knot_vectors, a.spline.knot_vectors) - assert np.allclose(spline.control_points, a.spline.control_points) - - -@pytest.mark.parametrize( - "spline_str, value_error, notimplemented_error", - ( # only one error type can be checked - (None, True, False), - ("bspline_para_1_dim_2", False, False), - ("bezier_2d", False, True), - ), -) -def test_insert_knots(spline_str, value_error, notimplemented_error, request): - a = FFD() - - if spline_str: - spline = request.getfixturevalue(spline_str) - a.spline = spline.copy() - if value_error: - with pytest.raises(ValueError): - a.insert_knots(0, [0.5]) - elif notimplemented_error: - with pytest.raises(NotImplementedError): - a.insert_knots(0, [0.5]) - else: - for current in [spline, a]: - current.insert_knots(0, [0.5]) - assert np.allclose(spline.degrees, a.spline.degrees) - assert np.allclose(spline.knot_vectors, a.spline.knot_vectors) - assert np.allclose(spline.control_points, a.spline.control_points) - - -@pytest.mark.parametrize( - "spline_str, value_error, notimplemented_error", - ( # only one error type can be checked - (None, True, False), - ("bspline_para_1_dim_2", False, False), - ("bezier_2d", False, True), - ), -) -def test_remove_knots(spline_str, value_error, notimplemented_error, request): - a = FFD() - - if spline_str: - spline = request.getfixturevalue(spline_str) - a.spline = spline.copy() - if value_error: - with pytest.raises(ValueError): - a.remove_knots(0, [0.5]) - elif notimplemented_error: - with pytest.raises(NotImplementedError): - a.remove_knots(0, [0.5]) - else: - for current in [spline, a]: - current.insert_knots(0, [0.5]) - for current in [spline, a]: - current.remove_knots(0, [0.5]) - assert np.allclose(spline.degrees, a.spline.degrees) - assert np.allclose(spline.knot_vectors, a.spline.knot_vectors) - assert np.allclose(spline.control_points, a.spline.control_points) - - -@pytest.mark.parametrize( - "spline_str, value_error, notimplemented_error", - ( # only one error type can be checked - (None, True, False), - ("bspline_para_1_dim_2", False, False), - ("bezier_2d", False, True), - ), -) -def test_reduce_degree(spline_str, value_error, notimplemented_error, request): - a = FFD() - if spline_str: - spline = request.getfixturevalue(spline_str) - a.spline = spline.copy() - if value_error: - with pytest.raises(ValueError): - a.reduce_degree(0) - elif notimplemented_error: - with pytest.raises(NotImplementedError): - a.reduce_degree(0) - else: - for current in [spline, a]: - current.reduce_degree(0) - assert np.allclose(spline.degrees, a.spline.degrees) - assert np.allclose(spline.knot_vectors, a.spline.knot_vectors) - assert np.allclose(spline.control_points, a.spline.control_points) From e00868857426319047a2ab571900198741423042 Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Thu, 22 Jun 2023 15:24:44 +0200 Subject: [PATCH 06/11] remove typing --- gustaf/_typing.py | 30 ------------------------------ gustaf/io/default.py | 4 +--- gustaf/io/meshio.py | 6 ++---- 3 files changed, 3 insertions(+), 37 deletions(-) delete mode 100644 gustaf/_typing.py diff --git a/gustaf/_typing.py b/gustaf/_typing.py deleted file mode 100644 index 55c64c3a4..000000000 --- a/gustaf/_typing.py +++ /dev/null @@ -1,30 +0,0 @@ -import sys -from typing import Any, Union - -from gustaf.edges import Edges -from gustaf.faces import Faces -from gustaf.vertices import Vertices -from gustaf.volumes import Volumes - -MESH_TYPES = Union[Vertices, Edges, Faces, Volumes] - - -def is_mesh(candidate: Any) -> bool: - """This function checks if the candidate is a mesh. - - The complicated version check is so that even python 3.6 is supported. - - Parameters - ----------- - candidate: Any - object to check for being a mesh. - - Returns - -------- - is_mesh: bool - Is the given object a mesh. - """ - if float(sys.version.split(".")[1]) > 6: - return isinstance(candidate, MESH_TYPES.__args__) - else: - return issubclass(type(candidate), MESH_TYPES) diff --git a/gustaf/io/default.py b/gustaf/io/default.py index 1ba8e50c9..b15b16952 100644 --- a/gustaf/io/default.py +++ b/gustaf/io/default.py @@ -1,11 +1,9 @@ import pathlib -from typing import Union -from gustaf._typing import MESH_TYPES from gustaf.io import meshio, mfem, mixd -def load(fname: Union[str, pathlib.Path]) -> MESH_TYPES: +def load(fname): """Load function for all supported file formats. This function tries to guess the correct io module for the given file. diff --git a/gustaf/io/meshio.py b/gustaf/io/meshio.py index 26f76bf4b..d20f73b15 100644 --- a/gustaf/io/meshio.py +++ b/gustaf/io/meshio.py @@ -4,11 +4,9 @@ gustaf. """ import pathlib -from typing import Union import numpy as np -from gustaf._typing import MESH_TYPES from gustaf.faces import Faces from gustaf.helpers.raise_if import ModuleImportRaiser from gustaf.volumes import Volumes @@ -20,7 +18,7 @@ # from meshio import Mesh as MeshioMesh -def load(fname: Union[str, pathlib.Path]) -> MESH_TYPES: +def load(fname): """Load mesh in meshio format. Loads vertices and their connectivity. Currently cannot process boundary. @@ -74,7 +72,7 @@ def load(fname: Union[str, pathlib.Path]) -> MESH_TYPES: return mesh -def export(mesh: MESH_TYPES, fname: Union[str, pathlib.Path]): +def export(mesh, fname): """Currently not implemented function. Parameters From b6cd47a0a610edf66f56b2f49ec7aec014be372e Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Thu, 22 Jun 2023 16:54:57 +0200 Subject: [PATCH 07/11] remove spline related examples --- examples/create_basic_splines.py | 43 ----- examples/show_ffd.py | 135 ---------------- examples/show_gus.py | 129 --------------- examples/show_microstructures.py | 201 ----------------------- examples/show_splinedata.py | 269 ------------------------------- examples/show_splines.py | 104 ------------ examples/show_subspline.py | 21 --- examples/show_tataratat.py | 203 ----------------------- 8 files changed, 1105 deletions(-) delete mode 100644 examples/create_basic_splines.py delete mode 100644 examples/show_ffd.py delete mode 100644 examples/show_gus.py delete mode 100644 examples/show_microstructures.py delete mode 100644 examples/show_splinedata.py delete mode 100644 examples/show_splines.py delete mode 100644 examples/show_subspline.py delete mode 100644 examples/show_tataratat.py diff --git a/examples/create_basic_splines.py b/examples/create_basic_splines.py deleted file mode 100644 index 80a0af7f9..000000000 --- a/examples/create_basic_splines.py +++ /dev/null @@ -1,43 +0,0 @@ -import numpy as np - -import gustaf as gus - -if __name__ == "__main__": - line = gus.spline.create.line(np.array([[0, 0, 0], [2, 5, 0], [4, 4, 2]])) - rect = gus.spline.create.box(5, 3) - box = gus.spline.create.box(3, 2, 4) - pyramid = gus.spline.create.pyramid(1, 1, 2) - - gus.show.show_vedo( - ["Line", line], - ["Rectangle", rect], - ["Box", box], - ["Pyramid", pyramid], - title="Rectangular objects", - resolution=50, - ) - - circ = gus.spline.create.circle(5) - disk1 = gus.spline.create.disk(3, angle=100, n_knot_spans=4) - disk2 = gus.spline.create.disk( - 5, inner_radius=1.0, angle=360, n_knot_spans=10 - ) - - gus.show.show_vedo( - ["Line Circle", circ], ["Disk section", disk1], ["Disk", disk2] - ) - - cone = gus.spline.create.cone(5, 10, angle=180) - gus.show.show_vedo(["Cone", cone]) - - torus = gus.spline.create.torus(10, 2) - torus2 = gus.spline.create.torus( - 5, 2, section_inner_radius=0.5, torus_angle=100, section_angle=210 - ) - - gus.show.show_vedo( - ["Torus", torus], ["Torus section", torus2], resolution=50 - ) - - sphere = gus.spline.create.sphere(3) - gus.show.show_vedo(["Sphere", sphere], resolution=50) diff --git a/examples/show_ffd.py b/examples/show_ffd.py deleted file mode 100644 index d46499b34..000000000 --- a/examples/show_ffd.py +++ /dev/null @@ -1,135 +0,0 @@ -import gustaf as gus - -if __name__ == "__main__": - gus.utils.log.configure(debug=True) - - # Lets start with a 2D example - gus.utils.log.info( - "Geometry files not accessible creating exemplary mesh." - ) - temp_spline = gus.BSpline( - [2, 2], - [[0, 0, 0, 0.33, 0.66, 1, 1, 1], [0, 0, 0, 1, 1, 1]], - [ - [0, 0], - [0.25, 0], - [0.5, 0.05], - [0.75, 0.1], - [1, 0.1], - [0, 0.2], - [0.25, 0.2], - [0.5, 0.2], - [0.75, 0.2], - [1, 0.2], - [0, 0.4], - [0.25, 0.4], - [0.5, 0.35], - [0.75, 0.3], - [1, 0.3], - ], - ) - d2_resolution = [50, 25] - sampled_2d = temp_spline.sample(d2_resolution) - connec_2d = gus.utils.connec.make_quad_faces(d2_resolution) - mesh_2d = gus.Faces(sampled_2d, connec_2d) - - spline_2d = gus.BSpline( - [2, 2], - [[0, 0, 0, 4, 4, 4], [2, 2, 2, 3, 3, 3]], - [ - [0, 0], - [0.5, -0.2], - [1, 0], - [0, 0.2], - [0.5, 0.2], - [1, 0.2], - [0, 0.4], - [0.5, 0.4], - [1, 0.4], - ], - ) - - ffd_2d = gus.FFD(mesh_2d, spline_2d) - ffd_2d.show(title="2D FFD - BSpline") - - # Now 3D - v_res = [5, 6, 7] - vertices = gus.create.vertices.raster( - bounds=[[-1, 0, 5], [4, 1, 10]], resolutions=v_res - ) - connections = gus.utils.connec.make_hexa_volumes(v_res) - volume_3d = gus.Volumes(vertices.vertices, connections) - - # create control point grid - control_points = list() - for i in range(3): - for j in range(3): - for k in range(3): - control_points.append([i * 0.5, j * 0.5, k * 0.5]) - - # change a control point so that there is a deformation - control_points[22] = [1.5, 0.5, 0.5] - control_points[2] = [0.25, 0.25, 0.75] - - spline_3d = gus.BSpline( - [2, 2, 2], - [ - [0, 0, 0, 4, 4, 4], - [0, 0, 0, 1, 1, 1], - [5, 5, 5, 10, 10, 10], - ], - control_points, - ) - - ffd_3d = gus.FFD(volume_3d, spline_3d) - - ffd_3d.show(title="3D FFD - BSpline") - - # RationalBezier in 2D - - spline_2d_bez = gus.Bezier( - [2, 2], - [ - [0, 0], - [0.5, -0.2], - [1, 0], - [0, 0.2], - [0.5, 0.2], - [1, 0.2], - [0, 0.4], - [0.5, 0.4], - [1, 0.4], - ], - ) - - spline_2d_bez = gus.FFD(mesh_2d, spline_2d) - spline_2d_bez.show(title="Bezier Spline based FFD") - - # Only provide mesh - ffd_with_out_spline = gus.FFD(volume_3d) - - ffd_with_out_spline.show( - title="Spline without defined spline." - " Projected into dimension of the given " - "mesh." - ) - - # Only provide mesh after initialization - # does same as above - ffd_with_out_spline_and_mesh = gus.FFD() - ffd_with_out_spline_and_mesh.mesh = volume_3d - ffd_with_out_spline_and_mesh.show( - title="Spline without defined spline." - " Projected into dimension of the given " - "mesh." - ) - - # Only provide spline and then mesh after initialization - ffd_with_out_mesh = gus.FFD() - ffd_with_out_mesh.spline = spline_3d - ffd_with_out_mesh.mesh = volume_3d - ffd_with_out_mesh.show( - title="Spline without defined mesh at " - "initialization. Projected into dimension " - "of the given mesh." - ) diff --git a/examples/show_gus.py b/examples/show_gus.py deleted file mode 100644 index 146f9a06d..000000000 --- a/examples/show_gus.py +++ /dev/null @@ -1,129 +0,0 @@ -"""Create gus""" -import gustaf as gus - -WITH_PUPILS = False -WITH_DARK_BACKGROUND = False -DARK_WITH_WHITE_OVAL_BACKGROUND = False -WITH_ALPHA_BACKGROUND = False - -if __name__ == "__main__": - if WITH_ALPHA_BACKGROUND: - import vedo - - vedo.settings.screenshot_transparent_background = 1 - - l_eye = gus.spline.create.disk(0.15) - l_eye.cps += [0.6, 0.2] - l_eye.show_options["c"] = "black" - - r_eye = gus.spline.create.disk(0.15) - r_eye.cps += [-0.6, 0.2] - r_eye.show_options["c"] = "black" - - x_offset = 0.59 - y_offset = 0.16 - - l_pupil = gus.spline.create.disk(0.05) - l_pupil.cps += [x_offset, y_offset] - l_pupil.show_options["c"] = "white" - - r_pupil = gus.spline.create.disk(0.05) - r_pupil.cps += [-x_offset, y_offset] - r_pupil.show_options["c"] = "white" - - upper_lip = gus.Bezier( - [5, 1], - [ - [-0.8, -0.1], - [-0.2, -0.4], - [-0.5, 0.5], - [0.5, 0.5], - [0.2, -0.4], - [0.8, -0.1], - [-0.75, -0.15], - [-0.2, -0.55], - [-0.5, 0.05], - [0.5, 0.05], - [0.2, -0.55], - [0.75, -0.15], - ], - ) - upper_lip.show_options["c"] = "orange7" - - inner_mouth = gus.Bezier( - [5, 1], - [ - *upper_lip.cps[6:], - [-0.75, -0.15], - [-0.6, -0.325], - [-0.4, -0.525], - [0.4, -0.525], - [0.6, -0.325], - [0.75, -0.15], - ], - ) - inner_mouth.show_options["c"] = "orange5" - - lower_lip = gus.Bezier( - [5, 1], - [ - *inner_mouth.cps[6:], - [-0.8, -0.1], - [-0.6, -0.4], - [-0.4, -0.6], - [0.4, -0.6], - [0.6, -0.4], - [0.8, -0.1], - ], - ) - lower_lip.show_options["c"] = "orange7" - - item_to_show = [l_eye, r_eye, upper_lip, inner_mouth, lower_lip] - if WITH_PUPILS: - item_to_show += [l_pupil, r_pupil] - - plt = gus.show( - item_to_show, - control_points=False, - knots=False, - lighting="off", - background="white", - close=False, - ) - - if WITH_DARK_BACKGROUND: - background = [] - - if DARK_WITH_WHITE_OVAL_BACKGROUND: - height = 0.6 - width = 0 - - x_shift = 0.3 - y_shift = -0.02 - - background = [ - gus.spline.create.disk(height), - gus.spline.create.disk(height), - gus.spline.create.box(width + 2 * x_shift, 2 * height), - ] - - background[0].cps += [-x_shift, y_shift] - background[1].cps += [width + x_shift, y_shift] - background[2].cps += [-x_shift, -height + y_shift] - for elem in background: - elem.show_options["c"] = "white" - elem.show_options["alpha"] = 0.25 - else: - l_eye.show_options["c"] = "white" - r_eye.show_options["c"] = "white" - l_pupil.show_options["c"] = "black" - r_pupil.show_options["c"] = "black" - - plt = gus.show( - [*background, *item_to_show], - control_points=False, - knots=False, - lighting="off", - background="black", - close=False, - ) diff --git a/examples/show_microstructures.py b/examples/show_microstructures.py deleted file mode 100644 index 667420325..000000000 --- a/examples/show_microstructures.py +++ /dev/null @@ -1,201 +0,0 @@ -import numpy as np -from vedo import Mesh, colors - -import gustaf as gus - -# First Test -generator = gus.spline.microstructure.Microstructure() -generator.deformation_function = gus.Bezier( - degrees=[2, 1], - control_points=[[0, 0], [1, 0], [2, -1], [-1, 1], [1, 1], [3, 2]], -) -generator.microtile = [ - gus.Bezier( - degrees=[3], control_points=[[0, 0.5], [0.5, 1], [0.5, 0], [1, 0.5]] - ), - gus.Bezier( - degrees=[4], - control_points=[ - [0.5, 0], - [0.75, 0.5], - [0.8, 0.8], - [0.25, 0.5], - [0.5, 1], - ], - ), -] -generator.tiling = [8, 8] -generator.show( - knots=False, control_points=False, title="2D Lattice Microstructure" -) - -# Second test - - -def parametrization_function(x): - return tuple( - [0.3 - 0.4 * np.maximum(abs(0.5 - x[:, 0]), abs(0.5 - x[:, 1]))] - ) - - -generator = gus.spline.microstructure.Microstructure() -generator.deformation_function = gus.Bezier( - degrees=[1, 1], control_points=[[0, 0], [1, 0], [0, 1], [1, 1]] -) -generator.microtile = gus.spline.microstructure.tiles.CrossTile2D() -generator.tiling = [5, 5] -generator.parametrization_function = parametrization_function -ms = generator.create(closing_face="x", center_expansion=1.3) -generator.show( - use_saved=True, - knots=True, - control_points=False, - title="2D Crosstile Parametrized Microstructure", -) - -# Third test -generator = gus.spline.microstructure.Microstructure() -generator.deformation_function = gus.Bezier( - degrees=[1, 1], control_points=[[0, 0], [1, 0], [0, 1], [1, 1]] -).create.extruded(extrusion_vector=[0, 0, 1]) -generator.microtile = [ - gus.Bezier( - degrees=[3], - control_points=[ - [0, 0.5, 0.5], - [0.5, 1, 0.5], - [0.5, 0, 0.5], - [1, 0.5, 0.5], - ], - ), - gus.Bezier( - degrees=[3], - control_points=[ - [0.5, 0.5, 0.0], - [0.5, 0, 0.5], - [0.5, 1.0, 0.5], - [0.5, 0.5, 1.0], - ], - ), - gus.Bezier( - degrees=[3], - control_points=[ - [0.5, 0, 0.5], - [1, 0.5, 0.5], - [0, 0.5, 0.5], - [0.5, 1, 0.5], - ], - ), -] -generator.tiling = [1, 2, 3] -generator.show( - knots=False, control_points=False, title="3D Lattice Microstructure" -) - -# Fourth test -generator = gus.spline.microstructure.microstructure.Microstructure() -generator.deformation_function = gus.Bezier( - degrees=[1, 1], control_points=[[0, 0], [1, 0], [0, 1], [1, 1]] -).create.extruded(extrusion_vector=[0, 0, 1]) -generator.microtile = gus.spline.microstructure.tiles.CrossTile3D() -generator.tiling = [2, 2, 3] -generator.show( - control_points=False, resolutions=2, title="3D Crosstile Microstructure" -) - -# Fifth test -# Non-uniform tiling -generator = gus.spline.microstructure.microstructure.Microstructure() -generator.deformation_function = gus.BSpline( - degrees=[2, 1], - control_points=[ - [0, 0], - [0.1, 0], - [0.2, 0], - [1, 0], - [0, 1], - [0.1, 1], - [0.2, 2], - [1, 3], - ], - knot_vectors=[[0, 0, 0, 0.15, 1, 1, 1], [0, 0, 1, 1]], -) -generator.microtile = [ - gus.Bezier( - degrees=[3], control_points=[[0, 0.5], [0.5, 1], [0.5, 0], [1, 0.5]] - ), - gus.Bezier( - degrees=[3], control_points=[[0.5, 0], [1, 0.5], [0, 0.5], [0.5, 1]] - ), -] -generator.tiling = [5, 1] -generator.show( - knot_span_wise=False, - control_points=False, - resolutions=20, - title="2D Lattice with global tiling", -) - -# Sixth test -# A Parametrized microstructure and its respective inverse structure - - -def foo(x): - """ - Parametrization Function (determines thickness) - """ - return tuple([(x[:, 0]) * 0.05 + (x[:, 1]) * 0.05 + (x[:, 2]) * 0.1 + 0.1]) - - -generator = gus.spline.microstructure.Microstructure() -generator.deformation_function = gus.Bezier( - degrees=[1, 1], control_points=[[0, 0], [1, 0], [0, 1], [1, 1]] -).create.extruded(extrusion_vector=[0, 0, 1]) -generator.microtile = gus.spline.microstructure.tiles.InverseCrossTile3D() -generator.tiling = [3, 3, 5] -generator.parametrization_function = foo - -inverse_microstructure = generator.create( - closing_face="z", seperator_distance=0.4, center_expansion=1.3 -) - -# Plot the results -_, showables_inverse = generator.show( - closing_face="z", - seperator_distance=0.4, - center_expansion=1.3, - title="Parametrized Inverse Microstructure", - control_points=False, - knots=True, - return_showable_list=True, - resolutions=5, -) - -# Corresponding Structure -generator.microtile = gus.spline.microstructure.tiles.CrossTile3D() -microstructure = generator.create( - closing_face="z", seperator_distance=0.4, center_expansion=1.3 -) -_, showables = generator.show( - closing_face="z", - center_expansion=1.3, - title="Parametrized Microstructure", - control_points=False, - knots=True, - return_showable_list=True, - resolutions=5, -) - -# Change the color of the inverse mesh to some blue shade -composed_structure = [] -for item in showables: - if isinstance(item, Mesh): - composed_structure.append(item) -for item in showables_inverse: - if isinstance(item, Mesh): - item.c(colors.getColor(rgb=(0, 102, 153))) - composed_structure.append(item) - -gus.show.show_vedo( - composed_structure, title="Parametrized Microstructure and its inverse" -) diff --git a/examples/show_splinedata.py b/examples/show_splinedata.py deleted file mode 100644 index 083a42518..000000000 --- a/examples/show_splinedata.py +++ /dev/null @@ -1,269 +0,0 @@ -import numpy as np - -import gustaf as gus - - -def bspline2p3d(): - """ - Creates new bspline of of parametric dimension 2, physical dimension 3 - """ - ds2 = [2, 2] - - # define knot vectors - kvs2 = [ - [0.0, 0.0, 0.0, 0.5, 1.0, 1.0, 1.0], - [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], - ] - - # define control points - cps2 = np.array( - [ - [0, 0, 0], - [0, 1, 0], - [1, 1.5, 0], - [3, 1.5, 0], - [-1, 0, 0], - [-1, 2, 0], - [1, 4, 0], - [3, 4, 0], - [-2, 0, 0], - [-2, 2, 0], - [1, 5, 0], - [3, 5, 2], - ] - ) - - # init bspline - return gus.BSpline( - degrees=ds2, - knot_vectors=kvs2, - control_points=cps2, - ) - - -if __name__ == "__main__": - # turn on debug logs - # gus.utils.log.configure(debug=True) - - # define spline_data - # 1. see coordinates's norm - b = bspline2p3d() - b.spline_data["me"] = b - b.show_options["data_name"] = "me" - gus.show(["1. Show norm of coordinates.", b]) - - # 1.1 default scalarbar - b = bspline2p3d() - b.spline_data["me"] = b - b.show_options["data_name"] = "me" - b.show_options["scalarbar"] = True - gus.show(["1.1. Show 1. plus scalarbar", b]) - - # 2. see coordinate's norm and as arrow - b = bspline2p3d() - b.spline_data["me"] = b - b.show_options["data_name"] = "me" - b.show_options["arrow_data"] = "me" - gus.show( - ["2. Show coordinate norm as scalar field and coordinate as arrows", b] - ) - - # 3. see coordinates norm and as arrows only on specified places - b = bspline2p3d() - b.spline_data["me"] = b - b.show_options["data_name"] = "me" - b.show_options["arrow_data"] = "me" - b.show_options["arrow_data_on"] = np.random.random((100, 2)) # para_coords - gus.show( - ["3. Show coordinates norm and as arrows on 100 random points.", b] - ) - - # 4. see 3. with parametric_view - b = bspline2p3d() - b.spline_data["me"] = b - b.show_options["data_name"] = "me" - b.show_options["arrow_data"] = "me" - b.show_options["arrow_data_on"] = np.random.random((100, 2)) # para_coords - b.show_options["scalarbar"] = True - gus.show( - ["4. Show 3. and 3. in parametric space view with scalarbar.", b], - b.create.parametric_view(), - ) - # b.show(parametric_space=True) # does the same - - # 5. plot function that supports both resolutions and on - def plot_func(data, resolutions=None, on=None): - """ - callback to evaluate derivatives - """ - if resolutions is not None: - q = gus.create.vertices.raster( - [[0, 0], [1, 1]], resolutions - ).vertices - return data.derivative(q, [0, 1]) - elif on is not None: - return data.derivative(on, [0, 1]) - - # use adaptor to defie a data - plot_func_data = gus.spline.SplineDataAdaptor(b, function=plot_func) - # the rest is the same - b = bspline2p3d() - b.spline_data["der01"] = plot_func_data - b.show_options["arrow_data"] = "der01" - gus.show( - [ - "5. Show partial derivative of seconda parametric dimension.\n" - "This uses a callback function and SplineDataAdaptor.\n" - "Also achievable with derivative spline.", - b, - ] - ) - - # remove on to sample same way as spline. - # however, gold - b = bspline2p3d() - b.spline_data["der01"] = plot_func_data - b.show_options["arrow_data"] = "der01" - b.show_options["arrow_data_on"] = np.random.random((100, 2)) # para_coords - b.show_options["arrow_data_color"] = "gold" - gus.show( - [ - "5.1. Same data as 5. However, on 100 random places with gold " - "arrows", - b, - ] - ) - - # 6. plot on predefined place - this will be only available as arrow data - locations = np.repeat(np.linspace(0, 1, 15), 2).reshape(-1, 2) - values = np.repeat(np.linspace(1, 2, 15), 3).reshape(-1, 3) + [0, 0, 2] - fixed_data = gus.spline.SplineDataAdaptor(values, locations=locations) - b = bspline2p3d() - b.spline_data["fixed"] = fixed_data - b.show_options["arrow_data"] = "fixed" - gus.show( - [ - "6. Show arbitrary array data on predefined locations using " - "SplineDataAdaptor.", - b, - ] - ) - - # fixed location data can't be shown in other requested locations. - # followings won't work - # b.show_options["arrow_data_on"] = locations[:5] - # b.show() - - # 7. plot any data with a function - # some manually defined deformed spline - deformed = bspline2p3d() # minimal copy - properties and cached data only - deformed.cps[11, -1] -= 4 - deformed.cps *= [5.5, 5.5, 5.5] - deformed.cps += [-5, 0, 8] - deformed.cps[0, [0, -1]] += 4 - deformed.show_options["c"] = "hotpink" - - # define callback - def func(self_and_deformed, resolutions=None, on=None): - """ - callback to sample displacements. - """ - # unpack data - self, deformed = self_and_deformed - if resolutions is not None: - return deformed.sample(resolutions) - self.sample(resolutions) - elif on is not None: - return deformed.evaluate(on) - self.evaluate(on) - - # use adaptor - data is used as the first arg for callback - deformed_data = gus.spline.SplineDataAdaptor( - data=(b, deformed), - function=func, - ) - b.spline_data["deformed"] = deformed_data - b.show_options["arrow_data"] = "deformed" - # arrows are always automatically scaled. for this one, let's not - b.show_options["arrow_data_scale"] = 1 - b.show_options["arrow_data_on"] = locations - # let's see in parametric space - p_view = b.create.parametric_view() # shallow copies data and options - p_view.show_options.pop("arrow_data_scale") # we want automatic scaling - p_view.show_options["arrow_data_color"] = "gold" - p_view.show_options["data_name"] = "deformed" - # plot side by side - gus.show( - [ - "7. Original spline, deformed spline\n" - "and pointwise correspondence on selected locations.", - b, - deformed, - ], - [ - "Parametric view of displacements.\n" - "Arrows are directly transfered with automatic rescaling.\n" - "(no inverse mapping)", - p_view, - ], - ) - # say, deformed has changed again - plots should adapt automatically - deformed.cps[:, [1, 2]] = deformed.cps[:, [2, 1]] - gus.show( - [ - "7.1. Same setup as 7. with inplace changes in deformed spline.", - b, - deformed, - ], - [ - "Parametric view of displacements.\n" - "Arrows are directly transfered with automatic rescaling.\n" - "(no inverse mapping)", - p_view, - ], - ) - - # 8. fixed location data that uses callback - # predefind some locations - bottom = gus.spline.create.arc(radius=0.5, angle=-180) # zero centered - bottom.cps += [0.5, 0.55] - circle1 = gus.spline.create.circle(radius=0.1) - circle2 = circle1.copy() - circle1.cps += [0.25, 0.75] - circle2.cps += [0.75, 0.75] - n = 30 - locations = np.vstack( - (bottom.sample(n), circle1.sample(n), circle2.sample(n)) - ) - - # callback - def heights(bot_c1_c2_n): - bot, c1, c2, n_repeat = bot_c1_c2_n - - values = np.repeat( - [ - [0, 0, bot], - [0, 0, c1], - [0, 0, c2], - ], - n_repeat, - axis=0, - ) - return values - - nice_data = gus.spline.SplineDataAdaptor( - data=(4, 2, 1, n), - locations=locations, - function=heights, - ) - disc = gus.spline.create.disk(2, angle=123) - disc.normalize_knot_vectors() - disc.spline_data["nice"] = nice_data - disc.show_options["arrow_data"] = "nice" - disc.show_options["arrow_data_color"] = "jet" - gus.show( - [ - "8. Showing arbitrary data with callback.\n" - "Any array-like multi-dim data can be plotted as long as\n" - "len(data) == len(locations) holds.", - disc, - ] - ) diff --git a/examples/show_splines.py b/examples/show_splines.py deleted file mode 100644 index bf7f7e352..000000000 --- a/examples/show_splines.py +++ /dev/null @@ -1,104 +0,0 @@ -import numpy as np - -import gustaf as gus - -if __name__ == "__main__": - # turn on debug logs - gus.utils.log.configure(debug=True) - - # curve - # define degrees - ds1 = [1] - - # define knot vectors - kvs1 = [[0.0, 0.0, 1.0, 1.0]] - - # define control points - cps1 = np.array( - [ - [0.0, 0.0], - [1.0, 3.0], - ] - ) - - # init bspline - bspline1 = gus.BSpline( - degrees=ds1, - knot_vectors=kvs1, - control_points=cps1, - ) - - # show - bspline1.show() - - # surface - # define degrees - ds2 = [2, 2] - - # define knot vectors - kvs2 = [ - [0.0, 0.0, 0.0, 0.5, 1.0, 1.0, 1.0], - [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], - ] - - # define control points - cps2 = np.array( - [ - [0, 0, 0], - [0, 1, 0], - [1, 1.5, 0], - [3, 1.5, 0], - [-1, 0, 0], - [-1, 2, 0], - [1, 4, 0], - [3, 4, 0], - [-2, 0, 0], - [-2, 2, 0], - [1, 5, 0], - [3, 5, 2], - ] - ) - - # init bspline - bspline2 = gus.BSpline( - degrees=ds2, - knot_vectors=kvs2, - control_points=cps2, - ) - - # show - bspline2.show() - - # volume - # define degrees - ds3 = [1, 1, 1] - - # define knot vectors - kvs3 = [ - [0.0, 0.0, 1.0, 1.0], - [0.0, 0.0, 1.0, 1.0], - [0.0, 0.0, 1.0, 1.0], - ] - - # define control points - cps3 = np.array( - [ - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [1.0, 1.0, 0.0], - [0.0, -1.0, 1.0], - [1.0, 0.0, 1.0], - [-1.0, 1.0, 2.0], - [2.0, 2.0, 2.0], - ] - ) - - # init bspline - bspline3 = gus.BSpline( - degrees=ds3, - knot_vectors=kvs3, - control_points=cps3, - ) - - bspline3.show() diff --git a/examples/show_subspline.py b/examples/show_subspline.py deleted file mode 100644 index ce3921518..000000000 --- a/examples/show_subspline.py +++ /dev/null @@ -1,21 +0,0 @@ -import gustaf as gus - -if __name__ == "__main__": - s_nurbs = gus.Bezier( - degrees=[2, 1], - control_points=[ - [0, 0], - [0.5, -0.5], - [1, 0], - [0, 1], - [0.5, 0.5], - [1, 1], - ], - ).nurbs - s_nurbs.insert_knots(0, [0.5]) - line = s_nurbs.extract.spline({0: [0.4, 0.8], 1: 0.7}) - gus.show( - ["Source spline", s_nurbs], - ["extract.spline({0: [0.4, 0.8], 1: 0.7})", line, s_nurbs], - ["Extracted spline", line], - ) diff --git a/examples/show_tataratat.py b/examples/show_tataratat.py deleted file mode 100644 index e80fcc7a8..000000000 --- a/examples/show_tataratat.py +++ /dev/null @@ -1,203 +0,0 @@ -import argparse - -import numpy as np -import vedo - -import gustaf as gus - -# this will cause a deprecated warning for newer version of vedo. -# for backward compatibility, we keep this one. -vedo.settings.screenshot_transparent_background = 1 - -if __name__ == "__main__": - arg_parser = argparse.ArgumentParser( - description=( - "Creates a fanfare. Default option corresponds to " - "the tataratat logo." - ) - ) - arg_parser.add_argument( - "--r_mouth", - dest="r_mouth", - default=0.2, - help="Mouth radius for mouth piece.", - ) - arg_parser.add_argument( - "--r_shaft", - dest="r_shaft", - default=0.1, - help="Shaft radius for mouth piece.", - ) - arg_parser.add_argument( - "--r_rounding", - dest="r_rounding", - default=0.05, - help="Radius offset for mouth piece rounding.", - ) - arg_parser.add_argument( - "--l_mouth", - dest="l_mouth", - default=0.4, - help="Length of the mouth piece.", - ) - arg_parser.add_argument( - "--l_cone", - dest="l_cone", - default=1.2, - help="Length of the cone.", - ) - arg_parser.add_argument( - "--r_cone", - dest="r_cone", - default=0.8, - help="Radius of the cone.", - ) - arg_parser.add_argument( - "--l_shaft", - dest="l_shaft", - default=4.0, - help="Length of the shaft.", - ) - arg_parser.add_argument( - "--r_curve", - dest="r_curve", - default=0.3, - help="Radius of connecting curves.", - ) - args = arg_parser.parse_args() - - # Let's draw a fanfare - fanfare = [] - - # cast all args to float - # Mouthpiece - r_mouth = float(args.r_mouth) - r_shaft = float(args.r_shaft) - r_rounding = float(args.r_rounding) - l_mouth = float(args.l_mouth) - # Cone - l_cone = float(args.l_cone) - r_cone = float(args.r_cone) - # Shaft - l_shaft = float(args.l_shaft) - curved_radius = float(args.r_curve) - - # Create fanfare part by part - mouth_piece = gus.BSpline( - knot_vectors=[[0, 0, 0, 0.25, 0.5, 0.5, 0.75, 1, 1, 1]], - degrees=[2], - control_points=[ - [l_mouth, r_mouth - r_rounding], - [l_mouth + r_rounding, r_mouth - r_rounding], - [l_mouth + r_rounding, r_mouth], - [l_mouth, r_mouth], - [0.5 * l_mouth, r_mouth], - [0.5 * l_mouth, r_shaft], - [0.0, r_shaft], - ], - ).nurbs.create.revolved( - axis=[1, 0, 0], - center=[0, 0, 0], - angle=360, - degree=True, - n_knot_spans=4, - ) - - isqrt2 = 2 ** (-0.5) - cross_section = gus.NURBS( - degrees=[2], - knot_vectors=[[0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1, 1, 1]], - control_points=( - np.array( - [ - [0.0, 0, 0], - [0.0, 0.5, -0.5], - [0.0, 1, 0], - [0.0, 1 + 0.5, 0.5], - [0.0, 1, 1], - [0.0, 0.5, 1 + 0.5], - [0.0, 0, 1], - [0.0, -0.5, 0.5], - [0, 0, 0], - ] - ) - + np.array([0, -0.5, -0.5]) - ) - * (r_shaft * 2 * isqrt2), - weights=[1, isqrt2, 1, isqrt2, 1, isqrt2, 1, isqrt2, 1], - ) - - shaft = cross_section.create.extruded(extrusion_vector=[-l_shaft, 0, 0]) - - lower_shaft = shaft.copy() - lower_shaft.control_points += [0, -2 * curved_radius, 0] - upper_shaft = shaft.copy() - upper_shaft.control_points += [ - 0, - -2 * curved_radius + 2 * isqrt2 * curved_radius, - -2 * isqrt2 * curved_radius, - ] - - cross_section.control_points += [-l_shaft, 0, 0] - - curved_piece = cross_section.nurbs.create.revolved( - axis=[0.0, 0, 1], center=[-l_shaft, -curved_radius, 0], angle=180 - ) - cross_section.control_points += [l_shaft, -2 * curved_radius, 0] - second_curved_piece = cross_section.nurbs.create.revolved( - axis=[0, 1.0, 1.0], - center=[ - 0, - -2 * curved_radius + isqrt2 * curved_radius, - -isqrt2 * curved_radius, - ], - angle=180, - ) - - cone_part = gus.BSpline( - degrees=[2], - knot_vectors=[[0, 0, 0, 0.5, 1, 1, 1]], - control_points=[ - [0.0, r_shaft, 0.0], - [-0.25 * l_cone, r_shaft, 0.0], - [-l_cone, 0.5 * (r_cone + r_shaft), 0.0], - [-l_cone, r_cone, 0.0], - ], - ).nurbs.create.revolved( - axis=[1, 0, 0], - center=[0, 0, 0], - angle=360, - degree=True, - ) - cone_part.control_points += [ - -l_shaft, - -2 * curved_radius + 2 * isqrt2 * curved_radius, - -2 * isqrt2 * curved_radius, - ] - - fanfare.append(mouth_piece) - fanfare.append(curved_piece) - fanfare.append(second_curved_piece) - fanfare.append(upper_shaft) - fanfare.append(cone_part) - fanfare.append(shaft) - fanfare.append(lower_shaft) - - # show fanfare - prepare only surface and full spline components - face_showables = [] - spline_showables = [] - for f in fanfare: - showables = f.showable(resolutions=500, c=(255, 215, 0)) - faces = showables["spline"] - face_showables.append(faces) - spline_showables.extend(list(showables.values())) - - cam = dict( - pos=(-11.90193, 5.995478, 3.057389), - focalPoint=(-2.817062, -0.6220471, -0.3948362), - viewup=(0.3271027, 0.7493888, -0.5756911), - distance=11.75773, - clippingRange=(6.030973, 19.21880), - ) - - gus.show.show_vedo(face_showables, spline_showables, cam=cam) From 29a53f684f6f63c2103d18433167be8d94e46ec7 Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Fri, 23 Jun 2023 17:51:00 +0200 Subject: [PATCH 08/11] remove return_saved kwarg --- gustaf/helpers/data.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/gustaf/helpers/data.py b/gustaf/helpers/data.py index c5a3f6cc3..27495b0ec 100644 --- a/gustaf/helpers/data.py +++ b/gustaf/helpers/data.py @@ -362,14 +362,15 @@ def compute_or_return_saved(*args, **kwargs): """Check if the key should be computed,""" # extract some related info self = args[0] # the helpee itself - recompute = kwargs.get("recompute", False) - return_saved = kwargs.get("return_saved", False) - - # if return_saved, try to escape as soon as possible - if return_saved: - saved = self._computed._saved.get(func.__name__, None) - if saved is not None and not recompute: - return saved + + # explicitly settable kwargs. + # unless recompute flag is set False, + # it will always recompute and save them + # if you call the same function without kwargs + # the last one with kwargs will be returned + recompute = False + if kwargs: + recompute = kwargs.get("recompute", True) # computed arrays are called _computed. # loop over dependees and check if they are modified From 417856cac0f6276abb14a4269e0c85d3249173d7 Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Fri, 23 Jun 2023 18:12:02 +0200 Subject: [PATCH 09/11] add close_row alternative with napf>=0.0.5 plus minor fixes in unique_vertices() --- gustaf/utils/arr.py | 74 ++++++++++++++-------- gustaf/vertices.py | 6 +- setup.py | 4 +- tests/test_vertices_and_element_updates.py | 6 +- 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/gustaf/utils/arr.py b/gustaf/utils/arr.py index eac0438d9..36386adbc 100644 --- a/gustaf/utils/arr.py +++ b/gustaf/utils/arr.py @@ -124,7 +124,9 @@ def unique_rows( return unique_stuff -def close_rows(arr, tolerance=None): +def close_rows( + arr, tolerance=None, return_intersection=False, nthreads=None, **kwargs +): """Similar to unique_rows, but if data type is floats, use this one. Performs radius search using KDTree. Currently uses `scipy.spatial.cKDTree`. @@ -133,7 +135,13 @@ def close_rows(arr, tolerance=None): ----------- arr: (n, d) array-like tolerance: (float) - Defaults to None. + Defaults to None. + return_intersection: bool + Default is False. Returns intersection. For vertices with singular + points, this will take a lot of memory space. + nthreads: int + number of concurrent query. In case of napf, concurrent build as well. + Default is taken from settings.NTHREADS Returns -------- @@ -143,36 +151,52 @@ def close_rows(arr, tolerance=None): overlapping: list(list) id of neighbors within the tolerance. """ - from scipy.spatial import cKDTree as KDTree - if tolerance is None: tolerance = settings.TOLERANCE - # Build kd tree - kdt = KDTree(arr) + if nthreads is None: + nthreads = settings.NTHREADS - # Ball point query, taking tolerance as radius - neighbors = kdt.query_ball_point( - arr, - tolerance, - # workers=workers, - # return_sorted=True # new in 1.6, but default is True, so pass. - ) + try: + from napf import KDT - # inverse based on original vertices. - o_inverse = np.array( - [n[0] for n in neighbors], - dtype=settings.INT_DTYPE, - ) + kdt = KDT(arr, nthread=nthreads) - # unique of o_inverse, and inverse based on that - (_, uniq_id, inv) = np.unique( - o_inverse, - return_index=True, - return_inverse=True, - ) + # call the function that's prepared for this moment + return kdt.unique_data_and_inverse( + tolerance, True, return_intersection, nthread=nthreads + ) + + except ImportError: + from scipy.spatial import cKDTree as KDTree + + # Build kd tree + kdt = KDTree(arr) + + # Ball point query, taking tolerance as radius + neighbors = kdt.query_ball_point( + arr, + tolerance, + return_sorted=True, + ) + + # inverse based on original vertices. + o_inverse = np.array( + [n[0] for n in neighbors], + dtype=settings.INT_DTYPE, + ) + + # unique of o_inverse, and inverse based on that + (_, uniq_id, inv) = np.unique( + o_inverse, + return_index=True, + return_inverse=True, + ) + + if not return_intersection: + neighbors = [] - return (arr[uniq_id], uniq_id, inv, neighbors) + return arr[uniq_id], uniq_id, inv, neighbors def bounds(arr): diff --git a/gustaf/vertices.py b/gustaf/vertices.py index c9c9700ba..63f9a69dd 100644 --- a/gustaf/vertices.py +++ b/gustaf/vertices.py @@ -310,7 +310,7 @@ def unique_vertices(self, tolerance=None, **kwargs): tolerance = settings.TOLERANCE values, ids, inverse, intersection = utils.arr.close_rows( - self.const_vertices, tolerance=tolerance + self.const_vertices, tolerance=tolerance, **kwargs ) return helpers.data.Unique2DFloats( @@ -472,7 +472,7 @@ def remove_vertices(self, ids): return self.update_vertices(mask) - def merge_vertices(self, tolerance=None): + def merge_vertices(self, tolerance=None, **kwargs): """Based on unique vertices, merge vertices if it is mergeable. Parameters @@ -484,7 +484,7 @@ def merge_vertices(self, tolerance=None): -------- merged_self: type(self) """ - unique_vs = self.unique_vertices() + unique_vs = self.unique_vertices(tolerance, **kwargs) self._logd("number of vertices") self._logd(f" before merge: {len(self.vertices)}") diff --git a/setup.py b/setup.py index a78b814d9..e0567c53e 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,9 @@ "gustaf.helpers", ], install_requires=["numpy"], - extras_require={"all": ["vedo>=2023.4.3", "scipy", "meshio"]}, + extras_require={ + "all": ["vedo>=2023.4.3", "scipy", "meshio", "napf>=0.0.5"] + }, classifiers=[ "Development Status :: 2 - Pre-Alpha", "License :: OSI Approved :: MIT License", diff --git a/tests/test_vertices_and_element_updates.py b/tests/test_vertices_and_element_updates.py index d60178d20..a0ff0e4e5 100644 --- a/tests/test_vertices_and_element_updates.py +++ b/tests/test_vertices_and_element_updates.py @@ -85,11 +85,12 @@ def test_unique_vertices(grid, request): # check if vertices are correctly set assert len(grid.vertices) == int((n_original_vertices + n_ran) * 2) - unique_vs = grid.unique_vertices() + unique_vs = grid.unique_vertices(return_intersection=True) n_expected_unique = int(n_original_vertices + n_ran) # value check + assert np.allclose(grid.vertices[:n_expected_unique], unique_vs.values) # ids check @@ -104,7 +105,8 @@ def test_unique_vertices(grid, request): intersection_ref = [ [i, i + n_expected_unique] for i in range(n_expected_unique) ] * 2 - assert intersection_list == intersection_ref + # although these are integers, this is still very nice one-line assert + assert np.allclose(intersection_list, intersection_ref) @pytest.mark.parametrize("grid", all_grids) From 92126ec06333a81b923b58dbab3a38b6d09804b9 Mon Sep 17 00:00:00 2001 From: "clemens.fricke" Date: Mon, 26 Jun 2023 10:55:21 +0200 Subject: [PATCH 10/11] Load necessary sample files directly from github. --- examples/.gitignore | 1 + examples/load_sample_file.py | 34 ++++++++++++++++++++++++++++++++++ examples/show_gmsh.py | 22 +++++++++++----------- 3 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 examples/.gitignore create mode 100644 examples/load_sample_file.py diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 000000000..81154dd4d --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +samples diff --git a/examples/load_sample_file.py b/examples/load_sample_file.py new file mode 100644 index 000000000..806854ea1 --- /dev/null +++ b/examples/load_sample_file.py @@ -0,0 +1,34 @@ +"""Helper file for examples. Loads sample files from the tataratat/samples.""" +import pathlib + +import requests + +base_url = "https://raw.githubusercontent.com/tataratat/samples/main/" +local_path = pathlib.Path(__file__).parent / "samples/" + + +def load_sample_file(filename: str, force_reload: bool = False) -> bool: + """Loads a sample file from the tataratat/samples repository. + + Args: + filename (str): Path of the file to load. + force_reload (bool, optional): Loads the file even if it already + exists. Defaults to False. + + Returns: + bool: File could be loaded. + """ + local_file = local_path / filename + if local_file.exists() and not force_reload: + return True + pathlib.Path.mkdir(local_file.parent, parents=True, exist_ok=True) + + url = base_url + filename + response = requests.get(url) + if response.status_code == 200: + with open(local_file, "wb") as f: + f.write(response.content) + return True + else: + print(f"Cant load {url}, status code: {response.status_code}.") + return False diff --git a/examples/show_gmsh.py b/examples/show_gmsh.py index 08c40a060..0a30d6aa8 100644 --- a/examples/show_gmsh.py +++ b/examples/show_gmsh.py @@ -5,31 +5,31 @@ """ import pathlib +import load_sample_file + from gustaf import io if __name__ == "__main__": - base_samples_path = pathlib.Path("samples/faces") + mesh_file_tri = pathlib.Path("faces/tri/2DChannelTria.msh") + mesh_file_quad = pathlib.Path("faces/quad/2DChannelQuad.msh") + + base_samples_path = pathlib.Path("samples") if not base_samples_path.exists(): - raise RuntimeError( - "The geometries could not be found. Please initialize the " - "samples submodule, instructions can be found in the " - "README.md." - ) - mesh_file_tri = pathlib.Path("samples/faces/tri/2DChannelTria.msh") - mesh_file_quad = pathlib.Path("samples/faces/quad/2DChannelQuad.msh") + load_sample_file.load_sample_file(str(mesh_file_tri)) + load_sample_file.load_sample_file(str(mesh_file_quad)) # load the .msh file directly with the correct io module (meshio) - loaded_mesh_tri = io.meshio.load(mesh_file_tri) + loaded_mesh_tri = io.meshio.load(base_samples_path / mesh_file_tri) loaded_mesh_tri.show() # load the .msh file directly with the correct io module (meshio) - loaded_mesh_quad = io.meshio.load(mesh_file_quad) + loaded_mesh_quad = io.meshio.load(base_samples_path / mesh_file_quad) loaded_mesh_quad.show() # load the .msh file with the default load function which needs to find out # it self which module is the correct one. - loaded_mesh_default = io.load(mesh_file_tri) + loaded_mesh_default = io.load(base_samples_path / mesh_file_tri) loaded_mesh_default.show() From 0ab1e3fb983e481ba83114959108ea385daa9294 Mon Sep 17 00:00:00 2001 From: Jaewook Lee Date: Tue, 27 Jun 2023 10:05:21 +0200 Subject: [PATCH 11/11] add funi option --- gustaf/utils/arr.py | 13 +++++++++++++ setup.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/gustaf/utils/arr.py b/gustaf/utils/arr.py index 36386adbc..21dbbe16c 100644 --- a/gustaf/utils/arr.py +++ b/gustaf/utils/arr.py @@ -157,6 +157,19 @@ def close_rows( if nthreads is None: nthreads = settings.NTHREADS + if not return_intersection: + try: + import funi + + return ( + *funi.unique_rows( + arr, tolerance, True, True, True, True, True + ), + [], + ) + except ImportError: + pass + try: from napf import KDT diff --git a/setup.py b/setup.py index e0567c53e..2021e60e3 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ ], install_requires=["numpy"], extras_require={ - "all": ["vedo>=2023.4.3", "scipy", "meshio", "napf>=0.0.5"] + "all": ["vedo>=2023.4.3", "scipy", "meshio", "napf>=0.0.5", "funi"] }, classifiers=[ "Development Status :: 2 - Pre-Alpha",