symcad.parts.generic.Custom

  1#!/usr/bin/env python3
  2# Copyright (C) 2022, Will Hedgecock
  3#
  4# This program is free software: you can redistribute it and/or modify
  5# it under the terms of the GNU General Public License as published by
  6# the Free Software Foundation, either version 3 of the License, or
  7# (at your option) any later version.
  8#
  9# This program is distributed in the hope that it will be useful,
 10# but WITHOUT ANY WARRANTY; without even the implied warranty of
 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12# GNU General Public License for more details.
 13#
 14# You should have received a copy of the GNU General Public License
 15# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 16
 17from __future__ import annotations
 18from typing import Callable, Optional, Tuple, Union
 19from ...core.CAD import CadGeneral
 20from ...core.ML import NeuralNet
 21from sympy import Expr, Symbol
 22from . import GenericShape
 23from pathlib import Path
 24
 25class Custom(GenericShape):
 26   """Model representing a custom generic part.
 27
 28   This class should be used to define a new type of CAD model from within an external library or
 29   application, without the need to alter the built-in library of parts within SymCAD.
 30
 31   Regardless of whether an existing CAD model (parameteric or fixed-geometry) or a CAD creation
 32   function is used to create this part, SymCAD will automatically retrieve any geometric free
 33   parameters specified in the model and create symbols for them.
 34   """
 35
 36   # Constructor ----------------------------------------------------------------------------------
 37
 38   def __init__(self, type_name: str,
 39                      identifier: str,
 40                      cad_representation: Union[str, Callable],
 41                      pretrained_geometric_properties_model: Union[str, None] = None,
 42                      material_density_kg_m3: Optional[float] = 1.0,
 43                      auto_train_missing_property_model: bool = True) -> None:
 44      """Initializes a custom `GenericShape` part object.
 45
 46      If the `Custom` part is parametric and requires a trained neural network to evaluate its
 47      geometric or mass properties, this network should be trained using the accompanying
 48      `NeuralNetTrainer` tool, and the full path to the resulting neural network model should be
 49      passed in using the `pretrained_geometric_properties_model` parameter. Alternately, a value
 50      of `None` may be passed in for this parameter, and a new model will automatically be
 51      trained if the `auto_train_missing_property_model` parameter is set to `True`.
 52
 53      Parameters
 54      ----------
 55      type_name : `str`
 56         Name identifying the type of part of this object.
 57      identifier : `str`
 58         Unique identifying name for this instance of the object.
 59      cad_representation : `Union[str, Callable]`
 60         Either the path to a representative CAD model for the given part or a callable
 61         method that can create such a model.
 62      pretrained_geometric_properties_model : `Union[str, None]`, default=None
 63         Path to a neural network that represents the underlying geometric properties for
 64         the given `Custom` part, or `None` if no model is required.
 65      material_density_kg_m3 : `float`, optional, default=1.0
 66         Uniform material density in `kg/m^3` to be used in mass property calculations.
 67      auto_train_missing_property_model : `bool`, default=True
 68         Whether to automatically train new a neural network model to evaluate the geometric
 69         properties of the `Custom` part.
 70      """
 71
 72      # Initialize the Custom part and detect its underlying free parameters
 73      super().__init__(identifier, cad_representation, None, material_density_kg_m3)
 74      free_params = CadGeneral.get_free_parameters_from_model(self.__cad__.cad_file_path, None) \
 75                       if isinstance(cad_representation, str) else \
 76                    CadGeneral.get_free_parameters_from_method(self.__cad__.creation_callback)
 77      for param in free_params:
 78         setattr(self.geometry, param, Symbol(self.name + '_' + param))
 79
 80      # Retrieve a physical property representation based on whether the part is fully concrete
 81      if len(free_params) == 0:
 82         self.__neural_net__ = None
 83         self.__cad_props__ = self.__cad__.get_physical_properties(self.geometry.as_dict(),
 84                                                                   (0.0, 0.0, 0.0),
 85                                                                   (0.0, 0.0, 0.0),
 86                                                                   self.material_density,
 87                                                                   True)
 88      else:
 89         self.__cad_props__ = None
 90         self.__neural_net__ = NeuralNet(type_name,
 91                                         pretrained_geometric_properties_model
 92                                            if pretrained_geometric_properties_model else
 93                                         Path('converted').joinpath(type_name + '.tar.xz'),
 94                                         self,
 95                                         auto_train_missing_property_model)
 96
 97
 98   # Geometry setter ------------------------------------------------------------------------------
 99
100   def set_geometry(self, **kwargs) -> Custom:
101      """Sets the physical geometry of the current `Custom` object.
102
103      The `**kwargs` argument should include key-value pairs containing the name and desired
104      concrete value for any geometric parameters of interest. In order to determine the parameters
105      available, you should inspect the `geometry` attribute of the current `Custom` instance
106      (e.g., `print(custom_shape.geometry)`).
107
108      Parameters
109      ----------
110      **kwargs : `Dict`
111         Set of named parameters that define the geometry of this Custom part. If the named
112         parameter is missing or set to `None` for any geometric parameter, that parameter will be
113         treated as a symbol.
114      """
115
116      # Re-initialize the physical property representation of the part based on whether it is
117      # fully concrete after the update to its underlying geometry
118      free_params = []
119      self.geometry.set(**kwargs)
120      for key in self.geometry.__dict__:
121         if key != 'name' and isinstance(getattr(self.geometry, key), Expr):
122            free_params.append(key)
123      if len(free_params) == 0:
124         self.__cad_props__ = self.__cad__.get_physical_properties(self.geometry.as_dict(),
125                                                                   (0.0, 0.0, 0.0),
126                                                                   (0.0, 0.0, 0.0),
127                                                                   self.material_density,
128                                                                   True)
129      else:
130         self.__cad_props__ = None
131      return self
132
133   def get_geometric_parameter_bounds(self, _parameter: str) -> Tuple[float, float]:
134      return 0.0, 2.0
135
136
137   # Geometric properties -------------------------------------------------------------------------
138
139   @property
140   def material_volume(self) -> Union[float, Expr]:
141      return self.__cad_props__['material_volume'] if self.__cad_props__ else \
142             self.__neural_net__.evaluate('material_volume', **self.geometry.as_dict())
143
144   @property
145   def displaced_volume(self) -> Union[float, Expr]:
146      return self.__cad_props__['displaced_volume'] if self.__cad_props__ else \
147             self.__neural_net__.evaluate('displaced_volume', **self.geometry.as_dict())
148
149   @property
150   def surface_area(self) -> Union[float, Expr]:
151      return self.__cad_props__['surface_area'] if self.__cad_props__ else \
152             self.__neural_net__.evaluate('surface_area', **self.geometry.as_dict())
153
154   @property
155   def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr],
156                                                   Union[float, Expr],
157                                                   Union[float, Expr]]:
158      return (self.__cad_props__['cg_x'], self.__cad_props__['cg_y'], self.__cad_props__['cg_z']) \
159                if self.__cad_props__ else \
160             (self.__neural_net__.evaluate('cg_x', **self.geometry.as_dict()),
161              self.__neural_net__.evaluate('cg_y', **self.geometry.as_dict()),
162              self.__neural_net__.evaluate('cg_z', **self.geometry.as_dict()))
163
164   @property
165   def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr],
166                                                    Union[float, Expr],
167                                                    Union[float, Expr]]:
168      return (self.__cad_props__['cb_x'], self.__cad_props__['cb_y'], self.__cad_props__['cb_z']) \
169                if self.__cad_props__ else \
170             (self.__neural_net__.evaluate('cb_x', **self.geometry.as_dict()),
171              self.__neural_net__.evaluate('cb_y', **self.geometry.as_dict()),
172              self.__neural_net__.evaluate('cb_z', **self.geometry.as_dict()))
173
174   @property
175   def unoriented_length(self) -> Union[float, Expr]:
176      return self.__cad_props__['xlen'] if self.__cad_props__ else \
177             self.__neural_net__.evaluate('xlen', **self.geometry.as_dict())
178
179   @property
180   def unoriented_width(self) -> Union[float, Expr]:
181      return self.__cad_props__['ylen'] if self.__cad_props__ else \
182             self.__neural_net__.evaluate('ylen', **self.geometry.as_dict())
183
184   @property
185   def unoriented_height(self) -> Union[float, Expr]:
186      return self.__cad_props__['zlen'] if self.__cad_props__ else \
187             self.__neural_net__.evaluate('zlen', **self.geometry.as_dict())
188
189   @property
190   def oriented_length(self) -> Union[float, Expr]:
191      # TODO: Implement this
192      return 0
193
194   @property
195   def oriented_width(self) -> Union[float, Expr]:
196      # TODO: Implement this
197      return 0
198
199   @property
200   def oriented_height(self) -> Union[float, Expr]:
201      # TODO: Implement this
202      return 0
class Custom(symcad.parts.generic.GenericShape):
 26class Custom(GenericShape):
 27   """Model representing a custom generic part.
 28
 29   This class should be used to define a new type of CAD model from within an external library or
 30   application, without the need to alter the built-in library of parts within SymCAD.
 31
 32   Regardless of whether an existing CAD model (parameteric or fixed-geometry) or a CAD creation
 33   function is used to create this part, SymCAD will automatically retrieve any geometric free
 34   parameters specified in the model and create symbols for them.
 35   """
 36
 37   # Constructor ----------------------------------------------------------------------------------
 38
 39   def __init__(self, type_name: str,
 40                      identifier: str,
 41                      cad_representation: Union[str, Callable],
 42                      pretrained_geometric_properties_model: Union[str, None] = None,
 43                      material_density_kg_m3: Optional[float] = 1.0,
 44                      auto_train_missing_property_model: bool = True) -> None:
 45      """Initializes a custom `GenericShape` part object.
 46
 47      If the `Custom` part is parametric and requires a trained neural network to evaluate its
 48      geometric or mass properties, this network should be trained using the accompanying
 49      `NeuralNetTrainer` tool, and the full path to the resulting neural network model should be
 50      passed in using the `pretrained_geometric_properties_model` parameter. Alternately, a value
 51      of `None` may be passed in for this parameter, and a new model will automatically be
 52      trained if the `auto_train_missing_property_model` parameter is set to `True`.
 53
 54      Parameters
 55      ----------
 56      type_name : `str`
 57         Name identifying the type of part of this object.
 58      identifier : `str`
 59         Unique identifying name for this instance of the object.
 60      cad_representation : `Union[str, Callable]`
 61         Either the path to a representative CAD model for the given part or a callable
 62         method that can create such a model.
 63      pretrained_geometric_properties_model : `Union[str, None]`, default=None
 64         Path to a neural network that represents the underlying geometric properties for
 65         the given `Custom` part, or `None` if no model is required.
 66      material_density_kg_m3 : `float`, optional, default=1.0
 67         Uniform material density in `kg/m^3` to be used in mass property calculations.
 68      auto_train_missing_property_model : `bool`, default=True
 69         Whether to automatically train new a neural network model to evaluate the geometric
 70         properties of the `Custom` part.
 71      """
 72
 73      # Initialize the Custom part and detect its underlying free parameters
 74      super().__init__(identifier, cad_representation, None, material_density_kg_m3)
 75      free_params = CadGeneral.get_free_parameters_from_model(self.__cad__.cad_file_path, None) \
 76                       if isinstance(cad_representation, str) else \
 77                    CadGeneral.get_free_parameters_from_method(self.__cad__.creation_callback)
 78      for param in free_params:
 79         setattr(self.geometry, param, Symbol(self.name + '_' + param))
 80
 81      # Retrieve a physical property representation based on whether the part is fully concrete
 82      if len(free_params) == 0:
 83         self.__neural_net__ = None
 84         self.__cad_props__ = self.__cad__.get_physical_properties(self.geometry.as_dict(),
 85                                                                   (0.0, 0.0, 0.0),
 86                                                                   (0.0, 0.0, 0.0),
 87                                                                   self.material_density,
 88                                                                   True)
 89      else:
 90         self.__cad_props__ = None
 91         self.__neural_net__ = NeuralNet(type_name,
 92                                         pretrained_geometric_properties_model
 93                                            if pretrained_geometric_properties_model else
 94                                         Path('converted').joinpath(type_name + '.tar.xz'),
 95                                         self,
 96                                         auto_train_missing_property_model)
 97
 98
 99   # Geometry setter ------------------------------------------------------------------------------
100
101   def set_geometry(self, **kwargs) -> Custom:
102      """Sets the physical geometry of the current `Custom` object.
103
104      The `**kwargs` argument should include key-value pairs containing the name and desired
105      concrete value for any geometric parameters of interest. In order to determine the parameters
106      available, you should inspect the `geometry` attribute of the current `Custom` instance
107      (e.g., `print(custom_shape.geometry)`).
108
109      Parameters
110      ----------
111      **kwargs : `Dict`
112         Set of named parameters that define the geometry of this Custom part. If the named
113         parameter is missing or set to `None` for any geometric parameter, that parameter will be
114         treated as a symbol.
115      """
116
117      # Re-initialize the physical property representation of the part based on whether it is
118      # fully concrete after the update to its underlying geometry
119      free_params = []
120      self.geometry.set(**kwargs)
121      for key in self.geometry.__dict__:
122         if key != 'name' and isinstance(getattr(self.geometry, key), Expr):
123            free_params.append(key)
124      if len(free_params) == 0:
125         self.__cad_props__ = self.__cad__.get_physical_properties(self.geometry.as_dict(),
126                                                                   (0.0, 0.0, 0.0),
127                                                                   (0.0, 0.0, 0.0),
128                                                                   self.material_density,
129                                                                   True)
130      else:
131         self.__cad_props__ = None
132      return self
133
134   def get_geometric_parameter_bounds(self, _parameter: str) -> Tuple[float, float]:
135      return 0.0, 2.0
136
137
138   # Geometric properties -------------------------------------------------------------------------
139
140   @property
141   def material_volume(self) -> Union[float, Expr]:
142      return self.__cad_props__['material_volume'] if self.__cad_props__ else \
143             self.__neural_net__.evaluate('material_volume', **self.geometry.as_dict())
144
145   @property
146   def displaced_volume(self) -> Union[float, Expr]:
147      return self.__cad_props__['displaced_volume'] if self.__cad_props__ else \
148             self.__neural_net__.evaluate('displaced_volume', **self.geometry.as_dict())
149
150   @property
151   def surface_area(self) -> Union[float, Expr]:
152      return self.__cad_props__['surface_area'] if self.__cad_props__ else \
153             self.__neural_net__.evaluate('surface_area', **self.geometry.as_dict())
154
155   @property
156   def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr],
157                                                   Union[float, Expr],
158                                                   Union[float, Expr]]:
159      return (self.__cad_props__['cg_x'], self.__cad_props__['cg_y'], self.__cad_props__['cg_z']) \
160                if self.__cad_props__ else \
161             (self.__neural_net__.evaluate('cg_x', **self.geometry.as_dict()),
162              self.__neural_net__.evaluate('cg_y', **self.geometry.as_dict()),
163              self.__neural_net__.evaluate('cg_z', **self.geometry.as_dict()))
164
165   @property
166   def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr],
167                                                    Union[float, Expr],
168                                                    Union[float, Expr]]:
169      return (self.__cad_props__['cb_x'], self.__cad_props__['cb_y'], self.__cad_props__['cb_z']) \
170                if self.__cad_props__ else \
171             (self.__neural_net__.evaluate('cb_x', **self.geometry.as_dict()),
172              self.__neural_net__.evaluate('cb_y', **self.geometry.as_dict()),
173              self.__neural_net__.evaluate('cb_z', **self.geometry.as_dict()))
174
175   @property
176   def unoriented_length(self) -> Union[float, Expr]:
177      return self.__cad_props__['xlen'] if self.__cad_props__ else \
178             self.__neural_net__.evaluate('xlen', **self.geometry.as_dict())
179
180   @property
181   def unoriented_width(self) -> Union[float, Expr]:
182      return self.__cad_props__['ylen'] if self.__cad_props__ else \
183             self.__neural_net__.evaluate('ylen', **self.geometry.as_dict())
184
185   @property
186   def unoriented_height(self) -> Union[float, Expr]:
187      return self.__cad_props__['zlen'] if self.__cad_props__ else \
188             self.__neural_net__.evaluate('zlen', **self.geometry.as_dict())
189
190   @property
191   def oriented_length(self) -> Union[float, Expr]:
192      # TODO: Implement this
193      return 0
194
195   @property
196   def oriented_width(self) -> Union[float, Expr]:
197      # TODO: Implement this
198      return 0
199
200   @property
201   def oriented_height(self) -> Union[float, Expr]:
202      # TODO: Implement this
203      return 0

Model representing a custom generic part.

This class should be used to define a new type of CAD model from within an external library or application, without the need to alter the built-in library of parts within SymCAD.

Regardless of whether an existing CAD model (parameteric or fixed-geometry) or a CAD creation function is used to create this part, SymCAD will automatically retrieve any geometric free parameters specified in the model and create symbols for them.

Custom( type_name: str, identifier: str, cad_representation: Union[str, Callable], pretrained_geometric_properties_model: Optional[str] = None, material_density_kg_m3: Optional[float] = 1.0, auto_train_missing_property_model: bool = True)
39   def __init__(self, type_name: str,
40                      identifier: str,
41                      cad_representation: Union[str, Callable],
42                      pretrained_geometric_properties_model: Union[str, None] = None,
43                      material_density_kg_m3: Optional[float] = 1.0,
44                      auto_train_missing_property_model: bool = True) -> None:
45      """Initializes a custom `GenericShape` part object.
46
47      If the `Custom` part is parametric and requires a trained neural network to evaluate its
48      geometric or mass properties, this network should be trained using the accompanying
49      `NeuralNetTrainer` tool, and the full path to the resulting neural network model should be
50      passed in using the `pretrained_geometric_properties_model` parameter. Alternately, a value
51      of `None` may be passed in for this parameter, and a new model will automatically be
52      trained if the `auto_train_missing_property_model` parameter is set to `True`.
53
54      Parameters
55      ----------
56      type_name : `str`
57         Name identifying the type of part of this object.
58      identifier : `str`
59         Unique identifying name for this instance of the object.
60      cad_representation : `Union[str, Callable]`
61         Either the path to a representative CAD model for the given part or a callable
62         method that can create such a model.
63      pretrained_geometric_properties_model : `Union[str, None]`, default=None
64         Path to a neural network that represents the underlying geometric properties for
65         the given `Custom` part, or `None` if no model is required.
66      material_density_kg_m3 : `float`, optional, default=1.0
67         Uniform material density in `kg/m^3` to be used in mass property calculations.
68      auto_train_missing_property_model : `bool`, default=True
69         Whether to automatically train new a neural network model to evaluate the geometric
70         properties of the `Custom` part.
71      """
72
73      # Initialize the Custom part and detect its underlying free parameters
74      super().__init__(identifier, cad_representation, None, material_density_kg_m3)
75      free_params = CadGeneral.get_free_parameters_from_model(self.__cad__.cad_file_path, None) \
76                       if isinstance(cad_representation, str) else \
77                    CadGeneral.get_free_parameters_from_method(self.__cad__.creation_callback)
78      for param in free_params:
79         setattr(self.geometry, param, Symbol(self.name + '_' + param))
80
81      # Retrieve a physical property representation based on whether the part is fully concrete
82      if len(free_params) == 0:
83         self.__neural_net__ = None
84         self.__cad_props__ = self.__cad__.get_physical_properties(self.geometry.as_dict(),
85                                                                   (0.0, 0.0, 0.0),
86                                                                   (0.0, 0.0, 0.0),
87                                                                   self.material_density,
88                                                                   True)
89      else:
90         self.__cad_props__ = None
91         self.__neural_net__ = NeuralNet(type_name,
92                                         pretrained_geometric_properties_model
93                                            if pretrained_geometric_properties_model else
94                                         Path('converted').joinpath(type_name + '.tar.xz'),
95                                         self,
96                                         auto_train_missing_property_model)

Initializes a custom GenericShape part object.

If the Custom part is parametric and requires a trained neural network to evaluate its geometric or mass properties, this network should be trained using the accompanying NeuralNetTrainer tool, and the full path to the resulting neural network model should be passed in using the pretrained_geometric_properties_model parameter. Alternately, a value of None may be passed in for this parameter, and a new model will automatically be trained if the auto_train_missing_property_model parameter is set to True.

Parameters
  • type_name (str): Name identifying the type of part of this object.
  • identifier (str): Unique identifying name for this instance of the object.
  • cad_representation (Union[str, Callable]): Either the path to a representative CAD model for the given part or a callable method that can create such a model.
  • pretrained_geometric_properties_model (Union[str, None], default=None): Path to a neural network that represents the underlying geometric properties for the given Custom part, or None if no model is required.
  • material_density_kg_m3 (float, optional, default=1.0): Uniform material density in kg/m^3 to be used in mass property calculations.
  • auto_train_missing_property_model (bool, default=True): Whether to automatically train new a neural network model to evaluate the geometric properties of the Custom part.
def set_geometry(self, **kwargs) -> Custom:
101   def set_geometry(self, **kwargs) -> Custom:
102      """Sets the physical geometry of the current `Custom` object.
103
104      The `**kwargs` argument should include key-value pairs containing the name and desired
105      concrete value for any geometric parameters of interest. In order to determine the parameters
106      available, you should inspect the `geometry` attribute of the current `Custom` instance
107      (e.g., `print(custom_shape.geometry)`).
108
109      Parameters
110      ----------
111      **kwargs : `Dict`
112         Set of named parameters that define the geometry of this Custom part. If the named
113         parameter is missing or set to `None` for any geometric parameter, that parameter will be
114         treated as a symbol.
115      """
116
117      # Re-initialize the physical property representation of the part based on whether it is
118      # fully concrete after the update to its underlying geometry
119      free_params = []
120      self.geometry.set(**kwargs)
121      for key in self.geometry.__dict__:
122         if key != 'name' and isinstance(getattr(self.geometry, key), Expr):
123            free_params.append(key)
124      if len(free_params) == 0:
125         self.__cad_props__ = self.__cad__.get_physical_properties(self.geometry.as_dict(),
126                                                                   (0.0, 0.0, 0.0),
127                                                                   (0.0, 0.0, 0.0),
128                                                                   self.material_density,
129                                                                   True)
130      else:
131         self.__cad_props__ = None
132      return self

Sets the physical geometry of the current Custom object.

The **kwargs argument should include key-value pairs containing the name and desired concrete value for any geometric parameters of interest. In order to determine the parameters available, you should inspect the geometry attribute of the current Custom instance (e.g., print(custom_shape.geometry)).

Parameters
  • **kwargs (Dict): Set of named parameters that define the geometry of this Custom part. If the named parameter is missing or set to None for any geometric parameter, that parameter will be treated as a symbol.
def get_geometric_parameter_bounds(self, _parameter: str) -> Tuple[float, float]:
134   def get_geometric_parameter_bounds(self, _parameter: str) -> Tuple[float, float]:
135      return 0.0, 2.0

Abstract method that must be overridden by a concrete SymPart class to return the minimum and maximum expected bounds for a given geometric parameter.

Parameters
  • parameter (str): Name of the geometric parameter for which to return the minimum and maximum bounds.
Returns
  • Tuple[float, float]: Minimum and maximum bounds for the specified geometric parameter.
material_volume: Union[float, sympy.core.expr.Expr]

Material volume (in m^3) of the SymPart (read-only).

displaced_volume: Union[float, sympy.core.expr.Expr]

Displaced volume (in m^3) of the SymPart (read-only).

surface_area: Union[float, sympy.core.expr.Expr]

Surface/wetted area (in m^2) of the SymPart (read-only).

unoriented_center_of_gravity: Tuple[Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr]]

Center of gravity (in m) of the unoriented SymPart (read-only).

unoriented_center_of_buoyancy: Tuple[Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr]]

Center of buoyancy (in m) of the unoriented SymPart (read-only).

unoriented_length: Union[float, sympy.core.expr.Expr]

X-axis length (in m) of the bounding box of the unoriented SymPart (read-only).

unoriented_width: Union[float, sympy.core.expr.Expr]

Y-axis width (in m) of the bounding box of the unoriented SymPart (read-only).

unoriented_height: Union[float, sympy.core.expr.Expr]

Z-axis height (in m) of the bounding box of the unoriented SymPart (read-only).

oriented_length: Union[float, sympy.core.expr.Expr]

X-axis length (in m) of the bounding box of the oriented SymPart (read-only).

oriented_width: Union[float, sympy.core.expr.Expr]

Y-axis length (in m) of the bounding box of the oriented SymPart (read-only).

oriented_height: Union[float, sympy.core.expr.Expr]

Z-axis length (in m) of the bounding box of the oriented SymPart (read-only).