From ba5aa9f125ad6809451747e2312ebf690c2b7003 Mon Sep 17 00:00:00 2001 From: Greg <11791585+elphick@users.noreply.github.com> Date: Tue, 11 Mar 2025 19:13:34 +0800 Subject: [PATCH] Closes #133 - Added RegularGridSurface with tests and modified docs. --- docs/conf.py | 6 +- docs/content/surface.rst | 8 +- docs/images/SurfaceGeometry.png | Bin 52238 -> 0 bytes docs/images/grid2_regular.svg | 431 ++++++++++++++++++++++++++++++++ docs/images/grid2_tensor.svg | 426 +++++++++++++++++++++++++++++++ omf/__init__.py | 2 +- omf/attribute.py | 2 +- omf/surface.py | 70 +++++- tests/test_surface.py | 25 +- 9 files changed, 959 insertions(+), 11 deletions(-) delete mode 100644 docs/images/SurfaceGeometry.png create mode 100644 docs/images/grid2_regular.svg create mode 100644 docs/images/grid2_tensor.svg diff --git a/docs/conf.py b/docs/conf.py index d90b9116..b56d8c43 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -119,7 +119,7 @@ import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + # html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] pass except: html_theme = "default" @@ -313,10 +313,10 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - "https://docs.python.org/": None, + "https://docs.python.org/": ("https://docs.python.org/", None), # 'http://docs.scipy.org/doc/numpy/': None, # 'http://docs.scipy.org/doc/scipy/reference/': None, - "http://propertiespy.readthedocs.io/en/latest/": None, + "http://propertiespy.readthedocs.io/en/latest/": ("http://propertiespy.readthedocs.io/en/latest/", None), } linkcheck_ignore = ["https://gmggroup.org"] linkcheck_retries = 10 diff --git a/docs/content/surface.rst b/docs/content/surface.rst index a6e576f5..5b7762a9 100644 --- a/docs/content/surface.rst +++ b/docs/content/surface.rst @@ -13,11 +13,17 @@ Elements .. autoclass:: omf.surface.Surface -.. image:: /images/SurfaceGridGeometry.png +.. image:: /images/grid2_tensor.svg :align: center .. autoclass:: omf.surface.TensorGridSurface +.. image:: /images/grid2_regular.svg + :align: center + +.. autoclass:: omf.surface.RegularGridSurface + + Attributes ---------- diff --git a/docs/images/SurfaceGeometry.png b/docs/images/SurfaceGeometry.png deleted file mode 100644 index 5ea00e7567d349128ea192e5073da57e2007754c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52238 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 diff --git a/docs/images/grid2_regular.svg b/docs/images/grid2_regular.svg new file mode 100644 index 00000000..105fd967 --- /dev/null +++ b/docs/images/grid2_regular.svg @@ -0,0 +1,431 @@ + + + + + + + + + + + + + + + u + + v + + A 2D regular grid with size [a, b] and count [5, 4] + + + + + + + + + + + + + + a + b + b + b + b + a + a + a + a + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/grid2_tensor.svg b/docs/images/grid2_tensor.svg new file mode 100644 index 00000000..71c21f84 --- /dev/null +++ b/docs/images/grid2_tensor.svg @@ -0,0 +1,426 @@ + + + + + + + + + + + + + + + u + + v + + A 2D tensor grid + + + + + + + + + + + + + + a₀ + b₃ + b₂ + b₁ + b₀ + a₁ + a₂ + a₃ + a₄ + v = [ b₀ b₁ b₃ b₃ ] + u = [ a₀ a₁ a₂ a₃ a₄ ] + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/omf/__init__.py b/omf/__init__.py index 5ac8d2a5..40728d34 100644 --- a/omf/__init__.py +++ b/omf/__init__.py @@ -20,7 +20,7 @@ ) from .lineset import LineSet from .pointset import PointSet -from .surface import Surface, TensorGridSurface +from .surface import Surface, TensorGridSurface, RegularGridSurface from .texture import ProjectedTexture, UVMappedTexture from .fileio import load, save, __version__ diff --git a/omf/attribute.py b/omf/attribute.py index f0fccb53..458ff932 100644 --- a/omf/attribute.py +++ b/omf/attribute.py @@ -118,7 +118,7 @@ def deserialize(cls, value, trusted=False, strict=False, assert_valid=False, **k array_dtype = DATA_TYPE_LOOKUP_TO_NUMPY[value["data_type"]] if value["data_type"] == "BooleanArray": int_arr = np.frombuffer(array_binary, dtype="uint8") - bit_arr = np.unpackbits(int_arr)[: np.product(value["shape"])] + bit_arr = np.unpackbits(int_arr)[: np.prod(value["shape"])] arr = bit_arr.astype(array_dtype) else: arr = np.frombuffer(array_binary, dtype=array_dtype) diff --git a/omf/surface.py b/omf/surface.py index aac10016..72499953 100644 --- a/omf/surface.py +++ b/omf/surface.py @@ -1,7 +1,10 @@ """surface.py: Surface element definitions""" + import numpy as np import properties +EPSILON = np.finfo(float).eps + from .base import ProjectElement from .attribute import ArrayInstanceProperty from .texture import HasTexturesMixin @@ -78,12 +81,12 @@ class TensorGridSurface(BaseSurfaceElement): # pylint: disable=R0901 tensor_u = properties.List( "Grid cell widths, u-direction", - properties.Float("", min=0.0), + properties.Float("", min=EPSILON), coerce=True, ) tensor_v = properties.List( "Grid cell widths, v-direction", - properties.Float("", min=0.0), + properties.Float("", min=EPSILON), coerce=True, ) axis_u = properties.Vector3( @@ -116,7 +119,7 @@ def num_cells(self): @properties.validator def _validate_mesh(self): """Check if mesh content is built correctly""" - if not np.abs(self.axis_u.dot(self.axis_v)) < 1e-6: + if not np.abs(self.axis_u.dot(self.axis_v)) < EPSILON: raise properties.ValidationError("axis_u and axis_v must be orthogonal") if self.offset_w is properties.undefined or self.offset_w is None: return True @@ -126,3 +129,64 @@ def _validate_mesh(self): "{nnode}".format(zlen=len(self.offset_w.array), nnode=self.num_nodes) ) return True + + +class RegularGridSurface(BaseSurfaceElement): # pylint: disable=R0901 + """Surface element with regular spacing in each dimension""" + + schema = "org.omf.v2.element.surfaceregulargrid" + + cell_count = properties.List( + "Number of cells along u, and v axes", + properties.Integer("", min=1), + min_length=2, + max_length=2, + ) + cell_size = properties.List( + "Size of cells in the u and v dimensions", + properties.Float("", min=EPSILON), + min_length=2, + max_length=2, + ) + axis_u = properties.Vector3( + "Vector orientation of u-direction", + default="X", + length=1, + ) + axis_v = properties.Vector3( + "Vector orientation of v-direction", + default="Y", + length=1, + ) + offset_w = ArrayInstanceProperty( + "Node offset", + shape=("*",), + dtype=float, + required=False, + ) + + @property + def num_nodes(self): + """Number of nodes (vertices)""" + cell_count = self.cell_count + return (cell_count[0] + 1) * (cell_count[1] + 1) + + @property + def num_cells(self): + """Number of cells (faces)""" + cell_count = self.cell_count + return cell_count[0] * cell_count[1] + + @properties.validator + def _validate_mesh(self): + """Check if mesh content is built correctly""" + if not np.abs(self.axis_u.dot(self.axis_v)) < 1e-6: + raise properties.ValidationError("axis_u and axis_v must be orthogonal") + if self.offset_w is properties.undefined or self.offset_w is None: + return True + elif len(self.offset_w.array) != self.num_nodes: + raise properties.ValidationError( + "Length of offset_w, {zlen}, must equal number of nodes, " + "{nnode}".format(zlen=len(self.offset_w.array), nnode=self.num_nodes) + ) + return True diff --git a/tests/test_surface.py b/tests/test_surface.py index 88b064d6..053b15dd 100644 --- a/tests/test_surface.py +++ b/tests/test_surface.py @@ -1,4 +1,5 @@ """Tests for Surface validation""" + import numpy as np import pytest @@ -21,8 +22,8 @@ def test_surface(): elem.validate() -def test_surfacegrid(): - """Test surface grid geometry validation""" +def test_tensor_surface(): + """Test tensor surface grid geometry validation""" elem = omf.surface.TensorGridSurface() elem.tensor_u = [1.0, 1.0] elem.tensor_v = [2.0, 2.0, 2.0] @@ -38,3 +39,23 @@ def test_surfacegrid(): elem.offset_w = np.random.rand(6) with pytest.raises(ValueError): elem.validate() + + +def test_regular_surface(): + """Test regular surface geometry validation""" + elem = omf.surface.RegularGridSurface() + elem.origin = [0.0, 0.0, 0.0] + elem.cell_size = [1.0, 1.0] + elem.cell_count = [2, 2] + assert elem.validate() + assert elem.location_length("vertices") == 9 + assert elem.location_length("faces") == 4 + with pytest.raises(ValueError): + elem.cell_size = [0.0, 1.0] + with pytest.raises(ValueError): + elem.origin = [0.0, 0.0] + elem.offset_w = np.random.rand(9) + elem.validate() + elem.offset_w = np.random.rand(4) + with pytest.raises(ValueError): + elem.validate()