K i_TdZddlmZmZddlmZmZmZmZm Z m Z ddl m Z ddl mZddlmZddlmZmZddlmZgd ZGd d eZGd d eZGddeZdZy)z.Geometry objects for use by wrapping pathways.)ABCabstractmethod)Integeracospisqrtsympifytan)Eq)atan2)cancel)Vectordot)trigsimp)WrappingGeometryBaseWrappingCylinderWrappingSpherec`eZdZdZeedZedZedZedZ dZ y)rzAbstract base class for all geometry classes to inherit from. Notes ===== Instances of this class cannot be directly instantiated by users. However, it can be used to created custom geometry types through subclassing. cy)z0The point with which the geometry is associated.N)clss o/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/sympy/physics/mechanics/wrapping_geometry.pypointzWrappingGeometryBase.points cy)zReturns ``True`` if a point is on the geometry's surface. Parameters ========== point : Point The point for which it's to be ascertained if it's on the geometry's surface or not. Nrselfrs rpoint_on_surfacez%WrappingGeometryBase.point_on_surface%s rcy)aGReturns the shortest distance between two points on a geometry's surface. Parameters ========== point_1 : Point The point from which the geodesic length should be calculated. point_2 : Point The point to which the geodesic length should be calculated. Nrrpoint_1point_2s rgeodesic_lengthz$WrappingGeometryBase.geodesic_length2s rcy)The vectors parallel to the geodesic at the two end points. Parameters ========== point_1 : Point The point from which the geodesic originates. point_2 : Point The point at which the geodesic terminates. Nrr s rgeodesic_end_vectorsz)WrappingGeometryBase.geodesic_end_vectorsBs rc4|jjdS)z,Default representation of a geometry object.z()) __class____name__rs r__repr__zWrappingGeometryBase.__repr__Qs..))*"--rN) r) __module__ __qualname____doc__propertyrrrr#r&r+rrrrrsd              .rrceZdZdZdZedZejdZedZejdZdZ dZ d Z d Z y ) raUA solid spherical object. Explanation =========== A wrapping geometry that allows for circular arcs to be defined between pairs of points. These paths are always geodetic (the shortest possible). Examples ======== To create a ``WrappingSphere`` instance, a ``Symbol`` denoting its radius and ``Point`` at which its center will be located are needed: >>> from sympy import symbols >>> from sympy.physics.mechanics import Point, WrappingSphere >>> r = symbols('r') >>> pO = Point('pO') A sphere with radius ``r`` centered on ``pO`` can be instantiated with: >>> WrappingSphere(r, pO) WrappingSphere(radius=r, point=pO) Parameters ========== radius : Symbol Radius of the sphere. This symbol must represent a value that is positive and constant, i.e. it cannot be a dynamic symbol, nor can it be an expression. point : Point A point at which the sphere is centered. See Also ======== WrappingCylinder: Cylindrical geometry where the wrapping direction can be defined. c ||_||_y)zInitializer for ``WrappingSphere``. Parameters ========== radius : Symbol The radius of the sphere. point : Point A point on which the sphere is centered. N)radiusr)rr2rs r__init__zWrappingSphere.__init__s  rc|jS)zRadius of the sphere._radiusr*s rr2zWrappingSphere.radius||rc||_yNr5rr2s rr2zWrappingSphere.radius  rc|jS)z(A point on which the sphere is centered._pointr*s rrzWrappingSphere.point{{rc||_yr9r=rs rrzWrappingSphere.point  rc|j|j}t|tr t ||}n|dz}t ||j dzdk(S)aReturns ``True`` if a point is on the sphere's surface. Parameters ========== point : Point The point for which it's to be ascertained if it's on the sphere's surface or not. This point's position relative to the sphere's center must be a simple expression involving the radius of the sphere, otherwise this check will likely not work. T)pos_fromr isinstancerrr r2)rr point_vectorpoint_radius_squareds rrzWrappingSphere.point_on_surfacesR~~djj1 lF +#&|\#B #/? & Q74??rc ||fD]k}|j|rd|d|j|jjd|jd|d|jd }t ||j|jj }|j|jj }t|j|}|j|z}|S)a{ Returns the shortest distance between two points on the sphere's surface. Explanation =========== The geodesic length, i.e. the shortest arc along the surface of a sphere, connecting two points can be calculated using the formula: .. math:: l = \arccos\left(\mathbf{v}_1 \cdot \mathbf{v}_2\right) where $\mathbf{v}_1$ and $\mathbf{v}_2$ are the unit vectors from the sphere's center to the first and second points on the sphere's surface respectively. Note that the actual path that the geodesic will take is undefined when the two points are directly opposite one another. Examples ======== A geodesic length can only be calculated between two points on the sphere's surface. Firstly, a ``WrappingSphere`` instance must be created along with two points that will lie on its surface: >>> from sympy import symbols >>> from sympy.physics.mechanics import (Point, ReferenceFrame, ... WrappingSphere) >>> N = ReferenceFrame('N') >>> r = symbols('r') >>> pO = Point('pO') >>> pO.set_vel(N, 0) >>> sphere = WrappingSphere(r, pO) >>> p1 = Point('p1') >>> p2 = Point('p2') Let's assume that ``p1`` lies at a distance of ``r`` in the ``N.x`` direction from ``pO`` and that ``p2`` is located on the sphere's surface in the ``N.y + N.z`` direction from ``pO``. These positions can be set with: >>> p1.set_pos(pO, r*N.x) >>> p1.pos_from(pO) r*N.x >>> p2.set_pos(pO, r*(N.y + N.z).normalize()) >>> p2.pos_from(pO) sqrt(2)*r/2*N.y + sqrt(2)*r/2*N.z The geodesic length, which is in this case is a quarter of the sphere's circumference, can be calculated using the ``geodesic_length`` method: >>> sphere.geodesic_length(p1, p2) pi*r/2 If the ``geodesic_length`` method is passed an argument, the ``Point`` that doesn't lie on the sphere's surface then a ``ValueError`` is raised because it's not possible to calculate a value in this case. Parameters ========== point_1 : Point Point from which the geodesic length should be calculated. point_2 : Point Point to which the geodesic length should be calculated. .Geodesic length cannot be calculated as point with radius z from the sphere's center does not lie on the surface of .) rrDr magnituder2 ValueError normalizerr) rr!r"rmsgpoint_1_vectorpoint_2_vector central_angler#s rr#zWrappingSphere.geodesic_lengthsHw' &E((/DUGL##(>>$**#=#G#G#I"JK115 =&&*V= QH !o% &!))$**5??A ))$**5??A^//?@ ++m3rc||}}|j}|j|}|j|}|j|dk(rd|d|d|d}t||j|j|j|j |j|j|j|j fS)r%rz:Can't compute geodesic end vectors for the pair of points  and z on a sphere zE as they are diametrically opposed, thus the geodesic is not defined.)rrDcrossrNrO) rr!r"pApBpOpA_vecpB_vecrPs rr&z#WrappingSphere.geodesic_end_vectors s'B ZZRR << 1 $M$eB4}TF3=>  S/ ! LLR ) / / 7 A A C LLR ) / / 7 A A C  rch|jjd|jd|jdS)z'Representation of a ``WrappingSphere``.(radius=, point=))r(r)r2rr*s rr+zWrappingSphere.__repr__'s8~~&&'x }=ZZL # rN) r)r,r-r.r3r/r2setterrrr#r&r+rrrrrVs|(T  ]] \\@(Qf < rrceZdZdZdZedZejdZedZejdZedZ e jdZ d Z d Z d Z d Z y )raQA solid (infinite) cylindrical object. Explanation =========== A wrapping geometry that allows for circular arcs to be defined between pairs of points. These paths are always geodetic (the shortest possible) in the sense that they will be a straight line on the unwrapped cylinder's surface. However, it is also possible for a direction to be specified, i.e. paths can be influenced such that they either wrap along the shortest side or the longest side of the cylinder. To define these directions, rotations are in the positive direction following the right-hand rule. Examples ======== To create a ``WrappingCylinder`` instance, a ``Symbol`` denoting its radius, a ``Vector`` defining its axis, and a ``Point`` through which its axis passes are needed: >>> from sympy import symbols >>> from sympy.physics.mechanics import (Point, ReferenceFrame, ... WrappingCylinder) >>> N = ReferenceFrame('N') >>> r = symbols('r') >>> pO = Point('pO') >>> ax = N.x A cylinder with radius ``r``, and axis parallel to ``N.x`` passing through ``pO`` can be instantiated with: >>> WrappingCylinder(r, pO, ax) WrappingCylinder(radius=r, point=pO, axis=N.x) Parameters ========== radius : Symbol The radius of the cylinder. point : Point A point through which the cylinder's axis passes. axis : Vector The axis along which the cylinder is aligned. See Also ======== WrappingSphere: Spherical geometry where the wrapping direction is always geodetic. c.||_||_||_y)aInitializer for ``WrappingCylinder``. Parameters ========== radius : Symbol The radius of the cylinder. This symbol must represent a value that is positive and constant, i.e. it cannot be a dynamic symbol. point : Point A point through which the cylinder's axis passes. axis : Vector The axis along which the cylinder is aligned. N)r2raxis)rr2rrcs rr3zWrappingCylinder.__init__ds   rc|jS)zRadius of the cylinder.r5r*s rr2zWrappingCylinder.radiuswr7rc||_yr9r5r:s rr2zWrappingCylinder.radius|r;rc|jS)z1A point through which the cylinder's axis passes.r=r*s rrzWrappingCylinder.pointr?rc||_yr9r=rs rrzWrappingCylinder.pointrArc|jS)z)Axis along which the cylinder is aligned.)_axisr*s rrczWrappingCylinder.axisszzrc.|j|_yr9)rOri)rrcs rrczWrappingCylinder.axiss^^% rc |j|j}|j|j|jz}||z }t |t r t||}n|dz}t t||jdzdk(S)aReturns ``True`` if a point is on the cylinder's surface. Parameters ========== point : Point The point for which it's to be ascertained if it's on the cylinder's surface or not. This point's position relative to the cylinder's axis must be a simple expression involving the radius of the sphere, otherwise this check will likely not work. rCT) rDrrrcrErr rr2)rrrelative_positionparallelrFrGs rrz!WrappingCylinder.point_on_surfaces"NN4::6$((3dii?(83 lF +#&|\#B #/? (/0$++q.ATIIrc||fD]x}|j|rd|d|j|jjd|jd|d|jd|j d }t ||j|}|j|j }|j|j}||j|j |j zz j}|j|j} | | j|j |j zz j} tt|j| j|j t|j| } |j| z} t|dz| dzz} | S)a The shortest distance between two points on a geometry's surface. Explanation =========== The geodesic length, i.e. the shortest arc along the surface of a cylinder, connecting two points. It can be calculated using Pythagoras' theorem. The first short side is the distance between the two points on the cylinder's surface parallel to the cylinder's axis. The second short side is the arc of a circle between the two points of the cylinder's surface perpendicular to the cylinder's axis. The resulting hypotenuse is the geodesic length. Examples ======== A geodesic length can only be calculated between two points on the cylinder's surface. Firstly, a ``WrappingCylinder`` instance must be created along with two points that will lie on its surface: >>> from sympy import symbols, cos, sin >>> from sympy.physics.mechanics import (Point, ReferenceFrame, ... WrappingCylinder, dynamicsymbols) >>> N = ReferenceFrame('N') >>> r = symbols('r') >>> pO = Point('pO') >>> pO.set_vel(N, 0) >>> cylinder = WrappingCylinder(r, pO, N.x) >>> p1 = Point('p1') >>> p2 = Point('p2') Let's assume that ``p1`` is located at ``N.x + r*N.y`` relative to ``pO`` and that ``p2`` is located at ``r*(cos(q)*N.y + sin(q)*N.z)`` relative to ``pO``, where ``q(t)`` is a generalized coordinate specifying the angle rotated around the ``N.x`` axis according to the right-hand rule where ``N.y`` is zero. These positions can be set with: >>> q = dynamicsymbols('q') >>> p1.set_pos(pO, N.x + r*N.y) >>> p1.pos_from(pO) N.x + r*N.y >>> p2.set_pos(pO, r*(cos(q)*N.y + sin(q)*N.z).normalize()) >>> p2.pos_from(pO).simplify() r*cos(q(t))*N.y + r*sin(q(t))*N.z The geodesic length, which is in this case a is the hypotenuse of a right triangle where the other two side lengths are ``1`` (parallel to the cylinder's axis) and ``r*q(t)`` (parallel to the cylinder's cross section), can be calculated using the ``geodesic_length`` method: >>> cylinder.geodesic_length(p1, p2).simplify() sqrt(r**2*q(t)**2 + 1) If the ``geodesic_length`` method is passed an argument ``Point`` that doesn't lie on the sphere's surface then a ``ValueError`` is raised because it's not possible to calculate a value in this case. Parameters ========== point_1 : Point Point from which the geodesic length should be calculated. point_2 : Point Point to which the geodesic length should be calculated. rIrJz from the cylinder's center rKz and axis rLrC) rrDrrMr2rcrNrrO_directional_atanr rVr)rr!r"rrPrlparallel_lengthpoint_1_relative_positionpoint_1_perpendicular_vectorpoint_2_relative_positionpoint_2_perpendicular_vectorrSplanar_arc_lengthr#s rr#z WrappingCylinder.geodesic_lengthsFw' &E((/DUGL##(>>$**#=#G#G#I"JK337::,?&&*V= Zyyk $!o% &$,,W5+// :$+$4$4TZZ$@! %'++DII6tyy@ A )+ % %,$4$4TZZ$@! %'++DII6tyy@ A )+ % * /34TYY ! /334PQ R  !KK 514Eq4HHIrcj|j|j}|j|j}||k(rd|d|d}t||j|j|jz}|j|j|jz}||z }||z } || k(rt d} t d} nS|jj |j} |jj | j } |j||} |j|} | j|j}t| dz|dzz }|| z||jzzj}|| z||jzz j}||fS)r%z:Cannot compute geodesic end vectors for coincident points rUz as no geodesic exists.rrC) rDrrNrrcrrVrOr#r)rr!r"point_1_from_origin_pointpoint_2_from_origin_pointrPpoint_1_parallelpoint_2_parallelpoint_1_normalpoint_2_normalpoint_1_perpendicularpoint_2_perpendicularr#rlrprurQrRs rr&z%WrappingCylinder.geodesic_end_vectorss%,$4$4TZZ$@!$+$4$4TZZ$@! $(A AL)5 )@B S/ !488CdiiO488CdiiO36FF36FF ^ +$*1I !$*1I !$(IIOON$C$M$M$O !%)YY__^%D%N%N%P$P !..w@#,,W5+// : !!3oq6H!HI  5 5 ) * )+   5 5 ) * )+  //rc|jjd|jd|jd|jdS)z)Representation of a ``WrappingCylinder``.r]r^z, axis=r_)r(r)r2rrcr*s rr+zWrappingCylinder.__repr__BsB~~&&'x }=ZZL {! 5 rN)r)r,r-r.r3r/r2r`rrcrr#r&r+rrrrr/s2h& ]] \\ [[&&J,fP00d rrcZ|jr+|jrt||}|dkr |dtzz }|S|jrd|d|d}t||jrd|d|d}t|t t ||z }t |tr|jd}|S|jr`|jdtdk(rEt |jd tr(dtz|jd jdz }|Sd |d }t|) aCompute atan in a directional sense as required for geodesics. Explanation =========== To be able to control the direction of the geodesic length along the surface of a cylinder a dedicated arctangent function is needed that properly handles the directionality of different case. This function ensures that the central angle is always positive but shifting the case where ``atan2`` would return a negative angle to be centered around ``2*pi``. Notes ===== This function only handles very specific cases, i.e. the ones that are expected to be encountered when calculating symbolic geodesics on uniformly curved surfaces. As such, ``NotImplemented`` errors can be raised in many cases. This function is named with a leader underscore to indicate that it only aims to provide very specific functionality within the private scope of this module. rrCz5Cannot compute a directional atan when the numerator z is numeric and the denominator z is symbolic.z! is symbolic and the denominator z is numeric.z0Cannot compute a directional atan for the value rL) is_numberr rNotImplementedErrorr rrEr argsis_Mulr) numerator denominatoranglerPratios rroroJsM2{44i- 19 QVOE6 L5   CI;O..9]- I "#&&   CI;O//:m< I "#&&[!89: eS !JJqME L LL 1 ,5::a=#.FUZZ]//22E LEUG1MC%c* *rN)r.abcrrsympyrrrrr r sympy.core.relationalr (sympy.functions.elementary.trigonometricr sympy.polys.polytoolsr sympy.physics.vectorrrsympy.simplify.simplifyr__all__rrrrorrrrsY4#77$:(,, ?.3?.DV )V rX +X v7r