Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make physics.mechanics.wrapping_geometry public #25510

Merged
merged 23 commits into from
Aug 15, 2023
Merged
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cf05ca0
Make _geometry public in docstrings and minor docstring edits.
moorepants Aug 14, 2023
76d4763
Renamed _geometry.py to geometry.py.
moorepants Aug 14, 2023
5df0e3d
Add geometry api to the docstrings.
moorepants Aug 14, 2023
2cb3040
Update sympy/physics/mechanics/geometry.py
brocksam Aug 14, 2023
91775e8
Update sympy/physics/mechanics/geometry.py
brocksam Aug 14, 2023
915f987
Added GeometryBase to __all__
moorepants Aug 14, 2023
51ba7ca
Corrected the math rendering.
moorepants Aug 14, 2023
32f55d7
Renamed _point_is_on_surface to point_on_surface (making it public.
moorepants Aug 14, 2023
fe18fb7
Warned that points on surfaces need to be simply defined and removed …
moorepants Aug 14, 2023
47c6779
Removed type hints because of sphinx parsing bug.
moorepants Aug 14, 2023
e5993ff
Renamed geometry.py to wrapping_geometry.py.
moorepants Aug 14, 2023
acb0f12
Remove unused import.
moorepants Aug 14, 2023
638bbef
Fixed doc page title.
moorepants Aug 14, 2023
2eecd41
Fixed merge conflicts.
moorepants Aug 14, 2023
8c5937e
Fixed imports in _pathway.
moorepants Aug 14, 2023
19657c2
Fixed failing tests.
moorepants Aug 14, 2023
e28367d
Reformat imports
brocksam Aug 15, 2023
eab6a24
Make geodesic_end_vectors methods public
brocksam Aug 15, 2023
c6d9fa4
Rename classes to WrappingCylinder, WrappingGeometryBase, WrappingSphere
brocksam Aug 15, 2023
d6c7b98
Add WrappingCylinder, WrappingGeometryBase, WrappingSphere to module …
brocksam Aug 15, 2023
3a7d33a
Fix typo in docstring
brocksam Aug 15, 2023
f1fe991
Swap incorrect explanations for geodesic_length methods
brocksam Aug 15, 2023
e3e4d2f
Remove type hints from wrapping_geometry modules
brocksam Aug 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
110 changes: 48 additions & 62 deletions sympy/physics/mechanics/_geometry.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
"""Implementations of geometry objects for use by wrapping pathways.

Notes
=====

This module is experimental and so is named with a leading underscore to
indicate that the API is not yet stabilized and could be subject to breaking
changes.

"""
"""Geometry objects for use by wrapping pathways."""

from __future__ import annotations

Expand Down Expand Up @@ -50,7 +41,7 @@ class GeometryBase(ABC):

@abstractmethod
def _point_is_on_surface(self, point: Point) -> bool:
"""Determine if a point is on the geometry's surface.
moorepants marked this conversation as resolved.
Show resolved Hide resolved
"""Returns ``True`` if a point is on the geometry's surface.

Parameters
==========
Expand All @@ -63,7 +54,8 @@ def _point_is_on_surface(self, point: Point) -> bool:

@abstractmethod
def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
"""The shortest distance between two points on a geometry's surface.
"""Returns the shortest distance between two points on a geometry's
surface.

Parameters
==========
Expand Down Expand Up @@ -93,21 +85,16 @@ class Sphere(GeometryBase):
Examples
========

As the ``_geometry.py`` module is experimental, it is not yet part of the
``sympy.physics.mechanics`` namespace. ``Sphere`` must therefore be
imported directly from the ``sympy.physics.mechanics._geometry`` module.

>>> from sympy.physics.mechanics._geometry import Sphere
>>> from sympy.physics.mechanics.geometry import Sphere

To create a ``Sphere`` instance, a ``Symbol`` denoting its radius and
``Point`` at which its center will be located are needed:

>>> from sympy import Symbol
>>> from sympy import symbols
>>> from sympy.physics.mechanics import Point
>>> r = Symbol('r')
>>> r = symbols('r')
>>> pO = Point('pO')


A sphere with radius ``r`` centered on ``pO`` can be instantiated with:

>>> Sphere(r, pO)
Expand All @@ -117,7 +104,7 @@ class Sphere(GeometryBase):
==========

radius : Symbol
The radius of the sphere. This symbol must represent a value that is
Radius of the sphere. This symbol must represent a value that is
positive and constant, i.e. it cannot be a dynamic symbol.
point : Point
A point at which the sphere is centered.
Expand All @@ -138,15 +125,15 @@ def __init__(self, radius: Symbol, point: Point) -> None:
radius : Symbol
The radius of the sphere.
point : Point
A point through which the sphere's axis passes.
A point through which the sphere is centered.

"""
self.radius = radius
self.point = point

@property
def radius(self) -> Symbol:
"""The radius of the sphere."""
"""Radius of the sphere."""
return self._radius

@radius.setter
Expand All @@ -163,7 +150,7 @@ def point(self, point: Point) -> None:
self._point = point

def _point_is_on_surface(self, point: Point) -> bool:
"""Determine if a point is on the sphere's surface.
"""Returns ``True`` if a point is on the sphere's surface.

Parameters
==========
Expand All @@ -175,13 +162,14 @@ def _point_is_on_surface(self, point: Point) -> bool:
"""
point_vector = point.pos_from(self.point)
if isinstance(point_vector, Vector):
point_radius = dot(point_vector, point_vector)
point_radius_squared = dot(point_vector, point_vector)
else:
point_radius = point_vector**2
return Eq(point_radius, self.radius**2) == True
point_radius_squared = point_vector**2
return Eq(point_radius_squared, self.radius**2) == True

moorepants marked this conversation as resolved.
Show resolved Hide resolved
def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
r"""The shortest distance between two points on a geometry's surface.
r"""Retrns the shortest distance between two points on the sphere's
surface.

Explanation
===========
Expand All @@ -190,6 +178,7 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
sphere, connecting two points can be calculated using the formula:

.. math::

l = \acos{\mathbf{v}_1 \dot \mathbf{v}_2}

where $mathbf{v}_1$ and $mathbf{v}_2$ are the unit vectors from the
Expand All @@ -206,11 +195,11 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
sphere's surface. Firstly, a ``Sphere`` instance must be created along
with two points that will lie on its surface:

>>> from sympy import Symbol
>>> from sympy import symbols
>>> from sympy.physics.mechanics import Point, ReferenceFrame
>>> from sympy.physics.mechanics._geometry import Sphere
>>> from sympy.physics.mechanics.geometry import Sphere
>>> N = ReferenceFrame('N')
>>> r = Symbol('r')
>>> r = symbols('r')
>>> pO = Point('pO')
>>> pO.set_vel(N, 0)
>>> sphere = Sphere(r, pO)
Expand All @@ -222,10 +211,10 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
surface in the ``N.y + N.z`` direction from ``pO``. These positions can
be set with:

>>> p1.set_pos(pO, r * N.x)
>>> 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.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

Expand All @@ -235,17 +224,17 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
>>> sphere.geodesic_length(p1, p2)
pi*r/2

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.
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
The point from which the geodesic length should be calculated.
Point from which the geodesic length should be calculated.
point_2 : Point
The point to which the geodesic length should be calculated.
Point to which the geodesic length should be calculated.

"""
for point in (point_1, point_2):
Expand All @@ -260,7 +249,7 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
point_1_vector = point_1.pos_from(self.point).normalize()
point_2_vector = point_2.pos_from(self.point).normalize()
central_angle = acos(point_2_vector.dot(point_1_vector))
geodesic_length = self.radius * central_angle
geodesic_length = self.radius*central_angle
return geodesic_length

def __repr__(self) -> str:
Expand Down Expand Up @@ -288,20 +277,16 @@ class Cylinder(GeometryBase):
Examples
========

As the ``_geometry.py`` module is experimental, it is not yet part of the
``sympy.physics.mechanics`` namespace. ``Cylinder`` must therefore be
imported directly from the ``sympy.physics.mechanics._geometry`` module.

>>> from sympy.physics.mechanics._geometry import Cylinder
>>> from sympy.physics.mechanics.geometry import Cylinder

To create a ``Cylinder`` instance, a ``Symbol`` denoting its radius, a
``Vector`` defining its axis, and a ``Point`` through which its axis passes
are needed:

>>> from sympy import Symbol
>>> from sympy import symbols
>>> from sympy.physics.mechanics import Point, ReferenceFrame
>>> N = ReferenceFrame('N')
>>> r = Symbol('r')
>>> r = symbols('r')
>>> pO = Point('pO')
>>> ax = N.x

Expand Down Expand Up @@ -349,7 +334,7 @@ def __init__(self, radius: Symbol, point: Point, axis: Vector) -> None:

@property
def radius(self) -> Symbol:
"""The radius of the cylinder."""
"""Radius of the cylinder."""
return self._radius

@radius.setter
Expand All @@ -367,15 +352,15 @@ def point(self, point: Point) -> None:

@property
def axis(self) -> Vector:
"""The axis along which the cylinder is aligned."""
"""Axis along which the cylinder is aligned."""
return self._axis

@axis.setter
def axis(self, axis: Vector) -> None:
self._axis = axis

def _point_is_on_surface(self, point: Point) -> bool:
"""Determine if a point is on the cylinder's surface.
"""Returns ``True`` if a point is on the cylinder's surface.

Parameters
==========
Expand All @@ -389,10 +374,10 @@ def _point_is_on_surface(self, point: Point) -> bool:
parallel = relative_position.dot(self.axis) * self.axis
point_vector = relative_position - parallel
if isinstance(point_vector, Vector):
point_radius = dot(point_vector, point_vector)
point_radius_squared = dot(point_vector, point_vector)
else:
point_radius = point_vector**2
return Eq(trigsimp(point_radius), self.radius**2) == True
point_radius_squared = point_vector**2
return Eq(trigsimp(point_radius_squared), self.radius**2) == True

def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
r"""The shortest distance between two points on a geometry's surface.
Expand All @@ -404,6 +389,7 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
cylinder, connecting two points can be calculated using the formula:

.. math::

l = \acos{\mathbf{v}_1 \dot \mathbf{v}_2}

moorepants marked this conversation as resolved.
Show resolved Hide resolved
where $mathbf{v}_1$ and $mathbf{v}_2$ are the unit vectors from the
Expand All @@ -417,12 +403,12 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
cylinder's surface. Firstly, a ``Cylinder`` instance must be created
along with two points that will lie on its surface:
moorepants marked this conversation as resolved.
Show resolved Hide resolved

>>> from sympy import Symbol, cos, sin
>>> from sympy import symbols, cos, sin
>>> from sympy.physics.mechanics import (Point, ReferenceFrame,
... dynamicsymbols)
>>> from sympy.physics.mechanics._geometry import Cylinder
... dynamicsymbols)
>>> from sympy.physics.mechanics.geometry import Cylinder
>>> N = ReferenceFrame('N')
>>> r = Symbol('r')
>>> r = symbols('r')
>>> pO = Point('pO')
>>> pO.set_vel(N, 0)
>>> cylinder = Cylinder(r, pO, N.x)
Expand All @@ -439,7 +425,7 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
>>> 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.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

Expand All @@ -459,9 +445,9 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
==========

point_1 : Point
The point from which the geodesic length should be calculated.
Point from which the geodesic length should be calculated.
point_2 : Point
The point to which the geodesic length should be calculated.
Point to which the geodesic length should be calculated.

"""
for point in (point_1, point_2):
Expand All @@ -481,13 +467,13 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
point_1_relative_position = point_1.pos_from(self.point)
point_1_perpendicular_vector = (
point_1_relative_position
- point_1_relative_position.dot(self.axis) * self.axis
- point_1_relative_position.dot(self.axis)*self.axis
).normalize()

point_2_relative_position = point_2.pos_from(self.point)
point_2_perpendicular_vector = (
point_2_relative_position
- point_2_relative_position.dot(self.axis) * self.axis
- point_2_relative_position.dot(self.axis)*self.axis
).normalize()

central_angle = _directional_atan(
Expand All @@ -497,7 +483,7 @@ def geodesic_length(self, point_1: Point, point_2: Point) -> ExprType:
cancel(point_1_perpendicular_vector.dot(point_2_perpendicular_vector)),
)

planar_arc_length = self.radius * central_angle
planar_arc_length = self.radius*central_angle
geodesic_length = sqrt(parallel_length**2 + planar_arc_length**2)
return geodesic_length

Expand Down