symcad.parts.generic.Cone

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

Model representing a generic parameteric cone.

By default, the cone is oriented such that its base is perpendicular to the z-axis:

Cone

The geometry of this shape includes the following parameters:

  • bottom_radius: Radius (in m) of the base of the Cone (must be larger than top_radius)
  • top_radius: Radius (in m) of the truncated Cone tip (may be 0 to create a full Cone)
  • height: Height (in m) of the Cone from base to tip

If a non-truncated cone is desired, the top_radius parameter may be set to 0.

Note that the above dimensions should be interpreted as if the Cone is unrotated. In other words, any shape rotation takes place after the Cone dimensions have been specified.

Cone(identifier: str, material_density_kg_m3: Optional[float] = 1.0)
46   def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None:
47      """Initializes a generic parametric cone 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, 'height', Symbol(self.name + '_height'))

Initializes a generic parametric cone object.

Parameters
  • identifier (str): Unique identifying name for the object.
  • material_density_kg_m3 (float, optional, default=1.0): Uniform material density in kg/m^3 to be used in mass property calculations.
def set_geometry( self, *, bottom_radius_m: Optional[float], top_radius_m: Optional[float], height_m: Optional[float]) -> Cone:
75   def set_geometry(self, *, bottom_radius_m: Union[float, None],
76                             top_radius_m: Union[float, None],
77                             height_m: Union[float, None]) -> Cone:
78      """Sets the physical geometry of the current `Cone` object.
79
80      See the `Cone` class documentation for a description of each geometric parameter.
81      """
82      self.geometry.set(bottom_radius=bottom_radius_m,
83                        top_radius=top_radius_m,
84                        height=height_m)
85      return self

Sets the physical geometry of the current Cone object.

See the Cone class documentation for a description of each geometric parameter.

def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
87   def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
88      parameter_bounds = {
89         'bottom_radius': (0.0, 2.0),
90         'top_radius': (0.0, 2.0),
91         'height': (0.0, 2.0)
92      }
93      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 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).