symcad.parts.endcaps.ConicalFrustrum
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 Part 19from typing import Dict, Optional, Tuple, Union 20from sympy import Expr, Symbol, sqrt, atan2, sin, tan 21from . import EndcapShape 22import math 23 24class ConicalFrustrum(EndcapShape): 25 """Model representing a hollow, parametric, conical frustrum. 26 27 By default, the frustrum is oriented such that its base is perpendicular to the z-axis: 28 29  30 31 The `geometry` of this shape includes the following parameters: 32 33 - `bottom_radius`: Radius (in `m`) of the base of the ConicalFrustrum 34 (must be larger than `top_radius`) 35 - `top_radius`: Radius (in `m`) of the tip of the ConicalFrustrum 36 - `height`: Height (in `m`) of the ConicalFrustrum from base to tip 37 - `thickness`: Thickness (in `m`) of the shell of the ConicalFrustrum 38 39 Note that the above dimensions should be interpreted as if the ConicalFrustrum is unrotated. 40 In other words, any shape rotation takes place *after* the ConicalFrustrum dimensions have been 41 specified. 42 """ 43 44 # Constructor ---------------------------------------------------------------------------------- 45 46 def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None: 47 """Initializes a hollow, parametric, conical frustrum object. 48 49 Parameters 50 ---------- 51 identifier : `str` 52 Unique identifying name for the object. 53 material_density_kg_m3 : `float`, optional, default=1.0 54 Uniform material density in `kg/m^3` to be used in mass property calculations. 55 """ 56 super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3) 57 setattr(self.geometry, 'bottom_radius', Symbol(self.name + '_bottom_radius')) 58 setattr(self.geometry, 'top_radius', Symbol(self.name + '_top_radius')) 59 setattr(self.geometry, 'thickness', Symbol(self.name + '_thickness')) 60 setattr(self.geometry, 'height', Symbol(self.name + '_height')) 61 62 63 # CAD generation function ---------------------------------------------------------------------- 64 65 @staticmethod 66 def __create_cad__(params: Dict[str, float], fully_displace: bool) -> Part.Solid: 67 """Scripted CAD generation method for a `ConicalFrustrum`.""" 68 height_angle = math.atan2(params['height'], params['bottom_radius'] - params['top_radius']) 69 thickness_mm = 1000.0 * params['thickness'] 70 outer_height_mm = 1000.0 * params['height'] 71 outer_bottom_radius_mm = 1000.0 * params['bottom_radius'] 72 outer_top_radius_mm = 1000.0 * params['top_radius'] 73 inner_height_mm = outer_height_mm - thickness_mm 74 inner_bottom_radius_mm = outer_bottom_radius_mm - (thickness_mm / math.sin(height_angle)) 75 inner_top_radius_mm = inner_bottom_radius_mm - (inner_height_mm / math.tan(height_angle)) 76 outer = Part.makeCone(outer_bottom_radius_mm, outer_top_radius_mm, outer_height_mm) 77 if not fully_displace: 78 inner = Part.makeCone(inner_bottom_radius_mm, inner_top_radius_mm, inner_height_mm) 79 return outer.cut(inner) 80 else: 81 return outer 82 83 84 # Geometry setter ------------------------------------------------------------------------------ 85 86 def set_geometry(self, *, bottom_radius_m: Union[float, None], 87 top_radius_m: Union[float, None], 88 height_m: Union[float, None], 89 thickness_m: Union[float, None]) -> ConicalFrustrum: 90 """Sets the physical geometry of the current `ConicalFrustrum` object. 91 92 See the `ConicalFrustrum` class documentation for a description of each geometric parameter. 93 """ 94 self.geometry.set(bottom_radius=bottom_radius_m, 95 top_radius=top_radius_m, 96 height=height_m, 97 thickness=thickness_m) 98 return self 99 100 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 101 parameter_bounds = { 102 'bottom_radius': (0.0, 2.0), 103 'top_radius': (0.0, 2.0), 104 'thickness': (0.0, 0.05), 105 'height': (0.0, 2.0) 106 } 107 return parameter_bounds.get(parameter, (0.0, 0.0)) 108 109 110 # Geometric properties ------------------------------------------------------------------------- 111 112 @property 113 def material_volume(self) -> Union[float, Expr]: 114 volume = self.displaced_volume 115 height_angle = atan2(self.geometry.height, 116 self.geometry.bottom_radius - self.geometry.top_radius) 117 inner_height = self.geometry.height - self.geometry.thickness 118 inner_bottom_radius = self.geometry.bottom_radius - \ 119 (self.geometry.thickness / sin(height_angle)) 120 inner_top_radius = inner_bottom_radius - (inner_height / tan(height_angle)) 121 volume -= ((inner_height * math.pi / 3.0) * \ 122 (inner_bottom_radius**2 + inner_top_radius**2 + 123 (inner_bottom_radius * inner_top_radius))) 124 return volume 125 126 @property 127 def displaced_volume(self) -> Union[float, Expr]: 128 return (self.geometry.height * math.pi / 3.0) * \ 129 (self.geometry.bottom_radius**2 + self.geometry.top_radius**2 + 130 (self.geometry.bottom_radius * self.geometry.top_radius)) 131 132 @property 133 def surface_area(self) -> Union[float, Expr]: 134 return (math.pi * self.geometry.top_radius**2) + \ 135 (math.pi * (self.geometry.bottom_radius + self.geometry.top_radius) * 136 sqrt(self.geometry.height**2 + 137 (self.geometry.bottom_radius - self.geometry.top_radius)**2)) 138 139 @property 140 def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr], 141 Union[float, Expr], 142 Union[float, Expr]]: 143 return (self.geometry.bottom_radius, 144 self.geometry.bottom_radius, 145 (self.geometry.height * 146 (self.geometry.bottom_radius + 147 (0.5 * self.geometry.top_radius**2) + 148 (2.0 * self.geometry.top_radius))) / 149 (3.0 * (self.geometry.bottom_radius + self.geometry.top_radius))) 150 151 @property 152 def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr], 153 Union[float, Expr], 154 Union[float, Expr]]: 155 return (self.geometry.bottom_radius, 156 self.geometry.bottom_radius, 157 (self.geometry.height * (self.geometry.bottom_radius**2 + 158 (2.0 * self.geometry.bottom_radius * self.geometry.top_radius) + 159 (3.0 * self.geometry.top_radius**2))) / 160 (4.0 * (self.geometry.bottom_radius**2 + 161 (self.geometry.bottom_radius * self.geometry.top_radius) + 162 self.geometry.top_radius**2))) 163 164 @property 165 def unoriented_length(self) -> Union[float, Expr]: 166 return 2.0 * self.geometry.bottom_radius 167 168 @property 169 def unoriented_width(self) -> Union[float, Expr]: 170 return self.unoriented_length 171 172 @property 173 def unoriented_height(self) -> Union[float, Expr]: 174 return self.geometry.height 175 176 @property 177 def oriented_length(self) -> Union[float, Expr]: 178 # TODO: Implement this 179 return 0 180 181 @property 182 def oriented_width(self) -> Union[float, Expr]: 183 # TODO: Implement this 184 return 0 185 186 @property 187 def oriented_height(self) -> Union[float, Expr]: 188 # TODO: Implement this 189 return 0
25class ConicalFrustrum(EndcapShape): 26 """Model representing a hollow, parametric, conical frustrum. 27 28 By default, the frustrum is oriented such that its base is perpendicular to the z-axis: 29 30  31 32 The `geometry` of this shape includes the following parameters: 33 34 - `bottom_radius`: Radius (in `m`) of the base of the ConicalFrustrum 35 (must be larger than `top_radius`) 36 - `top_radius`: Radius (in `m`) of the tip of the ConicalFrustrum 37 - `height`: Height (in `m`) of the ConicalFrustrum from base to tip 38 - `thickness`: Thickness (in `m`) of the shell of the ConicalFrustrum 39 40 Note that the above dimensions should be interpreted as if the ConicalFrustrum is unrotated. 41 In other words, any shape rotation takes place *after* the ConicalFrustrum dimensions have been 42 specified. 43 """ 44 45 # Constructor ---------------------------------------------------------------------------------- 46 47 def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None: 48 """Initializes a hollow, parametric, conical frustrum object. 49 50 Parameters 51 ---------- 52 identifier : `str` 53 Unique identifying name for the object. 54 material_density_kg_m3 : `float`, optional, default=1.0 55 Uniform material density in `kg/m^3` to be used in mass property calculations. 56 """ 57 super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3) 58 setattr(self.geometry, 'bottom_radius', Symbol(self.name + '_bottom_radius')) 59 setattr(self.geometry, 'top_radius', Symbol(self.name + '_top_radius')) 60 setattr(self.geometry, 'thickness', Symbol(self.name + '_thickness')) 61 setattr(self.geometry, 'height', Symbol(self.name + '_height')) 62 63 64 # CAD generation function ---------------------------------------------------------------------- 65 66 @staticmethod 67 def __create_cad__(params: Dict[str, float], fully_displace: bool) -> Part.Solid: 68 """Scripted CAD generation method for a `ConicalFrustrum`.""" 69 height_angle = math.atan2(params['height'], params['bottom_radius'] - params['top_radius']) 70 thickness_mm = 1000.0 * params['thickness'] 71 outer_height_mm = 1000.0 * params['height'] 72 outer_bottom_radius_mm = 1000.0 * params['bottom_radius'] 73 outer_top_radius_mm = 1000.0 * params['top_radius'] 74 inner_height_mm = outer_height_mm - thickness_mm 75 inner_bottom_radius_mm = outer_bottom_radius_mm - (thickness_mm / math.sin(height_angle)) 76 inner_top_radius_mm = inner_bottom_radius_mm - (inner_height_mm / math.tan(height_angle)) 77 outer = Part.makeCone(outer_bottom_radius_mm, outer_top_radius_mm, outer_height_mm) 78 if not fully_displace: 79 inner = Part.makeCone(inner_bottom_radius_mm, inner_top_radius_mm, inner_height_mm) 80 return outer.cut(inner) 81 else: 82 return outer 83 84 85 # Geometry setter ------------------------------------------------------------------------------ 86 87 def set_geometry(self, *, bottom_radius_m: Union[float, None], 88 top_radius_m: Union[float, None], 89 height_m: Union[float, None], 90 thickness_m: Union[float, None]) -> ConicalFrustrum: 91 """Sets the physical geometry of the current `ConicalFrustrum` object. 92 93 See the `ConicalFrustrum` class documentation for a description of each geometric parameter. 94 """ 95 self.geometry.set(bottom_radius=bottom_radius_m, 96 top_radius=top_radius_m, 97 height=height_m, 98 thickness=thickness_m) 99 return self 100 101 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 102 parameter_bounds = { 103 'bottom_radius': (0.0, 2.0), 104 'top_radius': (0.0, 2.0), 105 'thickness': (0.0, 0.05), 106 'height': (0.0, 2.0) 107 } 108 return parameter_bounds.get(parameter, (0.0, 0.0)) 109 110 111 # Geometric properties ------------------------------------------------------------------------- 112 113 @property 114 def material_volume(self) -> Union[float, Expr]: 115 volume = self.displaced_volume 116 height_angle = atan2(self.geometry.height, 117 self.geometry.bottom_radius - self.geometry.top_radius) 118 inner_height = self.geometry.height - self.geometry.thickness 119 inner_bottom_radius = self.geometry.bottom_radius - \ 120 (self.geometry.thickness / sin(height_angle)) 121 inner_top_radius = inner_bottom_radius - (inner_height / tan(height_angle)) 122 volume -= ((inner_height * math.pi / 3.0) * \ 123 (inner_bottom_radius**2 + inner_top_radius**2 + 124 (inner_bottom_radius * inner_top_radius))) 125 return volume 126 127 @property 128 def displaced_volume(self) -> Union[float, Expr]: 129 return (self.geometry.height * math.pi / 3.0) * \ 130 (self.geometry.bottom_radius**2 + self.geometry.top_radius**2 + 131 (self.geometry.bottom_radius * self.geometry.top_radius)) 132 133 @property 134 def surface_area(self) -> Union[float, Expr]: 135 return (math.pi * self.geometry.top_radius**2) + \ 136 (math.pi * (self.geometry.bottom_radius + self.geometry.top_radius) * 137 sqrt(self.geometry.height**2 + 138 (self.geometry.bottom_radius - self.geometry.top_radius)**2)) 139 140 @property 141 def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr], 142 Union[float, Expr], 143 Union[float, Expr]]: 144 return (self.geometry.bottom_radius, 145 self.geometry.bottom_radius, 146 (self.geometry.height * 147 (self.geometry.bottom_radius + 148 (0.5 * self.geometry.top_radius**2) + 149 (2.0 * self.geometry.top_radius))) / 150 (3.0 * (self.geometry.bottom_radius + self.geometry.top_radius))) 151 152 @property 153 def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr], 154 Union[float, Expr], 155 Union[float, Expr]]: 156 return (self.geometry.bottom_radius, 157 self.geometry.bottom_radius, 158 (self.geometry.height * (self.geometry.bottom_radius**2 + 159 (2.0 * self.geometry.bottom_radius * self.geometry.top_radius) + 160 (3.0 * self.geometry.top_radius**2))) / 161 (4.0 * (self.geometry.bottom_radius**2 + 162 (self.geometry.bottom_radius * self.geometry.top_radius) + 163 self.geometry.top_radius**2))) 164 165 @property 166 def unoriented_length(self) -> Union[float, Expr]: 167 return 2.0 * self.geometry.bottom_radius 168 169 @property 170 def unoriented_width(self) -> Union[float, Expr]: 171 return self.unoriented_length 172 173 @property 174 def unoriented_height(self) -> Union[float, Expr]: 175 return self.geometry.height 176 177 @property 178 def oriented_length(self) -> Union[float, Expr]: 179 # TODO: Implement this 180 return 0 181 182 @property 183 def oriented_width(self) -> Union[float, Expr]: 184 # TODO: Implement this 185 return 0 186 187 @property 188 def oriented_height(self) -> Union[float, Expr]: 189 # TODO: Implement this 190 return 0
Model representing a hollow, parametric, conical frustrum.
By default, the frustrum is oriented such that its base is perpendicular to the z-axis:

The geometry of this shape includes the following parameters:
bottom_radius: Radius (inm) of the base of the ConicalFrustrum (must be larger thantop_radius)top_radius: Radius (inm) of the tip of the ConicalFrustrumheight: Height (inm) of the ConicalFrustrum from base to tipthickness: Thickness (inm) of the shell of the ConicalFrustrum
Note that the above dimensions should be interpreted as if the ConicalFrustrum is unrotated. In other words, any shape rotation takes place after the ConicalFrustrum dimensions have been specified.
47 def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None: 48 """Initializes a hollow, parametric, conical frustrum object. 49 50 Parameters 51 ---------- 52 identifier : `str` 53 Unique identifying name for the object. 54 material_density_kg_m3 : `float`, optional, default=1.0 55 Uniform material density in `kg/m^3` to be used in mass property calculations. 56 """ 57 super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3) 58 setattr(self.geometry, 'bottom_radius', Symbol(self.name + '_bottom_radius')) 59 setattr(self.geometry, 'top_radius', Symbol(self.name + '_top_radius')) 60 setattr(self.geometry, 'thickness', Symbol(self.name + '_thickness')) 61 setattr(self.geometry, 'height', Symbol(self.name + '_height'))
Initializes a hollow, parametric, conical frustrum object.
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.
87 def set_geometry(self, *, bottom_radius_m: Union[float, None], 88 top_radius_m: Union[float, None], 89 height_m: Union[float, None], 90 thickness_m: Union[float, None]) -> ConicalFrustrum: 91 """Sets the physical geometry of the current `ConicalFrustrum` object. 92 93 See the `ConicalFrustrum` class documentation for a description of each geometric parameter. 94 """ 95 self.geometry.set(bottom_radius=bottom_radius_m, 96 top_radius=top_radius_m, 97 height=height_m, 98 thickness=thickness_m) 99 return self
Sets the physical geometry of the current ConicalFrustrum object.
See the ConicalFrustrum class documentation for a description of each geometric parameter.
101 def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]: 102 parameter_bounds = { 103 'bottom_radius': (0.0, 2.0), 104 'top_radius': (0.0, 2.0), 105 'thickness': (0.0, 0.05), 106 'height': (0.0, 2.0) 107 } 108 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