diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index 393cd8555..7fa2edca2 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -28,6 +28,8 @@ import warnings from warnings import warn +from ansys.motorcad.core.geometry_extrusion import ExtrusionBlockList + GEOM_TOLERANCE = 1e-6 @@ -146,7 +148,7 @@ def __init__(self, region_type=RegionType.adaptive, motorcad_instance=None): self._region_type = region_type self.mesh_length = 0 self._linked_regions = [] - + self._extrusion_blocks = ExtrusionBlockList() self._singular = False self._lamination_type = "" @@ -320,6 +322,9 @@ def _from_json(cls, json, motorcad_instance=None): if "lamination_type" in json: new_region._lamination_type = json["lamination_type"] + if "extrusion_blocks" in json: + new_region._extrusion_blocks._from_json(json["extrusion_blocks"]) + new_region._raw_region = json return new_region @@ -371,6 +376,7 @@ def _to_json(self): self._raw_region["on_boundary"] = False if len(self.linked_regions) == 0 else True self._raw_region["singular"] = self._singular self._raw_region["lamination_type"] = lamination_type + self._raw_region["extrusion_blocks"] = (self._extrusion_blocks._to_json,) return self._raw_region @@ -427,6 +433,16 @@ def singular(self): def singular(self, singular): self._singular = singular + @property + def extrusion_blocks(self): + """Get extrusion blocks list. + + Returns + ------- + list of ExtrusionBlock + """ + return self._extrusion_blocks + @property def child_names(self): """Get child names list. @@ -580,6 +596,11 @@ def region_coordinate(self): """Get the reference coordinate within the region.""" return self._region_coordinate + @property + def duplication_angle(self): + """Get linked Motor-CAD instance.""" + return 360 / self.duplications + def subtract(self, region): """Subtract region from self, returning any additional regions. diff --git a/src/ansys/motorcad/core/geometry_extrusion.py b/src/ansys/motorcad/core/geometry_extrusion.py new file mode 100644 index 000000000..2697fd794 --- /dev/null +++ b/src/ansys/motorcad/core/geometry_extrusion.py @@ -0,0 +1,163 @@ +"""Geometry extrusion classes.""" + +# Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +class ExtrusionBlock: + """Generic class for storing 3D extrusion data.""" + + def __init__(self, start_pos=0, end_pos=0, angle_step=0, angle_continuous=0): + """Initialise extrusion block.""" + self._start_pos = start_pos + self._end_pos = end_pos + self._angle_step = angle_step + self._angle_continuous = angle_continuous + + def __eq__(self, other): + """Compare equality of 2 ExtrusionBlock objects.""" + return ( + isinstance(other, ExtrusionBlock) + & (self.start_pos == other.start_pos) + & (self.end_pos == other.end_pos) + & (self.angle_step == other.angle_step) + & (self.angle_continuous == other.angle_continuous) + ) + + @property + def start_pos(self): + """Get start position of block.""" + return self._start_pos + + @start_pos.setter + def start_pos(self, pos): + """Set start position of block.""" + self._start_pos = pos + + @property + def end_pos(self): + """Get end position of block.""" + return self._end_pos + + @end_pos.setter + def end_pos(self, pos): + """Set start position of block.""" + self._end_pos = pos + + @property + def angle_step(self): + """Get end position of block.""" + return self._angle_step + + @angle_step.setter + def angle_step(self, pos): + """Set start position of block.""" + self._angle_step = pos + + @property + def angle_continuous(self): + """Get end position of block.""" + return self._angle_continuous + + @angle_continuous.setter + def angle_continuous(self, pos): + """Set start position of block.""" + self._angle_continuous = pos + + def _from_json(self, json): + """Convert the class from a JSON object. + + Parameters + ---------- + json: dict + Dictionary representing the extrusion block. + """ + self._start_pos = json["extrusion_block_start"] + self._end_pos = json["extrusion_block_end"] + self._angle_step = json["extrusion_block_angle_step"] + self._angle_continuous = json["extrusion_block_continuous_rotation"] + + def _to_json(self): + """Convert from a Python class to a JSON object. + + Returns + ------- + dict + Dictionary of the extrusion block represented as JSON. + """ + block_dict = { + "extrusion_block_start": self.start_pos, + "extrusion_block_end": self.end_pos, + "extrusion_block_angle_step": self.angle_step, + "extrusion_block_continuous_rotation": self.angle_continuous, + } + + return block_dict + + @property + def extrusion_length(self): + """Return extrusion length between start and end positions. + + Returns + ------- + float + Block extrusion length. + """ + return abs(self.end_pos - self.start_pos) + + +class ExtrusionBlockList(list): + """Generic class for list of Entities.""" + + def __eq__(self, other): + """Compare equality of 2 ExtrusionBlockList objects.""" + if isinstance(other, ExtrusionBlockList): + is_equal = True + for block_1, block_2 in zip(self, other): + if block_1 != block_2: + is_equal = False + break + return is_equal + else: + return False + + def _to_json(self): + """Convert from a Python class to a JSON object. + + Returns + ------- + list + List of the extrusion blocks represented as JSON. + """ + return [block._to_json() for block in self] + + def _from_json(self, json_list): + """Convert the class from a JSON object. + + Parameters + ---------- + json: list + List of extrusion blocks in json. + """ + for json_object in json_list: + block = ExtrusionBlock() + block._from_json(json_object) + self.append(block) diff --git a/tests/test_geometry_extrusion.py b/tests/test_geometry_extrusion.py new file mode 100644 index 000000000..b75e6679c --- /dev/null +++ b/tests/test_geometry_extrusion.py @@ -0,0 +1,93 @@ +# Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ansys.motorcad.core.geometry_extrusion import ExtrusionBlock, ExtrusionBlockList + + +def test_extrusion_block_to_json(): + block_dict = { + "extrusion_block_start": 100.1, + "extrusion_block_end": 200.2, + "extrusion_block_angle_step": 1.5, + "extrusion_block_continuous_rotation": 0, + } + + block = ExtrusionBlock() + block.start_pos = 100.1 + block.end_pos = 200.2 + block.angle_step = 1.5 + block.angle_continuous = 0 + + assert block._to_json() == block_dict + + +def test_extrusion_block_from_json(): + block_dict = { + "extrusion_block_start": 100, + "extrusion_block_end": 200, + "extrusion_block_angle_step": 0, + "extrusion_block_continuous_rotation": 20, + } + block = ExtrusionBlock() + block._from_json(block_dict) + block_actual = ExtrusionBlock(start_pos=100, end_pos=200, angle_continuous=20) + + assert block == block_actual + + +def test_extrusion_block_list_to_json(): + blocks_array = [ + { + "extrusion_block_start": 100, + "extrusion_block_end": 200, + "extrusion_block_angle_step": 0, + "extrusion_block_continuous_rotation": 10, + } + ] + + blocks = ExtrusionBlockList() + blocks.append(ExtrusionBlock(start_pos=100, end_pos=200, angle_continuous=10)) + + for block_1, block_2 in zip(blocks._to_json(), blocks_array): + assert block_1 == block_2 + + +def test_extrusion_block_list_from_json(): + blocks_array = [ + { + "extrusion_block_start": 100, + "extrusion_block_end": 200, + "extrusion_block_angle_step": 0, + "extrusion_block_continuous_rotation": 20, + } + ] + blocks = ExtrusionBlockList() + blocks._from_json(blocks_array) + blocks_actual = ExtrusionBlockList() + blocks_actual.append(ExtrusionBlock(start_pos=100, end_pos=200, angle_continuous=20)) + + assert blocks == blocks_actual + + +def test_block_extrusion_length(): + block = ExtrusionBlock(start_pos=100, end_pos=200.5, angle_continuous=0) + assert block.extrusion_length == 100.5