You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

423 lines
10 KiB

5 months ago
"""Parabolic geometrical entity.
Contains
* Parabola
"""
from sympy.core import S
from sympy.core.sorting import ordered
from sympy.core.symbol import _symbol, symbols
from sympy.geometry.entity import GeometryEntity, GeometrySet
from sympy.geometry.point import Point, Point2D
from sympy.geometry.line import Line, Line2D, Ray2D, Segment2D, LinearEntity3D
from sympy.geometry.ellipse import Ellipse
from sympy.functions import sign
from sympy.simplify import simplify
from sympy.solvers.solvers import solve
class Parabola(GeometrySet):
"""A parabolic GeometryEntity.
A parabola is declared with a point, that is called 'focus', and
a line, that is called 'directrix'.
Only vertical or horizontal parabolas are currently supported.
Parameters
==========
focus : Point
Default value is Point(0, 0)
directrix : Line
Attributes
==========
focus
directrix
axis of symmetry
focal length
p parameter
vertex
eccentricity
Raises
======
ValueError
When `focus` is not a two dimensional point.
When `focus` is a point of directrix.
NotImplementedError
When `directrix` is neither horizontal nor vertical.
Examples
========
>>> from sympy import Parabola, Point, Line
>>> p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7,8)))
>>> p1.focus
Point2D(0, 0)
>>> p1.directrix
Line2D(Point2D(5, 8), Point2D(7, 8))
"""
def __new__(cls, focus=None, directrix=None, **kwargs):
if focus:
focus = Point(focus, dim=2)
else:
focus = Point(0, 0)
directrix = Line(directrix)
if directrix.contains(focus):
raise ValueError('The focus must not be a point of directrix')
return GeometryEntity.__new__(cls, focus, directrix, **kwargs)
@property
def ambient_dimension(self):
"""Returns the ambient dimension of parabola.
Returns
=======
ambient_dimension : integer
Examples
========
>>> from sympy import Parabola, Point, Line
>>> f1 = Point(0, 0)
>>> p1 = Parabola(f1, Line(Point(5, 8), Point(7, 8)))
>>> p1.ambient_dimension
2
"""
return 2
@property
def axis_of_symmetry(self):
"""Return the axis of symmetry of the parabola: a line
perpendicular to the directrix passing through the focus.
Returns
=======
axis_of_symmetry : Line
See Also
========
sympy.geometry.line.Line
Examples
========
>>> from sympy import Parabola, Point, Line
>>> p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7, 8)))
>>> p1.axis_of_symmetry
Line2D(Point2D(0, 0), Point2D(0, 1))
"""
return self.directrix.perpendicular_line(self.focus)
@property
def directrix(self):
"""The directrix of the parabola.
Returns
=======
directrix : Line
See Also
========
sympy.geometry.line.Line
Examples
========
>>> from sympy import Parabola, Point, Line
>>> l1 = Line(Point(5, 8), Point(7, 8))
>>> p1 = Parabola(Point(0, 0), l1)
>>> p1.directrix
Line2D(Point2D(5, 8), Point2D(7, 8))
"""
return self.args[1]
@property
def eccentricity(self):
"""The eccentricity of the parabola.
Returns
=======
eccentricity : number
A parabola may also be characterized as a conic section with an
eccentricity of 1. As a consequence of this, all parabolas are
similar, meaning that while they can be different sizes,
they are all the same shape.
See Also
========
https://en.wikipedia.org/wiki/Parabola
Examples
========
>>> from sympy import Parabola, Point, Line
>>> p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7, 8)))
>>> p1.eccentricity
1
Notes
-----
The eccentricity for every Parabola is 1 by definition.
"""
return S.One
def equation(self, x='x', y='y'):
"""The equation of the parabola.
Parameters
==========
x : str, optional
Label for the x-axis. Default value is 'x'.
y : str, optional
Label for the y-axis. Default value is 'y'.
Returns
=======
equation : SymPy expression
Examples
========
>>> from sympy import Parabola, Point, Line
>>> p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7, 8)))
>>> p1.equation()
-x**2 - 16*y + 64
>>> p1.equation('f')
-f**2 - 16*y + 64
>>> p1.equation(y='z')
-x**2 - 16*z + 64
"""
x = _symbol(x, real=True)
y = _symbol(y, real=True)
m = self.directrix.slope
if m is S.Infinity:
t1 = 4 * (self.p_parameter) * (x - self.vertex.x)
t2 = (y - self.vertex.y)**2
elif m == 0:
t1 = 4 * (self.p_parameter) * (y - self.vertex.y)
t2 = (x - self.vertex.x)**2
else:
a, b = self.focus
c, d = self.directrix.coefficients[:2]
t1 = (x - a)**2 + (y - b)**2
t2 = self.directrix.equation(x, y)**2/(c**2 + d**2)
return t1 - t2
@property
def focal_length(self):
"""The focal length of the parabola.
Returns
=======
focal_lenght : number or symbolic expression
Notes
=====
The distance between the vertex and the focus
(or the vertex and directrix), measured along the axis
of symmetry, is the "focal length".
See Also
========
https://en.wikipedia.org/wiki/Parabola
Examples
========
>>> from sympy import Parabola, Point, Line
>>> p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7, 8)))
>>> p1.focal_length
4
"""
distance = self.directrix.distance(self.focus)
focal_length = distance/2
return focal_length
@property
def focus(self):
"""The focus of the parabola.
Returns
=======
focus : Point
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy import Parabola, Point, Line
>>> f1 = Point(0, 0)
>>> p1 = Parabola(f1, Line(Point(5, 8), Point(7, 8)))
>>> p1.focus
Point2D(0, 0)
"""
return self.args[0]
def intersection(self, o):
"""The intersection of the parabola and another geometrical entity `o`.
Parameters
==========
o : GeometryEntity, LinearEntity
Returns
=======
intersection : list of GeometryEntity objects
Examples
========
>>> from sympy import Parabola, Point, Ellipse, Line, Segment
>>> p1 = Point(0,0)
>>> l1 = Line(Point(1, -2), Point(-1,-2))
>>> parabola1 = Parabola(p1, l1)
>>> parabola1.intersection(Ellipse(Point(0, 0), 2, 5))
[Point2D(-2, 0), Point2D(2, 0)]
>>> parabola1.intersection(Line(Point(-7, 3), Point(12, 3)))
[Point2D(-4, 3), Point2D(4, 3)]
>>> parabola1.intersection(Segment((-12, -65), (14, -68)))
[]
"""
x, y = symbols('x y', real=True)
parabola_eq = self.equation()
if isinstance(o, Parabola):
if o in self:
return [o]
else:
return list(ordered([Point(i) for i in solve(
[parabola_eq, o.equation()], [x, y], set=True)[1]]))
elif isinstance(o, Point2D):
if simplify(parabola_eq.subs([(x, o._args[0]), (y, o._args[1])])) == 0:
return [o]
else:
return []
elif isinstance(o, (Segment2D, Ray2D)):
result = solve([parabola_eq,
Line2D(o.points[0], o.points[1]).equation()],
[x, y], set=True)[1]
return list(ordered([Point2D(i) for i in result if i in o]))
elif isinstance(o, (Line2D, Ellipse)):
return list(ordered([Point2D(i) for i in solve(
[parabola_eq, o.equation()], [x, y], set=True)[1]]))
elif isinstance(o, LinearEntity3D):
raise TypeError('Entity must be two dimensional, not three dimensional')
else:
raise TypeError('Wrong type of argument were put')
@property
def p_parameter(self):
"""P is a parameter of parabola.
Returns
=======
p : number or symbolic expression
Notes
=====
The absolute value of p is the focal length. The sign on p tells
which way the parabola faces. Vertical parabolas that open up
and horizontal that open right, give a positive value for p.
Vertical parabolas that open down and horizontal that open left,
give a negative value for p.
See Also
========
https://www.sparknotes.com/math/precalc/conicsections/section2/
Examples
========
>>> from sympy import Parabola, Point, Line
>>> p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7, 8)))
>>> p1.p_parameter
-4
"""
m = self.directrix.slope
if m is S.Infinity:
x = self.directrix.coefficients[2]
p = sign(self.focus.args[0] + x)
elif m == 0:
y = self.directrix.coefficients[2]
p = sign(self.focus.args[1] + y)
else:
d = self.directrix.projection(self.focus)
p = sign(self.focus.x - d.x)
return p * self.focal_length
@property
def vertex(self):
"""The vertex of the parabola.
Returns
=======
vertex : Point
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy import Parabola, Point, Line
>>> p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7, 8)))
>>> p1.vertex
Point2D(0, 4)
"""
focus = self.focus
m = self.directrix.slope
if m is S.Infinity:
vertex = Point(focus.args[0] - self.p_parameter, focus.args[1])
elif m == 0:
vertex = Point(focus.args[0], focus.args[1] - self.p_parameter)
else:
vertex = self.axis_of_symmetry.intersection(self)[0]
return vertex