symcad.parts.composite.SemiellipsoidalCapsule
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 21from . import CompositeShape 22import math 23 24class SemiellipsoidalCapsule(CompositeShape): 25 """Model representing a parameteric capsule with semiellipsoidal endcaps. 26 27 By default, the capsule is oriented such that the endcaps are aligned with the x-axis: 28 29  30 31 The minor axis of each endcap spans the open face of the endcap to its tip, while the major 32 axis spans the radius of the open face of the endcap itself. 33 34 The `geometry` of this shape includes the following parameters: 35 36 - `cylinder_radius`: Outer radius (in `m`) of the center cylindrical part of the Capsule 37 - `cylinder_length`: Length (in `m`) of the center cylindrical part of the Capsule 38 - `cylinder_thickness`: Thickness (in `m`) of the cylindrical shell of the Capsule 39 - `endcap_radius`: Radius (in `m`) of the semiellipsoidal endcaps of the Capsule 40 - `endcap_thickness`: Thickness (in `m`) of the semiellipsoidal endcaps of the Capsule 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, 49 material_density_kg_m3: Optional[float] = 1.0, 50 major_minor_axis_ratio: Optional[float] = 2.0) -> None: 51 """Initializes a parametric capsule object with semiellipsoidal endcaps. 52 53 The `major_minor_axis_ratio` parameter is used to determine the relative axis lengths of a 54 semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all 55 parameters are concretely defined, then this parameter is meaningless. 56 57 The minor axis of this shape spans the open face of the endcap to its tip, while the major 58 axis spans the radius of the open face of the endcap itself. 59 60 Parameters 61 ---------- 62 identifier : `str` 63 Unique identifying name for the object. 64 material_density_kg_m3 : `float`, optional, default=1.0 65 Uniform material density in `kg/m^3` to be used in mass property calculations. 66 major_minor_axis_ratio : `float`, optional, default=2.0 67 Desired major-to-minor axis ratio of the semiellipsoid. 68 """ 69 super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3) 70 setattr(self.geometry, 'cylinder_radius', Symbol(self.name + '_cylinder_radius')) 71 setattr(self.geometry, 'cylinder_length', Symbol(self.name + '_cylinder_length')) 72 setattr(self.geometry, 'cylinder_thickness', Symbol(self.name + '_cylinder_thickness')) 73 setattr(self.geometry, 'endcap_radius', Symbol(self.name + '_endcap_radius')) 74 setattr(self.geometry, 'endcap_thickness', Symbol(self.name + '_endcap_thickness')) 75 self.set_geometry(cylinder_radius_m=None, 76 cylinder_length_m=None, 77 cylinder_thickness_m=None, 78 endcap_radius_m=None, 79 endcap_thickness_m=None, 80 major_minor_axis_ratio=major_minor_axis_ratio) 81 82 83 # CAD generation function ---------------------------------------------------------------------- 84 85 @staticmethod 86 def __create_cad__(params: Dict[str, float], fully_displace: bool) -> Part.Solid: 87 """Scripted CAD generation method for `SemiellipsoidalCapsule`.""" 88 doc = FreeCAD.newDocument('Temp') 89 cylinder_length_mm = 1000.0 * params['cylinder_length'] 90 outer_cylinder_radius_mm = 1000.0 * params['cylinder_radius'] 91 inner_cylinder_radius_mm = 1000.0 * (params['cylinder_radius'] - params['cylinder_thickness']) 92 outer_endcap_radius_mm = 1000.0 * params['endcap_radius'] 93 inner_endcap_radius_mm = 1000.0 * (params['endcap_radius'] - params['endcap_thickness']) 94 outer_front = doc.addObject('Part::Ellipsoid', 'FrontOuter') 95 outer_front.Radius1 = int(outer_endcap_radius_mm) 96 outer_front.Radius2 = int(outer_cylinder_radius_mm) 97 outer_front.Radius3 = int(outer_cylinder_radius_mm) 98 outer_front.Angle1 = 0.0 99 outer_back = doc.addObject('Part::Ellipsoid', 'BackOuter') 100 outer_back.Radius1 = int(outer_endcap_radius_mm) 101 outer_back.Radius2 = int(outer_cylinder_radius_mm) 102 outer_back.Radius3 = int(outer_cylinder_radius_mm) 103 outer_back.Angle1 = 0.0 104 if not fully_displace: 105 inner_front = doc.addObject('Part::Ellipsoid', 'FrontInner') 106 inner_front.Radius1 = int(inner_endcap_radius_mm) 107 inner_front.Radius2 = int(inner_cylinder_radius_mm) 108 inner_front.Radius3 = int(inner_cylinder_radius_mm) 109 inner_front.Angle1 = 0.0 110 inner_back = doc.addObject('Part::Ellipsoid', 'BackInner') 111 inner_back.Radius1 = int(inner_endcap_radius_mm) 112 inner_back.Radius2 = int(inner_cylinder_radius_mm) 113 inner_back.Radius3 = int(inner_cylinder_radius_mm) 114 inner_back.Angle1 = 0.0 115 doc.recompute() 116 front = outer_front.Shape.cut(inner_front.Shape) 117 back = outer_back.Shape.cut(inner_back.Shape) 118 front.Placement = \ 119 FreeCAD.Placement(FreeCAD.Vector(0, 0, 0), 120 FreeCAD.Rotation(0, -90, 0)) 121 back.Placement = \ 122 FreeCAD.Placement(FreeCAD.Vector(cylinder_length_mm, 0, 0), 123 FreeCAD.Rotation(0, 90, 0)) 124 pipe2d = Part.makeRuledSurface(Part.makeCircle(outer_cylinder_radius_mm), 125 Part.makeCircle(inner_cylinder_radius_mm)) 126 pipe = pipe2d.extrude(FreeCAD.Vector(0, 0, cylinder_length_mm)) 127 else: 128 doc.recompute() 129 outer_front.Placement = \ 130 FreeCAD.Placement(FreeCAD.Vector(0, 0, 0), 131 FreeCAD.Rotation(0, -90, 0)) 132 outer_back.Placement = \ 133 FreeCAD.Placement(FreeCAD.Vector(cylinder_length_mm, 0, 0), 134 FreeCAD.Rotation(0, 90, 0)) 135 front = outer_front.Shape 136 back = outer_back.Shape 137 pipe = Part.makeCylinder(outer_cylinder_radius_mm, cylinder_length_mm) 138 pipe.Placement = FreeCAD.Placement(FreeCAD.Vector(0, 0, 0), 139 FreeCAD.Rotation(0, 90, 0)) 140 FreeCAD.closeDocument(doc.Name) 141 return pipe.generalFuse([front, back])[0] 142 143 144 # Geometry setter ------------------------------------------------------------------------------ 145 146 def set_geometry(self, *, cylinder_radius_m: Union[float, None], 147 cylinder_length_m: Union[float, None], 148 cylinder_thickness_m: Union[float, None], 149 endcap_radius_m: Union[float, None], 150 endcap_thickness_m: Union[float, None], 151 major_minor_axis_ratio: float = 2.0) -> SemiellipsoidalCapsule: 152 """Sets the physical geometry of the current `SemiellipsoidalCapsule` object. 153 154 The `major_minor_axis_ratio` parameter is used to determine the relative axis lengths of a 155 semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all 156 parameters are concretely defined, then this parameter is meaningless. 157 158 See the `SemiellipsoidalCapsule` class documentation for a description of each geometric 159 parameter. 160 """ 161 self.geometry.set(cylinder_radius=cylinder_radius_m, 162 cylinder_length=cylinder_length_m, 163 cylinder_thickness=cylinder_thickness_m, 164 endcap_radius=endcap_radius_m, 165 endcap_thickness=endcap_thickness_m) 166 if cylinder_radius_m is not None and endcap_radius_m is None: 167 self.geometry.endcap_radius = cylinder_radius_m / major_minor_axis_ratio 168 elif cylinder_radius_m is None and endcap_radius_m is not None: 169 self.geometry.cylinder_radius = endcap_radius_m * major_minor_axis_ratio 170 elif cylinder_radius_m is None and endcap_radius_m is None: 171 self.geometry.endcap_radius = self.geometry.cylinder_radius / major_minor_axis_ratio 172 return self 173 174 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 175 parameter_bounds = { 176 'cylinder_radius': (0.01, 2.0), 177 'cylinder_length': (0.01, 2.0), 178 'cylinder_thickness': (0.001, 0.05), 179 'endcap_radius': (0.01, 1.0), 180 'endcap_thickness': (0.001, 0.05) 181 } 182 return parameter_bounds.get(parameter, (0.0, 0.0)) 183 184 185 # Geometric properties ------------------------------------------------------------------------- 186 187 @property 188 def material_volume(self) -> Union[float, Expr]: 189 volume = self.displaced_volume 190 volume -= (math.pi 191 * (self.geometry.cylinder_radius - self.geometry.cylinder_thickness)**2 192 * self.geometry.cylinder_length) 193 volume -= ((4.0 * math.pi / 3.0) * 194 (self.geometry.cylinder_radius - self.geometry.cylinder_thickness)**2 * 195 (self.geometry.endcap_radius - self.geometry.endcap_thickness)) 196 return volume 197 198 @property 199 def displaced_volume(self) -> Union[float, Expr]: 200 endcap_volume = (4.0 * math.pi / 3.0) * \ 201 self.geometry.cylinder_radius**2 * self.geometry.endcap_radius 202 return (math.pi * self.geometry.cylinder_radius**2 * self.geometry.cylinder_length) + \ 203 endcap_volume 204 205 @property 206 def surface_area(self) -> Union[float, Expr]: 207 endcap_surface_area = 4.0 * math.pi * \ 208 ((((self.geometry.endcap_radius * self.geometry.cylinder_radius)**1.6 + 209 (self.geometry.endcap_radius * self.geometry.cylinder_radius)**1.6 + 210 (self.geometry.cylinder_radius * self.geometry.cylinder_radius)**1.6) / 3.0)**(1.0/1.6)) 211 return (2.0 * math.pi * self.geometry.cylinder_radius * self.geometry.cylinder_length) + \ 212 endcap_surface_area 213 214 @property 215 def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr], 216 Union[float, Expr], 217 Union[float, Expr]]: 218 return (0.5 * self.unoriented_length, 219 0.5 * self.unoriented_width, 220 0.5 * self.unoriented_height) 221 222 @property 223 def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr], 224 Union[float, Expr], 225 Union[float, Expr]]: 226 return self.unoriented_center_of_gravity 227 228 @property 229 def unoriented_length(self) -> Union[float, Expr]: 230 return (2.0 * self.geometry.endcap_radius) + self.geometry.cylinder_length 231 232 @property 233 def unoriented_width(self) -> Union[float, Expr]: 234 return 2.0 * self.geometry.cylinder_radius 235 236 @property 237 def unoriented_height(self) -> Union[float, Expr]: 238 return self.unoriented_width 239 240 @property 241 def oriented_length(self) -> Union[float, Expr]: 242 # TODO: Implement this 243 return 0 244 245 @property 246 def oriented_width(self) -> Union[float, Expr]: 247 # TODO: Implement this 248 return 0 249 250 @property 251 def oriented_height(self) -> Union[float, Expr]: 252 # TODO: Implement this 253 return 0
25class SemiellipsoidalCapsule(CompositeShape): 26 """Model representing a parameteric capsule with semiellipsoidal endcaps. 27 28 By default, the capsule is oriented such that the endcaps are aligned with the x-axis: 29 30  31 32 The minor axis of each endcap spans the open face of the endcap to its tip, while the major 33 axis spans the radius of the open face of the endcap itself. 34 35 The `geometry` of this shape includes the following parameters: 36 37 - `cylinder_radius`: Outer radius (in `m`) of the center cylindrical part of the Capsule 38 - `cylinder_length`: Length (in `m`) of the center cylindrical part of the Capsule 39 - `cylinder_thickness`: Thickness (in `m`) of the cylindrical shell of the Capsule 40 - `endcap_radius`: Radius (in `m`) of the semiellipsoidal endcaps of the Capsule 41 - `endcap_thickness`: Thickness (in `m`) of the semiellipsoidal endcaps of the Capsule 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, 50 material_density_kg_m3: Optional[float] = 1.0, 51 major_minor_axis_ratio: Optional[float] = 2.0) -> None: 52 """Initializes a parametric capsule object with semiellipsoidal endcaps. 53 54 The `major_minor_axis_ratio` parameter is used to determine the relative axis lengths of a 55 semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all 56 parameters are concretely defined, then this parameter is meaningless. 57 58 The minor axis of this shape spans the open face of the endcap to its tip, while the major 59 axis spans the radius of the open face of the endcap itself. 60 61 Parameters 62 ---------- 63 identifier : `str` 64 Unique identifying name for the object. 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 major_minor_axis_ratio : `float`, optional, default=2.0 68 Desired major-to-minor axis ratio of the semiellipsoid. 69 """ 70 super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3) 71 setattr(self.geometry, 'cylinder_radius', Symbol(self.name + '_cylinder_radius')) 72 setattr(self.geometry, 'cylinder_length', Symbol(self.name + '_cylinder_length')) 73 setattr(self.geometry, 'cylinder_thickness', Symbol(self.name + '_cylinder_thickness')) 74 setattr(self.geometry, 'endcap_radius', Symbol(self.name + '_endcap_radius')) 75 setattr(self.geometry, 'endcap_thickness', Symbol(self.name + '_endcap_thickness')) 76 self.set_geometry(cylinder_radius_m=None, 77 cylinder_length_m=None, 78 cylinder_thickness_m=None, 79 endcap_radius_m=None, 80 endcap_thickness_m=None, 81 major_minor_axis_ratio=major_minor_axis_ratio) 82 83 84 # CAD generation function ---------------------------------------------------------------------- 85 86 @staticmethod 87 def __create_cad__(params: Dict[str, float], fully_displace: bool) -> Part.Solid: 88 """Scripted CAD generation method for `SemiellipsoidalCapsule`.""" 89 doc = FreeCAD.newDocument('Temp') 90 cylinder_length_mm = 1000.0 * params['cylinder_length'] 91 outer_cylinder_radius_mm = 1000.0 * params['cylinder_radius'] 92 inner_cylinder_radius_mm = 1000.0 * (params['cylinder_radius'] - params['cylinder_thickness']) 93 outer_endcap_radius_mm = 1000.0 * params['endcap_radius'] 94 inner_endcap_radius_mm = 1000.0 * (params['endcap_radius'] - params['endcap_thickness']) 95 outer_front = doc.addObject('Part::Ellipsoid', 'FrontOuter') 96 outer_front.Radius1 = int(outer_endcap_radius_mm) 97 outer_front.Radius2 = int(outer_cylinder_radius_mm) 98 outer_front.Radius3 = int(outer_cylinder_radius_mm) 99 outer_front.Angle1 = 0.0 100 outer_back = doc.addObject('Part::Ellipsoid', 'BackOuter') 101 outer_back.Radius1 = int(outer_endcap_radius_mm) 102 outer_back.Radius2 = int(outer_cylinder_radius_mm) 103 outer_back.Radius3 = int(outer_cylinder_radius_mm) 104 outer_back.Angle1 = 0.0 105 if not fully_displace: 106 inner_front = doc.addObject('Part::Ellipsoid', 'FrontInner') 107 inner_front.Radius1 = int(inner_endcap_radius_mm) 108 inner_front.Radius2 = int(inner_cylinder_radius_mm) 109 inner_front.Radius3 = int(inner_cylinder_radius_mm) 110 inner_front.Angle1 = 0.0 111 inner_back = doc.addObject('Part::Ellipsoid', 'BackInner') 112 inner_back.Radius1 = int(inner_endcap_radius_mm) 113 inner_back.Radius2 = int(inner_cylinder_radius_mm) 114 inner_back.Radius3 = int(inner_cylinder_radius_mm) 115 inner_back.Angle1 = 0.0 116 doc.recompute() 117 front = outer_front.Shape.cut(inner_front.Shape) 118 back = outer_back.Shape.cut(inner_back.Shape) 119 front.Placement = \ 120 FreeCAD.Placement(FreeCAD.Vector(0, 0, 0), 121 FreeCAD.Rotation(0, -90, 0)) 122 back.Placement = \ 123 FreeCAD.Placement(FreeCAD.Vector(cylinder_length_mm, 0, 0), 124 FreeCAD.Rotation(0, 90, 0)) 125 pipe2d = Part.makeRuledSurface(Part.makeCircle(outer_cylinder_radius_mm), 126 Part.makeCircle(inner_cylinder_radius_mm)) 127 pipe = pipe2d.extrude(FreeCAD.Vector(0, 0, cylinder_length_mm)) 128 else: 129 doc.recompute() 130 outer_front.Placement = \ 131 FreeCAD.Placement(FreeCAD.Vector(0, 0, 0), 132 FreeCAD.Rotation(0, -90, 0)) 133 outer_back.Placement = \ 134 FreeCAD.Placement(FreeCAD.Vector(cylinder_length_mm, 0, 0), 135 FreeCAD.Rotation(0, 90, 0)) 136 front = outer_front.Shape 137 back = outer_back.Shape 138 pipe = Part.makeCylinder(outer_cylinder_radius_mm, cylinder_length_mm) 139 pipe.Placement = FreeCAD.Placement(FreeCAD.Vector(0, 0, 0), 140 FreeCAD.Rotation(0, 90, 0)) 141 FreeCAD.closeDocument(doc.Name) 142 return pipe.generalFuse([front, back])[0] 143 144 145 # Geometry setter ------------------------------------------------------------------------------ 146 147 def set_geometry(self, *, cylinder_radius_m: Union[float, None], 148 cylinder_length_m: Union[float, None], 149 cylinder_thickness_m: Union[float, None], 150 endcap_radius_m: Union[float, None], 151 endcap_thickness_m: Union[float, None], 152 major_minor_axis_ratio: float = 2.0) -> SemiellipsoidalCapsule: 153 """Sets the physical geometry of the current `SemiellipsoidalCapsule` object. 154 155 The `major_minor_axis_ratio` parameter is used to determine the relative axis lengths of a 156 semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all 157 parameters are concretely defined, then this parameter is meaningless. 158 159 See the `SemiellipsoidalCapsule` class documentation for a description of each geometric 160 parameter. 161 """ 162 self.geometry.set(cylinder_radius=cylinder_radius_m, 163 cylinder_length=cylinder_length_m, 164 cylinder_thickness=cylinder_thickness_m, 165 endcap_radius=endcap_radius_m, 166 endcap_thickness=endcap_thickness_m) 167 if cylinder_radius_m is not None and endcap_radius_m is None: 168 self.geometry.endcap_radius = cylinder_radius_m / major_minor_axis_ratio 169 elif cylinder_radius_m is None and endcap_radius_m is not None: 170 self.geometry.cylinder_radius = endcap_radius_m * major_minor_axis_ratio 171 elif cylinder_radius_m is None and endcap_radius_m is None: 172 self.geometry.endcap_radius = self.geometry.cylinder_radius / major_minor_axis_ratio 173 return self 174 175 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 176 parameter_bounds = { 177 'cylinder_radius': (0.01, 2.0), 178 'cylinder_length': (0.01, 2.0), 179 'cylinder_thickness': (0.001, 0.05), 180 'endcap_radius': (0.01, 1.0), 181 'endcap_thickness': (0.001, 0.05) 182 } 183 return parameter_bounds.get(parameter, (0.0, 0.0)) 184 185 186 # Geometric properties ------------------------------------------------------------------------- 187 188 @property 189 def material_volume(self) -> Union[float, Expr]: 190 volume = self.displaced_volume 191 volume -= (math.pi 192 * (self.geometry.cylinder_radius - self.geometry.cylinder_thickness)**2 193 * self.geometry.cylinder_length) 194 volume -= ((4.0 * math.pi / 3.0) * 195 (self.geometry.cylinder_radius - self.geometry.cylinder_thickness)**2 * 196 (self.geometry.endcap_radius - self.geometry.endcap_thickness)) 197 return volume 198 199 @property 200 def displaced_volume(self) -> Union[float, Expr]: 201 endcap_volume = (4.0 * math.pi / 3.0) * \ 202 self.geometry.cylinder_radius**2 * self.geometry.endcap_radius 203 return (math.pi * self.geometry.cylinder_radius**2 * self.geometry.cylinder_length) + \ 204 endcap_volume 205 206 @property 207 def surface_area(self) -> Union[float, Expr]: 208 endcap_surface_area = 4.0 * math.pi * \ 209 ((((self.geometry.endcap_radius * self.geometry.cylinder_radius)**1.6 + 210 (self.geometry.endcap_radius * self.geometry.cylinder_radius)**1.6 + 211 (self.geometry.cylinder_radius * self.geometry.cylinder_radius)**1.6) / 3.0)**(1.0/1.6)) 212 return (2.0 * math.pi * self.geometry.cylinder_radius * self.geometry.cylinder_length) + \ 213 endcap_surface_area 214 215 @property 216 def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr], 217 Union[float, Expr], 218 Union[float, Expr]]: 219 return (0.5 * self.unoriented_length, 220 0.5 * self.unoriented_width, 221 0.5 * self.unoriented_height) 222 223 @property 224 def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr], 225 Union[float, Expr], 226 Union[float, Expr]]: 227 return self.unoriented_center_of_gravity 228 229 @property 230 def unoriented_length(self) -> Union[float, Expr]: 231 return (2.0 * self.geometry.endcap_radius) + self.geometry.cylinder_length 232 233 @property 234 def unoriented_width(self) -> Union[float, Expr]: 235 return 2.0 * self.geometry.cylinder_radius 236 237 @property 238 def unoriented_height(self) -> Union[float, Expr]: 239 return self.unoriented_width 240 241 @property 242 def oriented_length(self) -> Union[float, Expr]: 243 # TODO: Implement this 244 return 0 245 246 @property 247 def oriented_width(self) -> Union[float, Expr]: 248 # TODO: Implement this 249 return 0 250 251 @property 252 def oriented_height(self) -> Union[float, Expr]: 253 # TODO: Implement this 254 return 0
Model representing a parameteric capsule with semiellipsoidal endcaps.
By default, the capsule is oriented such that the endcaps are aligned with the x-axis:

The minor axis of each endcap spans the open face of the endcap to its tip, while the major axis spans the radius of the open face of the endcap itself.
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_radius: Radius (inm) of the semiellipsoidal endcaps of the Capsuleendcap_thickness: Thickness (inm) of the semiellipsoidal endcaps of the Capsule
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, 50 material_density_kg_m3: Optional[float] = 1.0, 51 major_minor_axis_ratio: Optional[float] = 2.0) -> None: 52 """Initializes a parametric capsule object with semiellipsoidal endcaps. 53 54 The `major_minor_axis_ratio` parameter is used to determine the relative axis lengths of a 55 semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all 56 parameters are concretely defined, then this parameter is meaningless. 57 58 The minor axis of this shape spans the open face of the endcap to its tip, while the major 59 axis spans the radius of the open face of the endcap itself. 60 61 Parameters 62 ---------- 63 identifier : `str` 64 Unique identifying name for the object. 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 major_minor_axis_ratio : `float`, optional, default=2.0 68 Desired major-to-minor axis ratio of the semiellipsoid. 69 """ 70 super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3) 71 setattr(self.geometry, 'cylinder_radius', Symbol(self.name + '_cylinder_radius')) 72 setattr(self.geometry, 'cylinder_length', Symbol(self.name + '_cylinder_length')) 73 setattr(self.geometry, 'cylinder_thickness', Symbol(self.name + '_cylinder_thickness')) 74 setattr(self.geometry, 'endcap_radius', Symbol(self.name + '_endcap_radius')) 75 setattr(self.geometry, 'endcap_thickness', Symbol(self.name + '_endcap_thickness')) 76 self.set_geometry(cylinder_radius_m=None, 77 cylinder_length_m=None, 78 cylinder_thickness_m=None, 79 endcap_radius_m=None, 80 endcap_thickness_m=None, 81 major_minor_axis_ratio=major_minor_axis_ratio)
Initializes a parametric capsule object with semiellipsoidal endcaps.
The major_minor_axis_ratio parameter is used to determine the relative axis lengths of a
semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all
parameters are concretely defined, then this parameter is meaningless.
The minor axis of this shape spans the open face of the endcap to its tip, while the major axis spans the radius of the open face of the endcap itself.
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. - major_minor_axis_ratio (
float, optional, default=2.0): Desired major-to-minor axis ratio of the semiellipsoid.
147 def set_geometry(self, *, cylinder_radius_m: Union[float, None], 148 cylinder_length_m: Union[float, None], 149 cylinder_thickness_m: Union[float, None], 150 endcap_radius_m: Union[float, None], 151 endcap_thickness_m: Union[float, None], 152 major_minor_axis_ratio: float = 2.0) -> SemiellipsoidalCapsule: 153 """Sets the physical geometry of the current `SemiellipsoidalCapsule` object. 154 155 The `major_minor_axis_ratio` parameter is used to determine the relative axis lengths of a 156 semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all 157 parameters are concretely defined, then this parameter is meaningless. 158 159 See the `SemiellipsoidalCapsule` class documentation for a description of each geometric 160 parameter. 161 """ 162 self.geometry.set(cylinder_radius=cylinder_radius_m, 163 cylinder_length=cylinder_length_m, 164 cylinder_thickness=cylinder_thickness_m, 165 endcap_radius=endcap_radius_m, 166 endcap_thickness=endcap_thickness_m) 167 if cylinder_radius_m is not None and endcap_radius_m is None: 168 self.geometry.endcap_radius = cylinder_radius_m / major_minor_axis_ratio 169 elif cylinder_radius_m is None and endcap_radius_m is not None: 170 self.geometry.cylinder_radius = endcap_radius_m * major_minor_axis_ratio 171 elif cylinder_radius_m is None and endcap_radius_m is None: 172 self.geometry.endcap_radius = self.geometry.cylinder_radius / major_minor_axis_ratio 173 return self
Sets the physical geometry of the current SemiellipsoidalCapsule object.
The major_minor_axis_ratio parameter is used to determine the relative axis lengths of a
semiellipsoidal endcap when one or more of its geometric parameters are symbolic. If all
parameters are concretely defined, then this parameter is meaningless.
See the SemiellipsoidalCapsule class documentation for a description of each geometric
parameter.
175 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 176 parameter_bounds = { 177 'cylinder_radius': (0.01, 2.0), 178 'cylinder_length': (0.01, 2.0), 179 'cylinder_thickness': (0.001, 0.05), 180 'endcap_radius': (0.01, 1.0), 181 'endcap_thickness': (0.001, 0.05) 182 } 183 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