2
2
3
3
4
4
from beartype import beartype as check_input_types
5
- from beartype .typing import Optional , Union
5
+ from beartype .typing import Union
6
6
import numpy as np
7
- from pint import Unit
7
+ from pint import Quantity
8
8
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
11
13
from ansys .geometry .core .typing import Real , RealSequence
12
14
13
15
@@ -18,117 +20,221 @@ class Cone:
18
20
Parameters
19
21
----------
20
22
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]
27
25
Radius of the cone.
28
- half_angle : Real
26
+ half_angle : Union[Quantity, Angle, Real]
29
27
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 .
34
32
"""
35
33
36
34
@check_input_types
37
35
def __init__ (
38
36
self ,
39
37
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 ,
46
42
):
47
43
"""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 )
56
44
57
45
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
63
48
)
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." )
64
56
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 )
68
58
69
59
@property
70
60
def origin (self ) -> Point3D :
71
61
"""Origin of the cone."""
72
62
return self ._origin
73
63
74
- @origin .setter
75
- @check_input_types
76
- def origin (self , origin : Point3D ) -> None :
77
- self ._origin = origin
78
-
79
64
@property
80
- def radius (self ) -> Real :
65
+ def radius (self ) -> Quantity :
81
66
"""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
88
68
89
69
@property
90
- def half_angle (self ) -> Real :
70
+ def half_angle (self ) -> Quantity :
91
71
"""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
93
73
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
98
78
99
79
@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 )
103
83
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
109
88
110
89
@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 ))
114
93
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 )
120
113
121
114
@check_input_types
122
- def __eq__ (self , other : object ) -> bool :
115
+ def __eq__ (self , other : "Cone" ) -> bool :
123
116
"""Equals operator for the ``Cone`` class."""
124
117
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 ()
130
181
)
131
182
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