from __future__ import annotations
import json
import numpy as np
from stl.mesh import Mesh
import gcs
# The assumed material density
MATERIAL_DENSITY = 0.0012 # g/mm^3
[docs]class GCS:
"""The generalized cylindrical shell (GCS) class.
"""
def __init__(self,
c4_base: float,
c8_base: float,
c4_top: float,
c8_top: float,
twist_linear: float,
twist_amplitude: float,
twist_cycles: float,
perimeter_ratio: float,
height: float,
mass: float,
thickness: float,
n_steps: int = 100,
d_theta: float = 0.01,
triangulate_faces: bool = True) -> None:
"""Initialize ``GCS``.
Parameters
----------
c4_base : float
The parameter controlling the size and shape of the base 4-lobe feature.
c8_base : float
The parameter controlling the size and shape of the base 8-lobe feature.
c4_top : float
The parameter controlling the size and shape of the top 4-lobe feature.
c8_top : float
The parameter controlling the size and shape of the top 8-lobe feature.
twist_linear : float
The rotation (rad) of the top. This creates a linear twist between the base and top.
twist_amplitude : float
The amplitude (rad) of the oscillating twist between the base and top.
twist_cycles : float
The number of cycles of the oscillating twist between the base and top.
perimeter_ratio : float
The ratio between the top and base perimeters.
height : float
The height (mm).
mass : float
The mass (g).
thickness : float
The wall thickness (mm).
n_steps : int (default=100)
The number of height discretization steps.
d_theta : float (default=0.01)
The angular discretization step size.
triangulate_faces : bool (default=`True`)
Set to `True` to triangulate the top and bottom faces.
Examples
--------
>>> shape = gcs.GCS(c4_base=0.5, c8_base=0, ...)
"""
self._c4_base = c4_base
self._c8_base = c8_base
self._c4_top = c4_top
self._c8_top = c8_top
self._twist_linear = twist_linear
self._twist_amplitude = twist_amplitude
self._twist_cycles = twist_cycles
self._perimeter_ratio = perimeter_ratio
self._height = height
self._mass = mass
self._thickness = thickness
self._n_steps = n_steps
self._theta_step = d_theta
self._triangulate_faces = triangulate_faces
self._vertices = None
self._faces = None
@property
def parameters(self) -> dict:
"""The GCS parameters.
"""
return {
'c4_base': self._c4_base,
'c8_base': self._c8_base,
'c4_top': self._c4_top,
'c8_top': self._c8_top,
'twist_linear': self._twist_linear,
'twist_amplitude': self._twist_amplitude,
'twist_cycles': self._twist_cycles,
'perimeter_ratio': self._perimeter_ratio,
'height': self._height,
'mass': self._mass,
'thickness': self._thickness,
'n_steps': self._n_steps,
'd_theta': self._theta_step,
'triangulate_faces': self._triangulate_faces,
}
@property
def valid_base_perimeter(self) -> bool:
"""`True` if the base perimeter is valid.
Refer to ``gcs.verify.verify_base_perimeter`` for full documentation.
"""
return gcs.verify.verify_base_perimeter(shape=self)
@property
def valid_radius(self) -> bool:
"""`True` if the radii are valid.
Refer to ``gcs.verify.verify_radius`` for full documentation.
"""
return gcs.verify.verify_radius(shape=self)
@property
def valid(self) -> bool:
"""`True` if the GCS is valid.
Refer to ``gcs.verify.verify`` for full documentation.
"""
valid = gcs.verify.verify(shape=self)
return valid
@property
def base_perimeter(self) -> float:
"""The base perimeter (mm).
"""
perimeter = (2 * self._mass) / \
(MATERIAL_DENSITY * self._height * self._thickness * (1 + self._perimeter_ratio))
return perimeter
@property
def top_perimeter(self) -> float:
"""The top perimeter (mm).
"""
perimeter = (2 * self._mass * self._perimeter_ratio) / \
(MATERIAL_DENSITY * self._height * self._thickness * (1 + self._perimeter_ratio))
return perimeter
@property
def vertices(self) -> np.ndarray:
"""The vertices.
Refer to ``gcs.discretize`` for full documentation.
"""
if self._vertices is None:
self._vertices = gcs.discretize(shape=self)
return self._vertices
@property
def faces(self) -> np.ndarray:
"""The faces.
Refer to ``gcs.triangulate`` for full documentation.
"""
if self._faces is None:
self._faces = gcs.triangulate(shape=self)
return self._faces
@property
def mesh(self) -> Mesh:
"""The mesh.
References
----------
.. [1] https://github.com/wolph/numpy-stl/tree/develop#creating-mesh-objects-from-a-list-of-vertices-and-faces
"""
vertices = self.vertices
faces = self.faces
mesh = Mesh(np.zeros(faces.shape[0], dtype=Mesh.dtype))
for idx, face in enumerate(faces):
for dim in range(3):
mesh.vectors[idx][dim] = vertices[face[dim], :]
return mesh
def __str__(self):
output = f'{super().__str__()}: ' + \
json.dumps(obj=self.parameters, indent=2)
return output
[docs]class Cylinder(GCS):
"""Simple GCS cylinder.
"""
def __init__(self,
height: float,
mass: float,
thickness: float,
n_steps: int = 100,
d_theta: float = 0.01,
triangulate_faces: bool = True) -> None:
"""Initialize ``Cylinder``.
Parameters
----------
height : float
The height (mm).
mass : float
The mass (g).
thickness : float
The wall thickness (mm).
n_steps : int (default=100)
The number of height discretization steps.
d_theta : float (default=0.01)
The angular discretization step size.
triangulate_faces : bool (default=`True`)
Set to `True` to triangulate the top and bottom faces.
Examples
--------
>>> shape = gcs.Cylinder(height=10, mass=4, ...)
"""
super().__init__(c4_base=0,
c8_base=0,
c4_top=0,
c8_top=0,
twist_linear=0,
twist_amplitude=0,
twist_cycles=0,
perimeter_ratio=1,
height=height,
mass=mass,
thickness=thickness,
n_steps=n_steps,
d_theta=d_theta,
triangulate_faces=triangulate_faces)