11""" Provides the ``Cylinder`` class."""
22
33from beartype import beartype as check_input_types
4- from beartype .typing import Optional , Union
4+ from beartype .typing import Union
55import numpy as np
6- from pint import Unit
6+ from pint import Quantity
77
8- from ansys .geometry .core .math import Point3D , UnitVector3D , Vector3D
9- from ansys .geometry .core .misc import UNIT_LENGTH , UNITS , check_pint_unit_compatibility
8+ from ansys .geometry .core .math import UNITVECTOR3D_X , UNITVECTOR3D_Z , Point3D , UnitVector3D , Vector3D
9+ from ansys .geometry .core .misc import Distance
10+ from ansys .geometry .core .primitives .circle import Circle
11+ from ansys .geometry .core .primitives .line import Line
12+ from ansys .geometry .core .primitives .surface_evaluation import ParamUV , SurfaceEvaluation
1013from ansys .geometry .core .typing import Real , RealSequence
1114
1215
@@ -18,101 +21,188 @@ class Cylinder:
1821 ----------
1922 origin : Union[~numpy.ndarray, RealSequence, Point3D]
2023 Origin of the cylinder.
21- direction_x : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
22- X-plane direction.
23- direction_y : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
24- Y-plane direction.
25- radius : Real
24+ radius : Union[Quantity, Distance, Real]
2625 Radius of the cylinder.
27- height : Real
28- Height of the cylinder .
29- unit : Unit, default: UNIT_LENGTH
30- Units for defining the radius and height .
26+ reference : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
27+ X-axis direction .
28+ axis : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
29+ Z-axis direction .
3130 """
3231
3332 @check_input_types
3433 def __init__ (
3534 self ,
3635 origin : Union [np .ndarray , RealSequence , Point3D ],
37- direction_x : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ],
38- direction_y : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ],
39- radius : Real ,
40- height : Real ,
41- unit : Optional [Unit ] = UNIT_LENGTH ,
36+ radius : Union [Quantity , Distance , Real ],
37+ reference : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ] = UNITVECTOR3D_X ,
38+ axis : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ] = UNITVECTOR3D_Z ,
4239 ):
4340 """Constructor method for the ``Cylinder`` class."""
4441
45- check_pint_unit_compatibility (unit , UNIT_LENGTH )
46- self ._unit = unit
47- _ , self ._base_unit = UNITS .get_base_units (unit )
48-
4942 self ._origin = Point3D (origin ) if not isinstance (origin , Point3D ) else origin
50- self ._direction_x = (
51- UnitVector3D (direction_x ) if not isinstance (direction_x , UnitVector3D ) else direction_x
52- )
53- self ._direction_y = (
54- UnitVector3D (direction_y ) if not isinstance (direction_y , UnitVector3D ) else direction_y
43+ self ._reference = (
44+ UnitVector3D (reference ) if not isinstance (reference , UnitVector3D ) else reference
5545 )
46+ self ._axis = UnitVector3D (axis ) if not isinstance (axis , UnitVector3D ) else axis
47+ self ._axis = UnitVector3D (axis ) if not isinstance (axis , UnitVector3D ) else axis
48+ if not self ._reference .is_perpendicular_to (self ._axis ):
49+ raise ValueError ("Cylinder reference (dir_x) and axis (dir_z) must be perpendicular." )
5650
57- # Store values in base unit
58- self ._radius = UNITS . convert ( radius , self . _unit , self . _base_unit )
59- self . _height = UNITS . convert ( height , self . _unit , self . _base_unit )
51+ self . _radius = radius if isinstance ( radius , Distance ) else Distance ( radius )
52+ if self ._radius . value <= 0 :
53+ raise ValueError ( "Radius must be a real positive value." )
6054
6155 @property
6256 def origin (self ) -> Point3D :
6357 """Origin of the cylinder."""
6458 return self ._origin
6559
66- @origin .setter
67- @check_input_types
68- def origin (self , origin : Point3D ) -> None :
69- self ._origin = origin
70-
7160 @property
72- def radius (self ) -> Real :
61+ def radius (self ) -> Quantity :
7362 """Radius of the cylinder."""
74- return UNITS .convert (self ._radius , self ._base_unit , self ._unit )
75-
76- @radius .setter
77- @check_input_types
78- def radius (self , radius : Real ) -> None :
79- """Set the radius of the cylinder."""
80- self ._radius = UNITS .convert (radius , self ._unit , self ._base_unit )
63+ return self ._radius .value
8164
8265 @property
83- def height (self ) -> Real :
84- """Height of the cylinder."""
85- return UNITS . convert ( self ._height , self . _base_unit , self . _unit )
66+ def dir_x (self ) -> UnitVector3D :
67+ """X-direction of the cylinder."""
68+ return self ._reference
8669
87- @height .setter
88- @check_input_types
89- def height (self , height : Real ) -> None :
90- """Set the height of the cylinder."""
91- self ._height = UNITS .convert (height , self ._unit , self ._base_unit )
70+ @property
71+ def dir_y (self ) -> UnitVector3D :
72+ """Y-direction of the cylinder."""
73+ return self .dir_z .cross (self .dir_x )
9274
9375 @property
94- def unit (self ) -> Unit :
95- """Unit of the radius and height ."""
96- return self ._unit
76+ def dir_z (self ) -> UnitVector3D :
77+ """Z-direction of the cylinder ."""
78+ return self ._axis
9779
98- @unit .setter
99- @check_input_types
100- def unit (self , unit : Unit ) -> None :
101- """Set the unit of the object."""
102- check_pint_unit_compatibility (unit , UNIT_LENGTH )
103- self ._unit = unit
80+ def surface_area (self , height : Union [Quantity , Distance , Real ]) -> Quantity :
81+ """Surface area of the cylinder."""
82+ height = height if isinstance (height , Distance ) else Distance (height )
83+ if height .value <= 0 :
84+ raise ValueError ("Height must be a real positive value." )
85+
86+ return 2 * np .pi * self .radius * height .value + 2 * np .pi * self .radius ** 2
87+
88+ def volume (self , height : Union [Quantity , Distance , Real ]) -> Quantity :
89+ """Volume of the cylinder."""
90+ height = height if isinstance (height , Distance ) else Distance (height )
91+ if height .value <= 0 :
92+ raise ValueError ("Height must be a real positive value." )
93+
94+ return np .pi * self .radius ** 2 * height .value
10495
10596 @check_input_types
106- def __eq__ (self , other : object ) -> bool :
97+ def __eq__ (self , other : "Cylinder" ) -> bool :
10798 """Equals operator for the ``Cylinder`` class."""
10899 return (
109- self ._origin == other .origin
110- and self ._radius == other .radius
111- and self ._height == other .height
112- and self ._direction_x == other ._direction_x
113- and self ._direction_y == other ._direction_y
100+ self ._origin == other ._origin
101+ and self ._radius == other ._radius
102+ and self ._reference == other ._reference
103+ and self ._axis == other ._axis
104+ )
105+
106+ def evaluate (self , parameter : ParamUV ) -> "CylinderEvaluation" :
107+ """Evaluate the cylinder at the given parameters."""
108+ return CylinderEvaluation (self , parameter )
109+
110+ def project_point (self , point : Point3D ) -> "CylinderEvaluation" :
111+ """Project a point onto the cylinder and return its ``CylinderEvaluation``."""
112+ circle = Circle (self .origin , self .radius , self .dir_x , self .dir_z )
113+ u = circle .project_point (point ).parameter
114+
115+ line = Line (self .origin , self .dir_z )
116+ v = line .project_point (point ).parameter
117+
118+ return CylinderEvaluation (self , ParamUV (u , v ))
119+
120+
121+ class CylinderEvaluation (SurfaceEvaluation ):
122+ """
123+ Provides ``Cylinder`` evaluation at certain parameters.
124+
125+ Parameters
126+ ----------
127+ cylinder: ~ansys.geometry.core.primitives.cylinder.Cylinder
128+ The ``Cylinder`` object to be evaluated.
129+ parameter: ParamUV
130+ The parameters (u, v) at which the ``Cylinder`` evaluation is requested.
131+ """
132+
133+ def __init__ (self , cylinder : Cylinder , parameter : ParamUV ) -> None :
134+ """``CylinderEvaluation`` class constructor."""
135+ self ._cylinder = cylinder
136+ self ._parameter = parameter
137+
138+ @property
139+ def cylinder (self ) -> Cylinder :
140+ """The cylinder being evaluated."""
141+ return self ._cylinder
142+
143+ @property
144+ def parameter (self ) -> ParamUV :
145+ """The parameter that the evaluation is based upon."""
146+ return self ._parameter
147+
148+ def position (self ) -> Point3D :
149+ """The point on the cylinder, based on the evaluation."""
150+ return (
151+ self .cylinder .origin
152+ + self .cylinder .radius .m * self .__cylinder_normal ()
153+ + self .parameter .v * self .cylinder .dir_z
154+ )
155+
156+ def normal (self ) -> UnitVector3D :
157+ """The normal to the surface."""
158+ return UnitVector3D (self .__cylinder_normal ())
159+
160+ def __cylinder_normal (self ) -> Vector3D :
161+ """The normal to the cylinder."""
162+ return (
163+ np .cos (self .parameter .u ) * self .cylinder .dir_x
164+ + np .sin (self .parameter .u ) * self .cylinder .dir_y
114165 )
115166
116- def __ne__ (self , other ) -> bool :
117- """Not equals operator for the ``Cylinder`` class."""
118- return not self == other
167+ def __cylinder_tangent (self ) -> Vector3D :
168+ """The tangent to the cylinder."""
169+ return (
170+ - np .sin (self .parameter .u ) * self .cylinder .dir_x
171+ + np .cos (self .parameter .u ) * self .cylinder .dir_y
172+ )
173+
174+ def u_derivative (self ) -> Vector3D :
175+ """The first derivative with respect to u."""
176+ return self .cylinder .radius .m * self .__cylinder_tangent ()
177+
178+ def v_derivative (self ) -> Vector3D :
179+ """The first derivative with respect to v."""
180+ return self .cylinder .dir_z
181+
182+ def uu_derivative (self ) -> Vector3D :
183+ """The second derivative with respect to u."""
184+ return - self .cylinder .radius .m * self .__cylinder_normal ()
185+
186+ def uv_derivative (self ) -> Vector3D :
187+ """The second derivative with respect to u and v."""
188+ return Vector3D ([0 , 0 , 0 ])
189+
190+ def vv_derivative (self ) -> Vector3D :
191+ """The second derivative with respect to v."""
192+ return Vector3D ([0 , 0 , 0 ])
193+
194+ def min_curvature (self ) -> Real :
195+ """The minimum curvature."""
196+ return 0
197+
198+ def min_curvature_direction (self ) -> UnitVector3D :
199+ """The minimum curvature direction."""
200+ return UnitVector3D (self .cylinder .dir_z )
201+
202+ def max_curvature (self ) -> Real :
203+ """The maximum curvature."""
204+ return 1.0 / self .cylinder .radius .m
205+
206+ def max_curvature_direction (self ) -> UnitVector3D :
207+ """The maximum curvature direction."""
208+ return UnitVector3D (self .u_derivative ())
0 commit comments