symcad.parts.composite.TorisphericalCapsule
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 PyFreeCAD.FreeCAD import FreeCAD, Part 19from typing import Dict, Optional, Tuple, Union 20from sympy import Expr, Symbol, sqrt, cos, asin 21from . import CompositeShape 22import math 23 24class TorisphericalCapsule(CompositeShape): 25 """Model representing a parameteric capsule with torispherical endcaps. 26 27 By default, the capsule is oriented such that the endcaps are aligned with the x-axis: 28 29  30 31 The `geometry` of this shape includes the following parameters: 32 33 - `cylinder_radius`: Outer radius (in `m`) of the center cylindrical part of the Capsule 34 - `cylinder_length`: Length (in `m`) of the center cylindrical part of the Capsule 35 - `cylinder_thickness`: Thickness (in `m`) of the cylindrical shell of the Capsule 36 - `endcap_thickness`: Thickness (in `m`) of the torispherical endcaps of the Capsule 37 - `crown_ratio`: Ratio (in `%`) of the radius of the crown of the torispherical 38 endcap to the `cylinder_radius` 39 - `knuckle_ratio`: Ratio (in `%`) of the radius of the knuckle of the torispherical 40 endcap to the `cylinder_radius` 41 42 Note that the above dimensions should be interpreted as if the capsule is unrotated. In other 43 words, any shape rotation takes place *after* the capsule dimensions have been specified. 44 """ 45 46 # Constructor ---------------------------------------------------------------------------------- 47 48 def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None: 49 """Initializes a parametric capsule object with torispherical endcaps. 50 51 Parameters 52 ---------- 53 identifier : `str` 54 Unique identifying name for the object. 55 material_density_kg_m3 : `float`, optional, default=1.0 56 Uniform material density in `kg/m^3` to be used in mass property calculations. 57 """ 58 super().__init__(identifier, 'TorisphericalCapsule.FCStd', None, material_density_kg_m3) 59 setattr(self.geometry, 'cylinder_radius', Symbol(self.name + '_cylinder_radius')) 60 setattr(self.geometry, 'cylinder_length', Symbol(self.name + '_cylinder_length')) 61 setattr(self.geometry, 'cylinder_thickness', Symbol(self.name + '_cylinder_thickness')) 62 setattr(self.geometry, 'endcap_thickness', Symbol(self.name + '_endcap_thickness')) 63 setattr(self.geometry, 'crown_ratio', Symbol(self.name + '_crown_ratio')) 64 setattr(self.geometry, 'knuckle_ratio', Symbol(self.name + '_knuckle_ratio')) 65 66 67 # Geometry setter ------------------------------------------------------------------------------ 68 69 def set_geometry(self, *, cylinder_radius_m: Union[float, None], 70 cylinder_length_m: Union[float, None], 71 cylinder_thickness_m: Union[float, None], 72 endcap_thickness_m: Union[float, None], 73 crown_ratio_percent: Union[float, None] = 1.0, 74 knuckle_ratio_percent: Union[float, None] = 0.06) -> TorisphericalCapsule: 75 """Sets the physical geometry of the current `TorisphericalCapsule` object. 76 77 See the `TorisphericalCapsule` class documentation for a description of each geometric 78 parameter. 79 """ 80 if crown_ratio_percent is not None and crown_ratio_percent > 1.0: 81 raise ValueError('crown_ratio_percent ({}) is not a percentage between 0.0 - 1.0' 82 .format(crown_ratio_percent)) 83 if knuckle_ratio_percent is not None and (knuckle_ratio_percent < 0.06 or 84 knuckle_ratio_percent > 1.0): 85 raise ValueError('knuckle_ratio_percent ({}) is not a percentage between 0.06 - 1.0' 86 .format(knuckle_ratio_percent)) 87 self.geometry.set(cylinder_radius=cylinder_radius_m, 88 cylinder_length=cylinder_length_m, 89 cylinder_thickness=cylinder_thickness_m, 90 endcap_thickness=endcap_thickness_m, 91 crown_ratio=crown_ratio_percent, 92 knuckle_ratio=knuckle_ratio_percent) 93 return self 94 95 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 96 parameter_bounds = { 97 'cylinder_radius': (0.01, 2.0), 98 'cylinder_length': (0.01, 2.0), 99 'cylinder_thickness': (0.001, 0.05), 100 'endcap_thickness': (0.001, 0.05), 101 'crown_ratio': (0.0, 1.0), 102 'knuckle_ratio': (0.06, 1.0) 103 } 104 return parameter_bounds.get(parameter, (0.0, 0.0)) 105 106 107 # Geometric properties ------------------------------------------------------------------------- 108 109 @property 110 def material_volume(self) -> Union[float, Expr]: 111 knuckle_radius = (2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius) - \ 112 self.geometry.endcap_thickness 113 crown_radius = (2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius) - \ 114 self.geometry.endcap_thickness 115 c = self.geometry.cylinder_radius - self.geometry.endcap_thickness - knuckle_radius 116 h = crown_radius - sqrt((knuckle_radius + c - crown_radius) * 117 (knuckle_radius - c - crown_radius)) 118 volume = self.displaced_volume 119 volume -= ((math.pi / 3.0) * \ 120 ((2.0 * h * crown_radius**2) - 121 (((2.0 * knuckle_radius**2) + c**2 + 122 (2.0 * knuckle_radius * crown_radius)) * (crown_radius - h)) + 123 (3.0 * knuckle_radius**2 * c * 124 asin((crown_radius - h) / (crown_radius - knuckle_radius))))) 125 volume -= (math.pi 126 * (self.geometry.cylinder_radius - self.geometry.cylinder_thickness)**2 127 * self.geometry.cylinder_length) 128 return volume 129 130 @property 131 def displaced_volume(self) -> Union[float, Expr]: 132 knuckle_radius = 2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius 133 crown_radius = 2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius 134 c = self.geometry.cylinder_radius - knuckle_radius 135 h = crown_radius - sqrt((knuckle_radius + c - crown_radius) * 136 (knuckle_radius - c - crown_radius)) 137 endcap_volume = (math.pi / 3.0) * \ 138 ((2.0 * h * crown_radius**2) - 139 (((2.0 * knuckle_radius**2) + c**2 + 140 (2.0 * knuckle_radius * crown_radius)) * (crown_radius - h)) + 141 (3.0 * knuckle_radius**2 * c * 142 asin((crown_radius - h) / (crown_radius - knuckle_radius)))) 143 return (math.pi * self.geometry.cylinder_radius**2 * self.geometry.cylinder_length) + \ 144 (2.0 * endcap_volume) 145 146 @property 147 def surface_area(self) -> Union[float, Expr]: 148 knuckle_radius = 2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius 149 crown_radius = 2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius 150 cos_alpha = cos(asin((1.0 - (2.0 * self.geometry.knuckle_ratio)) / 151 (2.0 * (self.geometry.crown_ratio - self.geometry.knuckle_ratio)))) 152 a2 = knuckle_radius * cos_alpha 153 endcap_area = (4.0 * math.pi * crown_radius**2 * (1.0 - cos_alpha)) + \ 154 (4.0 * math.pi * knuckle_radius * 155 (a2 + ((self.geometry.cylinder_radius - knuckle_radius) * asin(cos_alpha)))) 156 return (2.0 * math.pi * self.geometry.cylinder_radius * self.geometry.cylinder_length) + \ 157 endcap_area 158 159 @property 160 def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr], 161 Union[float, Expr], 162 Union[float, Expr]]: 163 return (0.5 * self.unoriented_length, 164 0.5 * self.unoriented_width, 165 0.5 * self.unoriented_height) 166 167 @property 168 def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr], 169 Union[float, Expr], 170 Union[float, Expr]]: 171 return self.unoriented_center_of_gravity 172 173 @property 174 def unoriented_length(self) -> Union[float, Expr]: 175 knuckle_radius = 2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius 176 crown_radius = 2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius 177 c = self.geometry.cylinder_radius - knuckle_radius 178 endcap_length = crown_radius - sqrt((knuckle_radius + c - crown_radius) * 179 (knuckle_radius - c - crown_radius)) 180 return (2.0 * endcap_length) + self.geometry.cylinder_length 181 182 @property 183 def unoriented_width(self) -> Union[float, Expr]: 184 return 2.0 * self.geometry.cylinder_radius 185 186 @property 187 def unoriented_height(self) -> Union[float, Expr]: 188 return self.unoriented_width 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
25class TorisphericalCapsule(CompositeShape): 26 """Model representing a parameteric capsule with torispherical endcaps. 27 28 By default, the capsule is oriented such that the endcaps are aligned with the x-axis: 29 30  31 32 The `geometry` of this shape includes the following parameters: 33 34 - `cylinder_radius`: Outer radius (in `m`) of the center cylindrical part of the Capsule 35 - `cylinder_length`: Length (in `m`) of the center cylindrical part of the Capsule 36 - `cylinder_thickness`: Thickness (in `m`) of the cylindrical shell of the Capsule 37 - `endcap_thickness`: Thickness (in `m`) of the torispherical endcaps of the Capsule 38 - `crown_ratio`: Ratio (in `%`) of the radius of the crown of the torispherical 39 endcap to the `cylinder_radius` 40 - `knuckle_ratio`: Ratio (in `%`) of the radius of the knuckle of the torispherical 41 endcap to the `cylinder_radius` 42 43 Note that the above dimensions should be interpreted as if the capsule is unrotated. In other 44 words, any shape rotation takes place *after* the capsule dimensions have been specified. 45 """ 46 47 # Constructor ---------------------------------------------------------------------------------- 48 49 def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None: 50 """Initializes a parametric capsule object with torispherical endcaps. 51 52 Parameters 53 ---------- 54 identifier : `str` 55 Unique identifying name for the object. 56 material_density_kg_m3 : `float`, optional, default=1.0 57 Uniform material density in `kg/m^3` to be used in mass property calculations. 58 """ 59 super().__init__(identifier, 'TorisphericalCapsule.FCStd', None, material_density_kg_m3) 60 setattr(self.geometry, 'cylinder_radius', Symbol(self.name + '_cylinder_radius')) 61 setattr(self.geometry, 'cylinder_length', Symbol(self.name + '_cylinder_length')) 62 setattr(self.geometry, 'cylinder_thickness', Symbol(self.name + '_cylinder_thickness')) 63 setattr(self.geometry, 'endcap_thickness', Symbol(self.name + '_endcap_thickness')) 64 setattr(self.geometry, 'crown_ratio', Symbol(self.name + '_crown_ratio')) 65 setattr(self.geometry, 'knuckle_ratio', Symbol(self.name + '_knuckle_ratio')) 66 67 68 # Geometry setter ------------------------------------------------------------------------------ 69 70 def set_geometry(self, *, cylinder_radius_m: Union[float, None], 71 cylinder_length_m: Union[float, None], 72 cylinder_thickness_m: Union[float, None], 73 endcap_thickness_m: Union[float, None], 74 crown_ratio_percent: Union[float, None] = 1.0, 75 knuckle_ratio_percent: Union[float, None] = 0.06) -> TorisphericalCapsule: 76 """Sets the physical geometry of the current `TorisphericalCapsule` object. 77 78 See the `TorisphericalCapsule` class documentation for a description of each geometric 79 parameter. 80 """ 81 if crown_ratio_percent is not None and crown_ratio_percent > 1.0: 82 raise ValueError('crown_ratio_percent ({}) is not a percentage between 0.0 - 1.0' 83 .format(crown_ratio_percent)) 84 if knuckle_ratio_percent is not None and (knuckle_ratio_percent < 0.06 or 85 knuckle_ratio_percent > 1.0): 86 raise ValueError('knuckle_ratio_percent ({}) is not a percentage between 0.06 - 1.0' 87 .format(knuckle_ratio_percent)) 88 self.geometry.set(cylinder_radius=cylinder_radius_m, 89 cylinder_length=cylinder_length_m, 90 cylinder_thickness=cylinder_thickness_m, 91 endcap_thickness=endcap_thickness_m, 92 crown_ratio=crown_ratio_percent, 93 knuckle_ratio=knuckle_ratio_percent) 94 return self 95 96 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 97 parameter_bounds = { 98 'cylinder_radius': (0.01, 2.0), 99 'cylinder_length': (0.01, 2.0), 100 'cylinder_thickness': (0.001, 0.05), 101 'endcap_thickness': (0.001, 0.05), 102 'crown_ratio': (0.0, 1.0), 103 'knuckle_ratio': (0.06, 1.0) 104 } 105 return parameter_bounds.get(parameter, (0.0, 0.0)) 106 107 108 # Geometric properties ------------------------------------------------------------------------- 109 110 @property 111 def material_volume(self) -> Union[float, Expr]: 112 knuckle_radius = (2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius) - \ 113 self.geometry.endcap_thickness 114 crown_radius = (2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius) - \ 115 self.geometry.endcap_thickness 116 c = self.geometry.cylinder_radius - self.geometry.endcap_thickness - knuckle_radius 117 h = crown_radius - sqrt((knuckle_radius + c - crown_radius) * 118 (knuckle_radius - c - crown_radius)) 119 volume = self.displaced_volume 120 volume -= ((math.pi / 3.0) * \ 121 ((2.0 * h * crown_radius**2) - 122 (((2.0 * knuckle_radius**2) + c**2 + 123 (2.0 * knuckle_radius * crown_radius)) * (crown_radius - h)) + 124 (3.0 * knuckle_radius**2 * c * 125 asin((crown_radius - h) / (crown_radius - knuckle_radius))))) 126 volume -= (math.pi 127 * (self.geometry.cylinder_radius - self.geometry.cylinder_thickness)**2 128 * self.geometry.cylinder_length) 129 return volume 130 131 @property 132 def displaced_volume(self) -> Union[float, Expr]: 133 knuckle_radius = 2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius 134 crown_radius = 2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius 135 c = self.geometry.cylinder_radius - knuckle_radius 136 h = crown_radius - sqrt((knuckle_radius + c - crown_radius) * 137 (knuckle_radius - c - crown_radius)) 138 endcap_volume = (math.pi / 3.0) * \ 139 ((2.0 * h * crown_radius**2) - 140 (((2.0 * knuckle_radius**2) + c**2 + 141 (2.0 * knuckle_radius * crown_radius)) * (crown_radius - h)) + 142 (3.0 * knuckle_radius**2 * c * 143 asin((crown_radius - h) / (crown_radius - knuckle_radius)))) 144 return (math.pi * self.geometry.cylinder_radius**2 * self.geometry.cylinder_length) + \ 145 (2.0 * endcap_volume) 146 147 @property 148 def surface_area(self) -> Union[float, Expr]: 149 knuckle_radius = 2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius 150 crown_radius = 2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius 151 cos_alpha = cos(asin((1.0 - (2.0 * self.geometry.knuckle_ratio)) / 152 (2.0 * (self.geometry.crown_ratio - self.geometry.knuckle_ratio)))) 153 a2 = knuckle_radius * cos_alpha 154 endcap_area = (4.0 * math.pi * crown_radius**2 * (1.0 - cos_alpha)) + \ 155 (4.0 * math.pi * knuckle_radius * 156 (a2 + ((self.geometry.cylinder_radius - knuckle_radius) * asin(cos_alpha)))) 157 return (2.0 * math.pi * self.geometry.cylinder_radius * self.geometry.cylinder_length) + \ 158 endcap_area 159 160 @property 161 def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr], 162 Union[float, Expr], 163 Union[float, Expr]]: 164 return (0.5 * self.unoriented_length, 165 0.5 * self.unoriented_width, 166 0.5 * self.unoriented_height) 167 168 @property 169 def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr], 170 Union[float, Expr], 171 Union[float, Expr]]: 172 return self.unoriented_center_of_gravity 173 174 @property 175 def unoriented_length(self) -> Union[float, Expr]: 176 knuckle_radius = 2.0 * self.geometry.knuckle_ratio * self.geometry.cylinder_radius 177 crown_radius = 2.0 * self.geometry.crown_ratio * self.geometry.cylinder_radius 178 c = self.geometry.cylinder_radius - knuckle_radius 179 endcap_length = crown_radius - sqrt((knuckle_radius + c - crown_radius) * 180 (knuckle_radius - c - crown_radius)) 181 return (2.0 * endcap_length) + self.geometry.cylinder_length 182 183 @property 184 def unoriented_width(self) -> Union[float, Expr]: 185 return 2.0 * self.geometry.cylinder_radius 186 187 @property 188 def unoriented_height(self) -> Union[float, Expr]: 189 return self.unoriented_width 190 191 @property 192 def oriented_length(self) -> Union[float, Expr]: 193 # TODO: Implement this 194 return 0 195 196 @property 197 def oriented_width(self) -> Union[float, Expr]: 198 # TODO: Implement this 199 return 0 200 201 @property 202 def oriented_height(self) -> Union[float, Expr]: 203 # TODO: Implement this 204 return 0
Model representing a parameteric capsule with torispherical endcaps.
By default, the capsule is oriented such that the endcaps are aligned with the x-axis:

The geometry of this shape includes the following parameters:
cylinder_radius: Outer radius (inm) of the center cylindrical part of the Capsulecylinder_length: Length (inm) of the center cylindrical part of the Capsulecylinder_thickness: Thickness (inm) of the cylindrical shell of the Capsuleendcap_thickness: Thickness (inm) of the torispherical endcaps of the Capsulecrown_ratio: Ratio (in%) of the radius of the crown of the torispherical endcap to thecylinder_radiusknuckle_ratio: Ratio (in%) of the radius of the knuckle of the torispherical endcap to thecylinder_radius
Note that the above dimensions should be interpreted as if the capsule is unrotated. In other words, any shape rotation takes place after the capsule dimensions have been specified.
49 def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None: 50 """Initializes a parametric capsule object with torispherical endcaps. 51 52 Parameters 53 ---------- 54 identifier : `str` 55 Unique identifying name for the object. 56 material_density_kg_m3 : `float`, optional, default=1.0 57 Uniform material density in `kg/m^3` to be used in mass property calculations. 58 """ 59 super().__init__(identifier, 'TorisphericalCapsule.FCStd', None, material_density_kg_m3) 60 setattr(self.geometry, 'cylinder_radius', Symbol(self.name + '_cylinder_radius')) 61 setattr(self.geometry, 'cylinder_length', Symbol(self.name + '_cylinder_length')) 62 setattr(self.geometry, 'cylinder_thickness', Symbol(self.name + '_cylinder_thickness')) 63 setattr(self.geometry, 'endcap_thickness', Symbol(self.name + '_endcap_thickness')) 64 setattr(self.geometry, 'crown_ratio', Symbol(self.name + '_crown_ratio')) 65 setattr(self.geometry, 'knuckle_ratio', Symbol(self.name + '_knuckle_ratio'))
Initializes a parametric capsule object with torispherical endcaps.
Parameters
- identifier (
str): Unique identifying name for the object. - material_density_kg_m3 (
float, optional, default=1.0): Uniform material density inkg/m^3to be used in mass property calculations.
70 def set_geometry(self, *, cylinder_radius_m: Union[float, None], 71 cylinder_length_m: Union[float, None], 72 cylinder_thickness_m: Union[float, None], 73 endcap_thickness_m: Union[float, None], 74 crown_ratio_percent: Union[float, None] = 1.0, 75 knuckle_ratio_percent: Union[float, None] = 0.06) -> TorisphericalCapsule: 76 """Sets the physical geometry of the current `TorisphericalCapsule` object. 77 78 See the `TorisphericalCapsule` class documentation for a description of each geometric 79 parameter. 80 """ 81 if crown_ratio_percent is not None and crown_ratio_percent > 1.0: 82 raise ValueError('crown_ratio_percent ({}) is not a percentage between 0.0 - 1.0' 83 .format(crown_ratio_percent)) 84 if knuckle_ratio_percent is not None and (knuckle_ratio_percent < 0.06 or 85 knuckle_ratio_percent > 1.0): 86 raise ValueError('knuckle_ratio_percent ({}) is not a percentage between 0.06 - 1.0' 87 .format(knuckle_ratio_percent)) 88 self.geometry.set(cylinder_radius=cylinder_radius_m, 89 cylinder_length=cylinder_length_m, 90 cylinder_thickness=cylinder_thickness_m, 91 endcap_thickness=endcap_thickness_m, 92 crown_ratio=crown_ratio_percent, 93 knuckle_ratio=knuckle_ratio_percent) 94 return self
Sets the physical geometry of the current TorisphericalCapsule object.
See the TorisphericalCapsule class documentation for a description of each geometric
parameter.
96 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 97 parameter_bounds = { 98 'cylinder_radius': (0.01, 2.0), 99 'cylinder_length': (0.01, 2.0), 100 'cylinder_thickness': (0.001, 0.05), 101 'endcap_thickness': (0.001, 0.05), 102 'crown_ratio': (0.0, 1.0), 103 'knuckle_ratio': (0.06, 1.0) 104 } 105 return parameter_bounds.get(parameter, (0.0, 0.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 geometricparameter.
Material volume (in m^3) of the SymPart (read-only).
Displaced volume (in m^3) of the SymPart (read-only).
Surface/wetted area (in m^2) of the SymPart (read-only).
Center of gravity (in m) of the unoriented SymPart (read-only).
Center of buoyancy (in m) of the unoriented SymPart (read-only).
X-axis length (in m) of the bounding box of the unoriented SymPart (read-only).
Y-axis width (in m) of the bounding box of the unoriented SymPart (read-only).
Z-axis height (in m) of the bounding box of the unoriented SymPart (read-only).
X-axis length (in m) of the bounding box of the oriented SymPart (read-only).
Y-axis length (in m) of the bounding box of the oriented SymPart (read-only).
Z-axis length (in m) of the bounding box of the oriented SymPart (read-only).
Inherited Members
- symcad.core.SymPart.SymPart
- name
- geometry
- attachment_points
- attachments
- connection_ports
- connections
- static_origin
- static_placement
- orientation
- material_density
- current_states
- is_exposed
- clone
- set_placement
- set_orientation
- set_state
- set_unexposed
- set_material_density
- add_attachment_point
- add_connection_port
- attach
- connect
- get_cad_physical_properties
- export
- get_valid_states
- mass
- oriented_center_of_gravity
- oriented_center_of_buoyancy