22
33
44from beartype import beartype as check_input_types
5- from beartype .typing import Optional , Union
5+ from beartype .typing import Union
66import numpy as np
7- from pint import Unit
7+ from pint import Quantity
88
9- from ansys .geometry .core .math import Point3D , UnitVector3D , Vector3D
10- from ansys .geometry .core .misc import UNIT_ANGLE , UNIT_LENGTH , UNITS , check_pint_unit_compatibility
9+ from ansys .geometry .core .math import UNITVECTOR3D_X , UNITVECTOR3D_Z , Point3D , UnitVector3D , Vector3D
10+ from ansys .geometry .core .misc import Angle , Distance
11+ from ansys .geometry .core .primitives .line import Line
12+ from ansys .geometry .core .primitives .surface_evaluation import ParamUV , SurfaceEvaluation
1113from ansys .geometry .core .typing import Real , RealSequence
1214
1315
@@ -18,117 +20,221 @@ class Cone:
1820 Parameters
1921 ----------
2022 origin : Union[~numpy.ndarray, RealSequence, Point3D]
21- Centered origin of the cone.
22- direction_x : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
23- X-plane direction.
24- direction_y : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
25- Y-plane direction.
26- radius : Real
23+ Origin of the cone.
24+ radius : Union[Quantity, Distance, Real]
2725 Radius of the cone.
28- half_angle : Real
26+ half_angle : Union[Quantity, Angle, Real]
2927 Half angle of the apex, determining the upward angle.
30- length_unit : Unit, default: UNIT_LENGTH
31- Units for defining the radius .
32- angle_unit : Unit, default: UNIT_ANGLE
33- Units for defining the half angle .
28+ reference : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
29+ X-plane direction .
30+ axis : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]
31+ Z-plane direction .
3432 """
3533
3634 @check_input_types
3735 def __init__ (
3836 self ,
3937 origin : Union [np .ndarray , RealSequence , Point3D ],
40- direction_x : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ],
41- direction_y : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ],
42- radius : Real ,
43- half_angle : Real ,
44- length_unit : Optional [Unit ] = UNIT_LENGTH ,
45- angle_unit : Optional [Unit ] = UNIT_ANGLE ,
38+ radius : Union [Quantity , Distance , Real ],
39+ half_angle : Union [Quantity , Angle , Real ],
40+ reference : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ] = UNITVECTOR3D_X ,
41+ axis : Union [np .ndarray , RealSequence , UnitVector3D , Vector3D ] = UNITVECTOR3D_Z ,
4642 ):
4743 """Constructor method for the ``Cone`` class."""
48- check_pint_unit_compatibility (length_unit , UNIT_LENGTH )
49- check_pint_unit_compatibility (angle_unit , UNIT_ANGLE )
50-
51- self ._length_unit = length_unit
52- _ , self ._base_length_unit = UNITS .get_base_units (length_unit )
53-
54- self ._angle_unit = angle_unit
55- _ , self ._base_angle_unit = UNITS .get_base_units (angle_unit )
5644
5745 self ._origin = Point3D (origin ) if not isinstance (origin , Point3D ) else origin
58- self ._direction_x = (
59- UnitVector3D (direction_x ) if not isinstance (direction_x , UnitVector3D ) else direction_x
60- )
61- self ._direction_y = (
62- UnitVector3D (direction_y ) if not isinstance (direction_y , UnitVector3D ) else direction_y
46+ self ._reference = (
47+ UnitVector3D (reference ) if not isinstance (reference , UnitVector3D ) else reference
6348 )
49+ self ._axis = UnitVector3D (axis ) if not isinstance (axis , UnitVector3D ) else axis
50+ if not self ._reference .is_perpendicular_to (self ._axis ):
51+ raise ValueError ("Cone reference (dir_x) and axis (dir_z) must be perpendicular." )
52+
53+ self ._radius = radius if isinstance (radius , Distance ) else Distance (radius )
54+ if self ._radius .value <= 0 :
55+ raise ValueError ("Radius must be a real positive value." )
6456
65- # Store values in base unit
66- self ._radius = UNITS .convert (radius , self ._length_unit , self ._base_length_unit )
67- self ._half_angle = UNITS .convert (half_angle , self ._angle_unit , self ._base_angle_unit )
57+ self ._half_angle = half_angle if isinstance (half_angle , Angle ) else Angle (half_angle )
6858
6959 @property
7060 def origin (self ) -> Point3D :
7161 """Origin of the cone."""
7262 return self ._origin
7363
74- @origin .setter
75- @check_input_types
76- def origin (self , origin : Point3D ) -> None :
77- self ._origin = origin
78-
7964 @property
80- def radius (self ) -> Real :
65+ def radius (self ) -> Quantity :
8166 """Radius of the cone."""
82- return UNITS .convert (self ._radius , self ._base_length_unit , self ._length_unit )
83-
84- @radius .setter
85- @check_input_types
86- def radius (self , radius : Real ) -> None :
87- self ._radius = UNITS .convert (radius , self ._length_unit , self ._base_length_unit )
67+ return self ._radius .value
8868
8969 @property
90- def half_angle (self ) -> Real :
70+ def half_angle (self ) -> Quantity :
9171 """Half angle of the apex."""
92- return UNITS . convert ( self ._half_angle , self . _base_angle_unit , self . _angle_unit )
72+ return self ._half_angle . value
9373
94- @half_angle . setter
95- @ check_input_types
96- def half_angle ( self , half_angle : Real ) -> None :
97- self . _half_angle = UNITS . convert ( half_angle , self ._angle_unit , self . _base_angle_unit )
74+ @property
75+ def dir_x ( self ) -> UnitVector3D :
76+ """X-direction of the cone."""
77+ return self ._reference
9878
9979 @property
100- def length_unit (self ) -> Unit :
101- """Unit of the radius ."""
102- return self ._length_unit
80+ def dir_y (self ) -> UnitVector3D :
81+ """Y-direction of the cone ."""
82+ return self .dir_z . cross ( self . dir_x )
10383
104- @length_unit .setter
105- @check_input_types
106- def length_unit (self , length_unit : Unit ) -> None :
107- check_pint_unit_compatibility (length_unit , UNIT_LENGTH )
108- self ._length_unit = length_unit
84+ @property
85+ def dir_z (self ) -> UnitVector3D :
86+ """Z-direction of the cone."""
87+ return self ._axis
10988
11089 @property
111- def angle_unit (self ) -> Unit :
112- """Unit of the angle ."""
113- return self ._angle_unit
90+ def height (self ) -> Quantity :
91+ """Height of the cone ."""
92+ return np . abs ( self .radius / np . tan ( self . half_angle ))
11493
115- @angle_unit .setter
116- @check_input_types
117- def angle_unit (self , angle_unit : Unit ) -> None :
118- check_pint_unit_compatibility (angle_unit , UNIT_ANGLE )
119- self ._angle_unit = angle_unit
94+ @property
95+ def surface_area (self ) -> Quantity :
96+ """Surface area of the cone."""
97+ return np .pi * self .radius * (self .radius + np .sqrt (self .height ** 2 + self .radius ** 2 ))
98+
99+ @property
100+ def volume (self ) -> Quantity :
101+ """Volume of the cone."""
102+ return np .pi * self .radius ** 2 * self .height / 3
103+
104+ @property
105+ def apex (self ) -> Point3D :
106+ """Apex point of the cone."""
107+ return self .origin + self .apex_param * self .dir_z
108+
109+ @property
110+ def apex_param (self ) -> Real :
111+ """Apex parameter of the cone."""
112+ return - np .abs (self .radius .m ) / np .tan (self .half_angle .m )
120113
121114 @check_input_types
122- def __eq__ (self , other : object ) -> bool :
115+ def __eq__ (self , other : "Cone" ) -> bool :
123116 """Equals operator for the ``Cone`` class."""
124117 return (
125- self ._origin == other .origin
126- and self ._radius == other .radius
127- and self ._half_angle == other .half_angle
128- and self ._direction_x == other ._direction_x
129- and self ._direction_y == other ._direction_y
118+ self ._origin == other ._origin
119+ and self ._radius == other ._radius
120+ and self ._half_angle == other ._half_angle
121+ and self ._reference == other ._reference
122+ and self ._axis == other ._axis
123+ )
124+
125+ def evaluate (self , parameter : ParamUV ) -> "ConeEvaluation" :
126+ """Evaluate the cone at the given parameters."""
127+ return ConeEvaluation (self , parameter )
128+
129+ def project_point (self , point : Point3D ) -> "ConeEvaluation" :
130+ """Project a point onto the cone and return its ``ConeEvaluation``."""
131+ u = np .arctan2 (self .dir_y .dot (point - self .origin ), self .dir_x .dot (point - self .origin ))
132+ while u < 0 :
133+ u += 2 * np .pi
134+ while u > 2 * np .pi :
135+ u -= 2 * np .pi
136+ axis = Line (self .origin , self .dir_z )
137+ line_eval = axis .project_point (point )
138+ v = line_eval .parameter
139+
140+ cone_radius = self .radius .m + v * np .tan (self .half_angle .m )
141+ point_radius = np .linalg .norm (point - line_eval .position ())
142+ dist_to_cone = (point_radius - cone_radius ) * np .cos (self .half_angle .m )
143+ v += dist_to_cone * np .sin (self .half_angle .m )
144+
145+ return ConeEvaluation (self , ParamUV (u , v ))
146+
147+
148+ class ConeEvaluation (SurfaceEvaluation ):
149+ """
150+ Provides ``Cone`` evaluation at certain parameters.
151+
152+ Parameters
153+ ----------
154+ cone: ~ansys.geometry.core.primitives.cone.Cone
155+ The ``Cone`` object to be evaluated.
156+ parameter: ParamUV
157+ The parameters (u, v) at which the ``Cone`` evaluation is requested.
158+ """
159+
160+ def __init__ (self , cone : Cone , parameter : ParamUV ) -> None :
161+ """``ConeEvaluation`` class constructor."""
162+ self ._cone = cone
163+ self ._parameter = parameter
164+
165+ @property
166+ def cone (self ) -> Cone :
167+ """The cone being evaluated."""
168+ return self ._cone
169+
170+ @property
171+ def parameter (self ) -> ParamUV :
172+ """The parameter that the evaluation is based upon."""
173+ return self ._parameter
174+
175+ def position (self ) -> Point3D :
176+ """The point on the cone, based on the evaluation."""
177+ return (
178+ self .cone .origin
179+ + self .parameter .v * self .cone .dir_z
180+ + self .__radius_v () * self .__cone_normal ()
130181 )
131182
132- def __ne__ (self , other ) -> bool :
133- """Not equals operator for the ``Cone`` class."""
134- return not self == other
183+ def normal (self ) -> UnitVector3D :
184+ """The normal to the surface."""
185+ return UnitVector3D (
186+ self .__cone_normal () * np .cos (self .cone .half_angle .m )
187+ - self .cone .dir_z * np .sin (self .cone .half_angle .m )
188+ )
189+
190+ def __radius_v (self ) -> Real :
191+ """Private radius helper method."""
192+ return self .cone .radius .m + self .parameter .v * np .tan (self .cone .half_angle .m )
193+
194+ def __cone_normal (self ) -> Vector3D :
195+ """Private normal helper method."""
196+ return (
197+ np .cos (self .parameter .u ) * self .cone .dir_x + np .sin (self .parameter .u ) * self .cone .dir_y
198+ )
199+
200+ def __cone_tangent (self ) -> Vector3D :
201+ """Private tangent helper method."""
202+ return (
203+ - np .sin (self .parameter .u ) * self .cone .dir_x + np .cos (self .parameter .u ) * self .cone .dir_y
204+ )
205+
206+ def u_derivative (self ) -> Vector3D :
207+ """The first derivative with respect to u."""
208+ return self .__radius_v () * self .__cone_tangent ()
209+
210+ def v_derivative (self ) -> Vector3D :
211+ """The first derivative with respect to v."""
212+ return self .cone .dir_z + np .tan (self .cone .half_angle .m ) * self .__cone_normal ()
213+
214+ def uu_derivative (self ) -> Vector3D :
215+ """The second derivative with respect to u."""
216+ return - self .__radius_v () * self .__cone_normal ()
217+
218+ def uv_derivative (self ) -> Vector3D :
219+ """The second derivative with respect to u and v."""
220+ return np .tan (self .cone .half_angle .m ) * self .__cone_tangent ()
221+
222+ def vv_derivative (self ) -> Vector3D :
223+ """The second derivative with respect to v."""
224+ return Vector3D ([0 , 0 , 0 ])
225+
226+ def min_curvature (self ) -> Real :
227+ """The minimum curvature."""
228+ return 0
229+
230+ def min_curvature_direction (self ) -> UnitVector3D :
231+ """The minimum curvature direction."""
232+ return UnitVector3D (self .v_derivative ())
233+
234+ def max_curvature (self ) -> Real :
235+ """The maximum curvature."""
236+ return 1.0 / self .__radius_v ()
237+
238+ def max_curvature_direction (self ) -> UnitVector3D :
239+ """The maximum curvature direction."""
240+ return UnitVector3D (self .u_derivative ())
0 commit comments