I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,45 @@
"""
A geometry module for the SymPy library. This module contains all of the
entities and functions needed to construct basic geometrical data and to
perform simple informational queries.
Usage:
======
Examples
========
"""
from sympy.geometry.point import Point, Point2D, Point3D
from sympy.geometry.line import Line, Ray, Segment, Line2D, Segment2D, Ray2D, \
Line3D, Segment3D, Ray3D
from sympy.geometry.plane import Plane
from sympy.geometry.ellipse import Ellipse, Circle
from sympy.geometry.polygon import Polygon, RegularPolygon, Triangle, rad, deg
from sympy.geometry.util import are_similar, centroid, convex_hull, idiff, \
intersection, closest_points, farthest_points
from sympy.geometry.exceptions import GeometryError
from sympy.geometry.curve import Curve
from sympy.geometry.parabola import Parabola
__all__ = [
'Point', 'Point2D', 'Point3D',
'Line', 'Ray', 'Segment', 'Line2D', 'Segment2D', 'Ray2D', 'Line3D',
'Segment3D', 'Ray3D',
'Plane',
'Ellipse', 'Circle',
'Polygon', 'RegularPolygon', 'Triangle', 'rad', 'deg',
'are_similar', 'centroid', 'convex_hull', 'idiff', 'intersection',
'closest_points', 'farthest_points',
'GeometryError',
'Curve',
'Parabola',
]

View File

@ -0,0 +1,424 @@
"""Curves in 2-dimensional Euclidean space.
Contains
========
Curve
"""
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.core import diff
from sympy.core.containers import Tuple
from sympy.core.symbol import _symbol
from sympy.geometry.entity import GeometryEntity, GeometrySet
from sympy.geometry.point import Point
from sympy.integrals import integrate
from sympy.matrices import Matrix, rot_axis3
from sympy.utilities.iterables import is_sequence
from mpmath.libmp.libmpf import prec_to_dps
class Curve(GeometrySet):
"""A curve in space.
A curve is defined by parametric functions for the coordinates, a
parameter and the lower and upper bounds for the parameter value.
Parameters
==========
function : list of functions
limits : 3-tuple
Function parameter and lower and upper bounds.
Attributes
==========
functions
parameter
limits
Raises
======
ValueError
When `functions` are specified incorrectly.
When `limits` are specified incorrectly.
Examples
========
>>> from sympy import Curve, sin, cos, interpolate
>>> from sympy.abc import t, a
>>> C = Curve((sin(t), cos(t)), (t, 0, 2))
>>> C.functions
(sin(t), cos(t))
>>> C.limits
(t, 0, 2)
>>> C.parameter
t
>>> C = Curve((t, interpolate([1, 4, 9, 16], t)), (t, 0, 1)); C
Curve((t, t**2), (t, 0, 1))
>>> C.subs(t, 4)
Point2D(4, 16)
>>> C.arbitrary_point(a)
Point2D(a, a**2)
See Also
========
sympy.core.function.Function
sympy.polys.polyfuncs.interpolate
"""
def __new__(cls, function, limits):
if not is_sequence(function) or len(function) != 2:
raise ValueError("Function argument should be (x(t), y(t)) "
"but got %s" % str(function))
if not is_sequence(limits) or len(limits) != 3:
raise ValueError("Limit argument should be (t, tmin, tmax) "
"but got %s" % str(limits))
return GeometryEntity.__new__(cls, Tuple(*function), Tuple(*limits))
def __call__(self, f):
return self.subs(self.parameter, f)
def _eval_subs(self, old, new):
if old == self.parameter:
return Point(*[f.subs(old, new) for f in self.functions])
def _eval_evalf(self, prec=15, **options):
f, (t, a, b) = self.args
dps = prec_to_dps(prec)
f = tuple([i.evalf(n=dps, **options) for i in f])
a, b = [i.evalf(n=dps, **options) for i in (a, b)]
return self.func(f, (t, a, b))
def arbitrary_point(self, parameter='t'):
"""A parameterized point on the curve.
Parameters
==========
parameter : str or Symbol, optional
Default value is 't'.
The Curve's parameter is selected with None or self.parameter
otherwise the provided symbol is used.
Returns
=======
Point :
Returns a point in parametric form.
Raises
======
ValueError
When `parameter` already appears in the functions.
Examples
========
>>> from sympy import Curve, Symbol
>>> from sympy.abc import s
>>> C = Curve([2*s, s**2], (s, 0, 2))
>>> C.arbitrary_point()
Point2D(2*t, t**2)
>>> C.arbitrary_point(C.parameter)
Point2D(2*s, s**2)
>>> C.arbitrary_point(None)
Point2D(2*s, s**2)
>>> C.arbitrary_point(Symbol('a'))
Point2D(2*a, a**2)
See Also
========
sympy.geometry.point.Point
"""
if parameter is None:
return Point(*self.functions)
tnew = _symbol(parameter, self.parameter, real=True)
t = self.parameter
if (tnew.name != t.name and
tnew.name in (f.name for f in self.free_symbols)):
raise ValueError('Symbol %s already appears in object '
'and cannot be used as a parameter.' % tnew.name)
return Point(*[w.subs(t, tnew) for w in self.functions])
@property
def free_symbols(self):
"""Return a set of symbols other than the bound symbols used to
parametrically define the Curve.
Returns
=======
set :
Set of all non-parameterized symbols.
Examples
========
>>> from sympy.abc import t, a
>>> from sympy import Curve
>>> Curve((t, t**2), (t, 0, 2)).free_symbols
set()
>>> Curve((t, t**2), (t, a, 2)).free_symbols
{a}
"""
free = set()
for a in self.functions + self.limits[1:]:
free |= a.free_symbols
free = free.difference({self.parameter})
return free
@property
def ambient_dimension(self):
"""The dimension of the curve.
Returns
=======
int :
the dimension of curve.
Examples
========
>>> from sympy.abc import t
>>> from sympy import Curve
>>> C = Curve((t, t**2), (t, 0, 2))
>>> C.ambient_dimension
2
"""
return len(self.args[0])
@property
def functions(self):
"""The functions specifying the curve.
Returns
=======
functions :
list of parameterized coordinate functions.
Examples
========
>>> from sympy.abc import t
>>> from sympy import Curve
>>> C = Curve((t, t**2), (t, 0, 2))
>>> C.functions
(t, t**2)
See Also
========
parameter
"""
return self.args[0]
@property
def limits(self):
"""The limits for the curve.
Returns
=======
limits : tuple
Contains parameter and lower and upper limits.
Examples
========
>>> from sympy.abc import t
>>> from sympy import Curve
>>> C = Curve([t, t**3], (t, -2, 2))
>>> C.limits
(t, -2, 2)
See Also
========
plot_interval
"""
return self.args[1]
@property
def parameter(self):
"""The curve function variable.
Returns
=======
Symbol :
returns a bound symbol.
Examples
========
>>> from sympy.abc import t
>>> from sympy import Curve
>>> C = Curve([t, t**2], (t, 0, 2))
>>> C.parameter
t
See Also
========
functions
"""
return self.args[1][0]
@property
def length(self):
"""The curve length.
Examples
========
>>> from sympy import Curve
>>> from sympy.abc import t
>>> Curve((t, t), (t, 0, 1)).length
sqrt(2)
"""
integrand = sqrt(sum(diff(func, self.limits[0])**2 for func in self.functions))
return integrate(integrand, self.limits)
def plot_interval(self, parameter='t'):
"""The plot interval for the default geometric plot of the curve.
Parameters
==========
parameter : str or Symbol, optional
Default value is 't';
otherwise the provided symbol is used.
Returns
=======
List :
the plot interval as below:
[parameter, lower_bound, upper_bound]
Examples
========
>>> from sympy import Curve, sin
>>> from sympy.abc import x, s
>>> Curve((x, sin(x)), (x, 1, 2)).plot_interval()
[t, 1, 2]
>>> Curve((x, sin(x)), (x, 1, 2)).plot_interval(s)
[s, 1, 2]
See Also
========
limits : Returns limits of the parameter interval
"""
t = _symbol(parameter, self.parameter, real=True)
return [t] + list(self.limits[1:])
def rotate(self, angle=0, pt=None):
"""This function is used to rotate a curve along given point ``pt`` at given angle(in radian).
Parameters
==========
angle :
the angle at which the curve will be rotated(in radian) in counterclockwise direction.
default value of angle is 0.
pt : Point
the point along which the curve will be rotated.
If no point given, the curve will be rotated around origin.
Returns
=======
Curve :
returns a curve rotated at given angle along given point.
Examples
========
>>> from sympy import Curve, pi
>>> from sympy.abc import x
>>> Curve((x, x), (x, 0, 1)).rotate(pi/2)
Curve((-x, x), (x, 0, 1))
"""
if pt:
pt = -Point(pt, dim=2)
else:
pt = Point(0,0)
rv = self.translate(*pt.args)
f = list(rv.functions)
f.append(0)
f = Matrix(1, 3, f)
f *= rot_axis3(angle)
rv = self.func(f[0, :2].tolist()[0], self.limits)
pt = -pt
return rv.translate(*pt.args)
def scale(self, x=1, y=1, pt=None):
"""Override GeometryEntity.scale since Curve is not made up of Points.
Returns
=======
Curve :
returns scaled curve.
Examples
========
>>> from sympy import Curve
>>> from sympy.abc import x
>>> Curve((x, x), (x, 0, 1)).scale(2)
Curve((2*x, x), (x, 0, 1))
"""
if pt:
pt = Point(pt, dim=2)
return self.translate(*(-pt).args).scale(x, y).translate(*pt.args)
fx, fy = self.functions
return self.func((fx*x, fy*y), self.limits)
def translate(self, x=0, y=0):
"""Translate the Curve by (x, y).
Returns
=======
Curve :
returns a translated curve.
Examples
========
>>> from sympy import Curve
>>> from sympy.abc import x
>>> Curve((x, x), (x, 0, 1)).translate(1, 2)
Curve((x + 1, x + 2), (x, 0, 1))
"""
fx, fy = self.functions
return self.func((fx + x, fy + y), self.limits)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,641 @@
"""The definition of the base geometrical entity with attributes common to
all derived geometrical entities.
Contains
========
GeometryEntity
GeometricSet
Notes
=====
A GeometryEntity is any object that has special geometric properties.
A GeometrySet is a superclass of any GeometryEntity that can also
be viewed as a sympy.sets.Set. In particular, points are the only
GeometryEntity not considered a Set.
Rn is a GeometrySet representing n-dimensional Euclidean space. R2 and
R3 are currently the only ambient spaces implemented.
"""
from __future__ import annotations
from sympy.core.basic import Basic
from sympy.core.containers import Tuple
from sympy.core.evalf import EvalfMixin, N
from sympy.core.numbers import oo
from sympy.core.symbol import Dummy
from sympy.core.sympify import sympify
from sympy.functions.elementary.trigonometric import cos, sin, atan
from sympy.matrices import eye
from sympy.multipledispatch import dispatch
from sympy.printing import sstr
from sympy.sets import Set, Union, FiniteSet
from sympy.sets.handlers.intersection import intersection_sets
from sympy.sets.handlers.union import union_sets
from sympy.solvers.solvers import solve
from sympy.utilities.misc import func_name
from sympy.utilities.iterables import is_sequence
# How entities are ordered; used by __cmp__ in GeometryEntity
ordering_of_classes = [
"Point2D",
"Point3D",
"Point",
"Segment2D",
"Ray2D",
"Line2D",
"Segment3D",
"Line3D",
"Ray3D",
"Segment",
"Ray",
"Line",
"Plane",
"Triangle",
"RegularPolygon",
"Polygon",
"Circle",
"Ellipse",
"Curve",
"Parabola"
]
x, y = [Dummy('entity_dummy') for i in range(2)]
T = Dummy('entity_dummy', real=True)
class GeometryEntity(Basic, EvalfMixin):
"""The base class for all geometrical entities.
This class does not represent any particular geometric entity, it only
provides the implementation of some methods common to all subclasses.
"""
__slots__: tuple[str, ...] = ()
def __cmp__(self, other):
"""Comparison of two GeometryEntities."""
n1 = self.__class__.__name__
n2 = other.__class__.__name__
c = (n1 > n2) - (n1 < n2)
if not c:
return 0
i1 = -1
for cls in self.__class__.__mro__:
try:
i1 = ordering_of_classes.index(cls.__name__)
break
except ValueError:
i1 = -1
if i1 == -1:
return c
i2 = -1
for cls in other.__class__.__mro__:
try:
i2 = ordering_of_classes.index(cls.__name__)
break
except ValueError:
i2 = -1
if i2 == -1:
return c
return (i1 > i2) - (i1 < i2)
def __contains__(self, other):
"""Subclasses should implement this method for anything more complex than equality."""
if type(self) is type(other):
return self == other
raise NotImplementedError()
def __getnewargs__(self):
"""Returns a tuple that will be passed to __new__ on unpickling."""
return tuple(self.args)
def __ne__(self, o):
"""Test inequality of two geometrical entities."""
return not self == o
def __new__(cls, *args, **kwargs):
# Points are sequences, but they should not
# be converted to Tuples, so use this detection function instead.
def is_seq_and_not_point(a):
# we cannot use isinstance(a, Point) since we cannot import Point
if hasattr(a, 'is_Point') and a.is_Point:
return False
return is_sequence(a)
args = [Tuple(*a) if is_seq_and_not_point(a) else sympify(a) for a in args]
return Basic.__new__(cls, *args)
def __radd__(self, a):
"""Implementation of reverse add method."""
return a.__add__(self)
def __rtruediv__(self, a):
"""Implementation of reverse division method."""
return a.__truediv__(self)
def __repr__(self):
"""String representation of a GeometryEntity that can be evaluated
by sympy."""
return type(self).__name__ + repr(self.args)
def __rmul__(self, a):
"""Implementation of reverse multiplication method."""
return a.__mul__(self)
def __rsub__(self, a):
"""Implementation of reverse subtraction method."""
return a.__sub__(self)
def __str__(self):
"""String representation of a GeometryEntity."""
return type(self).__name__ + sstr(self.args)
def _eval_subs(self, old, new):
from sympy.geometry.point import Point, Point3D
if is_sequence(old) or is_sequence(new):
if isinstance(self, Point3D):
old = Point3D(old)
new = Point3D(new)
else:
old = Point(old)
new = Point(new)
return self._subs(old, new)
def _repr_svg_(self):
"""SVG representation of a GeometryEntity suitable for IPython"""
try:
bounds = self.bounds
except (NotImplementedError, TypeError):
# if we have no SVG representation, return None so IPython
# will fall back to the next representation
return None
if not all(x.is_number and x.is_finite for x in bounds):
return None
svg_top = '''<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="{1}" height="{2}" viewBox="{0}"
preserveAspectRatio="xMinYMin meet">
<defs>
<marker id="markerCircle" markerWidth="8" markerHeight="8"
refx="5" refy="5" markerUnits="strokeWidth">
<circle cx="5" cy="5" r="1.5" style="stroke: none; fill:#000000;"/>
</marker>
<marker id="markerArrow" markerWidth="13" markerHeight="13" refx="2" refy="4"
orient="auto" markerUnits="strokeWidth">
<path d="M2,2 L2,6 L6,4" style="fill: #000000;" />
</marker>
<marker id="markerReverseArrow" markerWidth="13" markerHeight="13" refx="6" refy="4"
orient="auto" markerUnits="strokeWidth">
<path d="M6,2 L6,6 L2,4" style="fill: #000000;" />
</marker>
</defs>'''
# Establish SVG canvas that will fit all the data + small space
xmin, ymin, xmax, ymax = map(N, bounds)
if xmin == xmax and ymin == ymax:
# This is a point; buffer using an arbitrary size
xmin, ymin, xmax, ymax = xmin - .5, ymin -.5, xmax + .5, ymax + .5
else:
# Expand bounds by a fraction of the data ranges
expand = 0.1 # or 10%; this keeps arrowheads in view (R plots use 4%)
widest_part = max([xmax - xmin, ymax - ymin])
expand_amount = widest_part * expand
xmin -= expand_amount
ymin -= expand_amount
xmax += expand_amount
ymax += expand_amount
dx = xmax - xmin
dy = ymax - ymin
width = min([max([100., dx]), 300])
height = min([max([100., dy]), 300])
scale_factor = 1. if max(width, height) == 0 else max(dx, dy) / max(width, height)
try:
svg = self._svg(scale_factor)
except (NotImplementedError, TypeError):
# if we have no SVG representation, return None so IPython
# will fall back to the next representation
return None
view_box = "{} {} {} {}".format(xmin, ymin, dx, dy)
transform = "matrix(1,0,0,-1,0,{})".format(ymax + ymin)
svg_top = svg_top.format(view_box, width, height)
return svg_top + (
'<g transform="{}">{}</g></svg>'
).format(transform, svg)
def _svg(self, scale_factor=1., fill_color="#66cc99"):
"""Returns SVG path element for the GeometryEntity.
Parameters
==========
scale_factor : float
Multiplication factor for the SVG stroke-width. Default is 1.
fill_color : str, optional
Hex string for fill color. Default is "#66cc99".
"""
raise NotImplementedError()
def _sympy_(self):
return self
@property
def ambient_dimension(self):
"""What is the dimension of the space that the object is contained in?"""
raise NotImplementedError()
@property
def bounds(self):
"""Return a tuple (xmin, ymin, xmax, ymax) representing the bounding
rectangle for the geometric figure.
"""
raise NotImplementedError()
def encloses(self, o):
"""
Return True if o is inside (not on or outside) the boundaries of self.
The object will be decomposed into Points and individual Entities need
only define an encloses_point method for their class.
See Also
========
sympy.geometry.ellipse.Ellipse.encloses_point
sympy.geometry.polygon.Polygon.encloses_point
Examples
========
>>> from sympy import RegularPolygon, Point, Polygon
>>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices)
>>> t2 = Polygon(*RegularPolygon(Point(0, 0), 2, 3).vertices)
>>> t2.encloses(t)
True
>>> t.encloses(t2)
False
"""
from sympy.geometry.point import Point
from sympy.geometry.line import Segment, Ray, Line
from sympy.geometry.ellipse import Ellipse
from sympy.geometry.polygon import Polygon, RegularPolygon
if isinstance(o, Point):
return self.encloses_point(o)
elif isinstance(o, Segment):
return all(self.encloses_point(x) for x in o.points)
elif isinstance(o, (Ray, Line)):
return False
elif isinstance(o, Ellipse):
return self.encloses_point(o.center) and \
self.encloses_point(
Point(o.center.x + o.hradius, o.center.y)) and \
not self.intersection(o)
elif isinstance(o, Polygon):
if isinstance(o, RegularPolygon):
if not self.encloses_point(o.center):
return False
return all(self.encloses_point(v) for v in o.vertices)
raise NotImplementedError()
def equals(self, o):
return self == o
def intersection(self, o):
"""
Returns a list of all of the intersections of self with o.
Notes
=====
An entity is not required to implement this method.
If two different types of entities can intersect, the item with
higher index in ordering_of_classes should implement
intersections with anything having a lower index.
See Also
========
sympy.geometry.util.intersection
"""
raise NotImplementedError()
def is_similar(self, other):
"""Is this geometrical entity similar to another geometrical entity?
Two entities are similar if a uniform scaling (enlarging or
shrinking) of one of the entities will allow one to obtain the other.
Notes
=====
This method is not intended to be used directly but rather
through the `are_similar` function found in util.py.
An entity is not required to implement this method.
If two different types of entities can be similar, it is only
required that one of them be able to determine this.
See Also
========
scale
"""
raise NotImplementedError()
def reflect(self, line):
"""
Reflects an object across a line.
Parameters
==========
line: Line
Examples
========
>>> from sympy import pi, sqrt, Line, RegularPolygon
>>> l = Line((0, pi), slope=sqrt(2))
>>> pent = RegularPolygon((1, 2), 1, 5)
>>> rpent = pent.reflect(l)
>>> rpent
RegularPolygon(Point2D(-2*sqrt(2)*pi/3 - 1/3 + 4*sqrt(2)/3, 2/3 + 2*sqrt(2)/3 + 2*pi/3), -1, 5, -atan(2*sqrt(2)) + 3*pi/5)
>>> from sympy import pi, Line, Circle, Point
>>> l = Line((0, pi), slope=1)
>>> circ = Circle(Point(0, 0), 5)
>>> rcirc = circ.reflect(l)
>>> rcirc
Circle(Point2D(-pi, pi), -5)
"""
from sympy.geometry.point import Point
g = self
l = line
o = Point(0, 0)
if l.slope.is_zero:
v = l.args[0].y
if not v: # x-axis
return g.scale(y=-1)
reps = [(p, p.translate(y=2*(v - p.y))) for p in g.atoms(Point)]
elif l.slope is oo:
v = l.args[0].x
if not v: # y-axis
return g.scale(x=-1)
reps = [(p, p.translate(x=2*(v - p.x))) for p in g.atoms(Point)]
else:
if not hasattr(g, 'reflect') and not all(
isinstance(arg, Point) for arg in g.args):
raise NotImplementedError(
'reflect undefined or non-Point args in %s' % g)
a = atan(l.slope)
c = l.coefficients
d = -c[-1]/c[1] # y-intercept
# apply the transform to a single point
xf = Point(x, y)
xf = xf.translate(y=-d).rotate(-a, o).scale(y=-1
).rotate(a, o).translate(y=d)
# replace every point using that transform
reps = [(p, xf.xreplace({x: p.x, y: p.y})) for p in g.atoms(Point)]
return g.xreplace(dict(reps))
def rotate(self, angle, pt=None):
"""Rotate ``angle`` radians counterclockwise about Point ``pt``.
The default pt is the origin, Point(0, 0)
See Also
========
scale, translate
Examples
========
>>> from sympy import Point, RegularPolygon, Polygon, pi
>>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices)
>>> t # vertex on x axis
Triangle(Point2D(1, 0), Point2D(-1/2, sqrt(3)/2), Point2D(-1/2, -sqrt(3)/2))
>>> t.rotate(pi/2) # vertex on y axis now
Triangle(Point2D(0, 1), Point2D(-sqrt(3)/2, -1/2), Point2D(sqrt(3)/2, -1/2))
"""
newargs = []
for a in self.args:
if isinstance(a, GeometryEntity):
newargs.append(a.rotate(angle, pt))
else:
newargs.append(a)
return type(self)(*newargs)
def scale(self, x=1, y=1, pt=None):
"""Scale the object by multiplying the x,y-coordinates by x and y.
If pt is given, the scaling is done relative to that point; the
object is shifted by -pt, scaled, and shifted by pt.
See Also
========
rotate, translate
Examples
========
>>> from sympy import RegularPolygon, Point, Polygon
>>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices)
>>> t
Triangle(Point2D(1, 0), Point2D(-1/2, sqrt(3)/2), Point2D(-1/2, -sqrt(3)/2))
>>> t.scale(2)
Triangle(Point2D(2, 0), Point2D(-1, sqrt(3)/2), Point2D(-1, -sqrt(3)/2))
>>> t.scale(2, 2)
Triangle(Point2D(2, 0), Point2D(-1, sqrt(3)), Point2D(-1, -sqrt(3)))
"""
from sympy.geometry.point import Point
if pt:
pt = Point(pt, dim=2)
return self.translate(*(-pt).args).scale(x, y).translate(*pt.args)
return type(self)(*[a.scale(x, y) for a in self.args]) # if this fails, override this class
def translate(self, x=0, y=0):
"""Shift the object by adding to the x,y-coordinates the values x and y.
See Also
========
rotate, scale
Examples
========
>>> from sympy import RegularPolygon, Point, Polygon
>>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices)
>>> t
Triangle(Point2D(1, 0), Point2D(-1/2, sqrt(3)/2), Point2D(-1/2, -sqrt(3)/2))
>>> t.translate(2)
Triangle(Point2D(3, 0), Point2D(3/2, sqrt(3)/2), Point2D(3/2, -sqrt(3)/2))
>>> t.translate(2, 2)
Triangle(Point2D(3, 2), Point2D(3/2, sqrt(3)/2 + 2), Point2D(3/2, 2 - sqrt(3)/2))
"""
newargs = []
for a in self.args:
if isinstance(a, GeometryEntity):
newargs.append(a.translate(x, y))
else:
newargs.append(a)
return self.func(*newargs)
def parameter_value(self, other, t):
"""Return the parameter corresponding to the given point.
Evaluating an arbitrary point of the entity at this parameter
value will return the given point.
Examples
========
>>> from sympy import Line, Point
>>> from sympy.abc import t
>>> a = Point(0, 0)
>>> b = Point(2, 2)
>>> Line(a, b).parameter_value((1, 1), t)
{t: 1/2}
>>> Line(a, b).arbitrary_point(t).subs(_)
Point2D(1, 1)
"""
from sympy.geometry.point import Point
if not isinstance(other, GeometryEntity):
other = Point(other, dim=self.ambient_dimension)
if not isinstance(other, Point):
raise ValueError("other must be a point")
sol = solve(self.arbitrary_point(T) - other, T, dict=True)
if not sol:
raise ValueError("Given point is not on %s" % func_name(self))
return {t: sol[0][T]}
class GeometrySet(GeometryEntity, Set):
"""Parent class of all GeometryEntity that are also Sets
(compatible with sympy.sets)
"""
__slots__ = ()
def _contains(self, other):
"""sympy.sets uses the _contains method, so include it for compatibility."""
if isinstance(other, Set) and other.is_FiniteSet:
return all(self.__contains__(i) for i in other)
return self.__contains__(other)
@dispatch(GeometrySet, Set) # type:ignore # noqa:F811
def union_sets(self, o): # noqa:F811
""" Returns the union of self and o
for use with sympy.sets.Set, if possible. """
# if its a FiniteSet, merge any points
# we contain and return a union with the rest
if o.is_FiniteSet:
other_points = [p for p in o if not self._contains(p)]
if len(other_points) == len(o):
return None
return Union(self, FiniteSet(*other_points))
if self._contains(o):
return self
return None
@dispatch(GeometrySet, Set) # type: ignore # noqa:F811
def intersection_sets(self, o): # noqa:F811
""" Returns a sympy.sets.Set of intersection objects,
if possible. """
from sympy.geometry.point import Point
try:
# if o is a FiniteSet, find the intersection directly
# to avoid infinite recursion
if o.is_FiniteSet:
inter = FiniteSet(*(p for p in o if self.contains(p)))
else:
inter = self.intersection(o)
except NotImplementedError:
# sympy.sets.Set.reduce expects None if an object
# doesn't know how to simplify
return None
# put the points in a FiniteSet
points = FiniteSet(*[p for p in inter if isinstance(p, Point)])
non_points = [p for p in inter if not isinstance(p, Point)]
return Union(*(non_points + [points]))
def translate(x, y):
"""Return the matrix to translate a 2-D point by x and y."""
rv = eye(3)
rv[2, 0] = x
rv[2, 1] = y
return rv
def scale(x, y, pt=None):
"""Return the matrix to multiply a 2-D point's coordinates by x and y.
If pt is given, the scaling is done relative to that point."""
rv = eye(3)
rv[0, 0] = x
rv[1, 1] = y
if pt:
from sympy.geometry.point import Point
pt = Point(pt, dim=2)
tr1 = translate(*(-pt).args)
tr2 = translate(*pt.args)
return tr1*rv*tr2
return rv
def rotate(th):
"""Return the matrix to rotate a 2-D point about the origin by ``angle``.
The angle is measured in radians. To Point a point about a point other
then the origin, translate the Point, do the rotation, and
translate it back:
>>> from sympy.geometry.entity import rotate, translate
>>> from sympy import Point, pi
>>> rot_about_11 = translate(-1, -1)*rotate(pi/2)*translate(1, 1)
>>> Point(1, 1).transform(rot_about_11)
Point2D(1, 1)
>>> Point(0, 0).transform(rot_about_11)
Point2D(2, 0)
"""
s = sin(th)
rv = eye(3)*cos(th)
rv[0, 1] = s
rv[1, 0] = -s
rv[2, 2] = 1
return rv

View File

@ -0,0 +1,5 @@
"""Geometry Errors."""
class GeometryError(ValueError):
"""An exception raised by classes in the geometry module."""
pass

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,422 @@
"""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

View File

@ -0,0 +1,885 @@
"""Geometrical Planes.
Contains
========
Plane
"""
from sympy.core import Dummy, Rational, S, Symbol
from sympy.core.symbol import _symbol
from sympy.functions.elementary.trigonometric import cos, sin, acos, asin, sqrt
from .entity import GeometryEntity
from .line import (Line, Ray, Segment, Line3D, LinearEntity, LinearEntity3D,
Ray3D, Segment3D)
from .point import Point, Point3D
from sympy.matrices import Matrix
from sympy.polys.polytools import cancel
from sympy.solvers import solve, linsolve
from sympy.utilities.iterables import uniq, is_sequence
from sympy.utilities.misc import filldedent, func_name, Undecidable
from mpmath.libmp.libmpf import prec_to_dps
import random
x, y, z, t = [Dummy('plane_dummy') for i in range(4)]
class Plane(GeometryEntity):
"""
A plane is a flat, two-dimensional surface. A plane is the two-dimensional
analogue of a point (zero-dimensions), a line (one-dimension) and a solid
(three-dimensions). A plane can generally be constructed by two types of
inputs. They are:
- three non-collinear points
- a point and the plane's normal vector
Attributes
==========
p1
normal_vector
Examples
========
>>> from sympy import Plane, Point3D
>>> Plane(Point3D(1, 1, 1), Point3D(2, 3, 4), Point3D(2, 2, 2))
Plane(Point3D(1, 1, 1), (-1, 2, -1))
>>> Plane((1, 1, 1), (2, 3, 4), (2, 2, 2))
Plane(Point3D(1, 1, 1), (-1, 2, -1))
>>> Plane(Point3D(1, 1, 1), normal_vector=(1,4,7))
Plane(Point3D(1, 1, 1), (1, 4, 7))
"""
def __new__(cls, p1, a=None, b=None, **kwargs):
p1 = Point3D(p1, dim=3)
if a and b:
p2 = Point(a, dim=3)
p3 = Point(b, dim=3)
if Point3D.are_collinear(p1, p2, p3):
raise ValueError('Enter three non-collinear points')
a = p1.direction_ratio(p2)
b = p1.direction_ratio(p3)
normal_vector = tuple(Matrix(a).cross(Matrix(b)))
else:
a = kwargs.pop('normal_vector', a)
evaluate = kwargs.get('evaluate', True)
if is_sequence(a) and len(a) == 3:
normal_vector = Point3D(a).args if evaluate else a
else:
raise ValueError(filldedent('''
Either provide 3 3D points or a point with a
normal vector expressed as a sequence of length 3'''))
if all(coord.is_zero for coord in normal_vector):
raise ValueError('Normal vector cannot be zero vector')
return GeometryEntity.__new__(cls, p1, normal_vector, **kwargs)
def __contains__(self, o):
k = self.equation(x, y, z)
if isinstance(o, (LinearEntity, LinearEntity3D)):
d = Point3D(o.arbitrary_point(t))
e = k.subs([(x, d.x), (y, d.y), (z, d.z)])
return e.equals(0)
try:
o = Point(o, dim=3, strict=True)
d = k.xreplace(dict(zip((x, y, z), o.args)))
return d.equals(0)
except TypeError:
return False
def _eval_evalf(self, prec=15, **options):
pt, tup = self.args
dps = prec_to_dps(prec)
pt = pt.evalf(n=dps, **options)
tup = tuple([i.evalf(n=dps, **options) for i in tup])
return self.func(pt, normal_vector=tup, evaluate=False)
def angle_between(self, o):
"""Angle between the plane and other geometric entity.
Parameters
==========
LinearEntity3D, Plane.
Returns
=======
angle : angle in radians
Notes
=====
This method accepts only 3D entities as it's parameter, but if you want
to calculate the angle between a 2D entity and a plane you should
first convert to a 3D entity by projecting onto a desired plane and
then proceed to calculate the angle.
Examples
========
>>> from sympy import Point3D, Line3D, Plane
>>> a = Plane(Point3D(1, 2, 2), normal_vector=(1, 2, 3))
>>> b = Line3D(Point3D(1, 3, 4), Point3D(2, 2, 2))
>>> a.angle_between(b)
-asin(sqrt(21)/6)
"""
if isinstance(o, LinearEntity3D):
a = Matrix(self.normal_vector)
b = Matrix(o.direction_ratio)
c = a.dot(b)
d = sqrt(sum(i**2 for i in self.normal_vector))
e = sqrt(sum(i**2 for i in o.direction_ratio))
return asin(c/(d*e))
if isinstance(o, Plane):
a = Matrix(self.normal_vector)
b = Matrix(o.normal_vector)
c = a.dot(b)
d = sqrt(sum(i**2 for i in self.normal_vector))
e = sqrt(sum(i**2 for i in o.normal_vector))
return acos(c/(d*e))
def arbitrary_point(self, u=None, v=None):
""" Returns an arbitrary point on the Plane. If given two
parameters, the point ranges over the entire plane. If given 1
or no parameters, returns a point with one parameter which,
when varying from 0 to 2*pi, moves the point in a circle of
radius 1 about p1 of the Plane.
Examples
========
>>> from sympy import Plane, Ray
>>> from sympy.abc import u, v, t, r
>>> p = Plane((1, 1, 1), normal_vector=(1, 0, 0))
>>> p.arbitrary_point(u, v)
Point3D(1, u + 1, v + 1)
>>> p.arbitrary_point(t)
Point3D(1, cos(t) + 1, sin(t) + 1)
While arbitrary values of u and v can move the point anywhere in
the plane, the single-parameter point can be used to construct a
ray whose arbitrary point can be located at angle t and radius
r from p.p1:
>>> Ray(p.p1, _).arbitrary_point(r)
Point3D(1, r*cos(t) + 1, r*sin(t) + 1)
Returns
=======
Point3D
"""
circle = v is None
if circle:
u = _symbol(u or 't', real=True)
else:
u = _symbol(u or 'u', real=True)
v = _symbol(v or 'v', real=True)
x, y, z = self.normal_vector
a, b, c = self.p1.args
# x1, y1, z1 is a nonzero vector parallel to the plane
if x.is_zero and y.is_zero:
x1, y1, z1 = S.One, S.Zero, S.Zero
else:
x1, y1, z1 = -y, x, S.Zero
# x2, y2, z2 is also parallel to the plane, and orthogonal to x1, y1, z1
x2, y2, z2 = tuple(Matrix((x, y, z)).cross(Matrix((x1, y1, z1))))
if circle:
x1, y1, z1 = (w/sqrt(x1**2 + y1**2 + z1**2) for w in (x1, y1, z1))
x2, y2, z2 = (w/sqrt(x2**2 + y2**2 + z2**2) for w in (x2, y2, z2))
p = Point3D(a + x1*cos(u) + x2*sin(u), \
b + y1*cos(u) + y2*sin(u), \
c + z1*cos(u) + z2*sin(u))
else:
p = Point3D(a + x1*u + x2*v, b + y1*u + y2*v, c + z1*u + z2*v)
return p
@staticmethod
def are_concurrent(*planes):
"""Is a sequence of Planes concurrent?
Two or more Planes are concurrent if their intersections
are a common line.
Parameters
==========
planes: list
Returns
=======
Boolean
Examples
========
>>> from sympy import Plane, Point3D
>>> a = Plane(Point3D(5, 0, 0), normal_vector=(1, -1, 1))
>>> b = Plane(Point3D(0, -2, 0), normal_vector=(3, 1, 1))
>>> c = Plane(Point3D(0, -1, 0), normal_vector=(5, -1, 9))
>>> Plane.are_concurrent(a, b)
True
>>> Plane.are_concurrent(a, b, c)
False
"""
planes = list(uniq(planes))
for i in planes:
if not isinstance(i, Plane):
raise ValueError('All objects should be Planes but got %s' % i.func)
if len(planes) < 2:
return False
planes = list(planes)
first = planes.pop(0)
sol = first.intersection(planes[0])
if sol == []:
return False
else:
line = sol[0]
for i in planes[1:]:
l = first.intersection(i)
if not l or l[0] not in line:
return False
return True
def distance(self, o):
"""Distance between the plane and another geometric entity.
Parameters
==========
Point3D, LinearEntity3D, Plane.
Returns
=======
distance
Notes
=====
This method accepts only 3D entities as it's parameter, but if you want
to calculate the distance between a 2D entity and a plane you should
first convert to a 3D entity by projecting onto a desired plane and
then proceed to calculate the distance.
Examples
========
>>> from sympy import Point3D, Line3D, Plane
>>> a = Plane(Point3D(1, 1, 1), normal_vector=(1, 1, 1))
>>> b = Point3D(1, 2, 3)
>>> a.distance(b)
sqrt(3)
>>> c = Line3D(Point3D(2, 3, 1), Point3D(1, 2, 2))
>>> a.distance(c)
0
"""
if self.intersection(o) != []:
return S.Zero
if isinstance(o, (Segment3D, Ray3D)):
a, b = o.p1, o.p2
pi, = self.intersection(Line3D(a, b))
if pi in o:
return self.distance(pi)
elif a in Segment3D(pi, b):
return self.distance(a)
else:
assert isinstance(o, Segment3D) is True
return self.distance(b)
# following code handles `Point3D`, `LinearEntity3D`, `Plane`
a = o if isinstance(o, Point3D) else o.p1
n = Point3D(self.normal_vector).unit
d = (a - self.p1).dot(n)
return abs(d)
def equals(self, o):
"""
Returns True if self and o are the same mathematical entities.
Examples
========
>>> from sympy import Plane, Point3D
>>> a = Plane(Point3D(1, 2, 3), normal_vector=(1, 1, 1))
>>> b = Plane(Point3D(1, 2, 3), normal_vector=(2, 2, 2))
>>> c = Plane(Point3D(1, 2, 3), normal_vector=(-1, 4, 6))
>>> a.equals(a)
True
>>> a.equals(b)
True
>>> a.equals(c)
False
"""
if isinstance(o, Plane):
a = self.equation()
b = o.equation()
return cancel(a/b).is_constant()
else:
return False
def equation(self, x=None, y=None, z=None):
"""The equation of the Plane.
Examples
========
>>> from sympy import Point3D, Plane
>>> a = Plane(Point3D(1, 1, 2), Point3D(2, 4, 7), Point3D(3, 5, 1))
>>> a.equation()
-23*x + 11*y - 2*z + 16
>>> a = Plane(Point3D(1, 4, 2), normal_vector=(6, 6, 6))
>>> a.equation()
6*x + 6*y + 6*z - 42
"""
x, y, z = [i if i else Symbol(j, real=True) for i, j in zip((x, y, z), 'xyz')]
a = Point3D(x, y, z)
b = self.p1.direction_ratio(a)
c = self.normal_vector
return (sum(i*j for i, j in zip(b, c)))
def intersection(self, o):
""" The intersection with other geometrical entity.
Parameters
==========
Point, Point3D, LinearEntity, LinearEntity3D, Plane
Returns
=======
List
Examples
========
>>> from sympy import Point3D, Line3D, Plane
>>> a = Plane(Point3D(1, 2, 3), normal_vector=(1, 1, 1))
>>> b = Point3D(1, 2, 3)
>>> a.intersection(b)
[Point3D(1, 2, 3)]
>>> c = Line3D(Point3D(1, 4, 7), Point3D(2, 2, 2))
>>> a.intersection(c)
[Point3D(2, 2, 2)]
>>> d = Plane(Point3D(6, 0, 0), normal_vector=(2, -5, 3))
>>> e = Plane(Point3D(2, 0, 0), normal_vector=(3, 4, -3))
>>> d.intersection(e)
[Line3D(Point3D(78/23, -24/23, 0), Point3D(147/23, 321/23, 23))]
"""
if not isinstance(o, GeometryEntity):
o = Point(o, dim=3)
if isinstance(o, Point):
if o in self:
return [o]
else:
return []
if isinstance(o, (LinearEntity, LinearEntity3D)):
# recast to 3D
p1, p2 = o.p1, o.p2
if isinstance(o, Segment):
o = Segment3D(p1, p2)
elif isinstance(o, Ray):
o = Ray3D(p1, p2)
elif isinstance(o, Line):
o = Line3D(p1, p2)
else:
raise ValueError('unhandled linear entity: %s' % o.func)
if o in self:
return [o]
else:
a = Point3D(o.arbitrary_point(t))
p1, n = self.p1, Point3D(self.normal_vector)
# TODO: Replace solve with solveset, when this line is tested
c = solve((a - p1).dot(n), t)
if not c:
return []
else:
c = [i for i in c if i.is_real is not False]
if len(c) > 1:
c = [i for i in c if i.is_real]
if len(c) != 1:
raise Undecidable("not sure which point is real")
p = a.subs(t, c[0])
if p not in o:
return [] # e.g. a segment might not intersect a plane
return [p]
if isinstance(o, Plane):
if self.equals(o):
return [self]
if self.is_parallel(o):
return []
else:
x, y, z = map(Dummy, 'xyz')
a, b = Matrix([self.normal_vector]), Matrix([o.normal_vector])
c = list(a.cross(b))
d = self.equation(x, y, z)
e = o.equation(x, y, z)
result = list(linsolve([d, e], x, y, z))[0]
for i in (x, y, z): result = result.subs(i, 0)
return [Line3D(Point3D(result), direction_ratio=c)]
def is_coplanar(self, o):
""" Returns True if `o` is coplanar with self, else False.
Examples
========
>>> from sympy import Plane
>>> o = (0, 0, 0)
>>> p = Plane(o, (1, 1, 1))
>>> p2 = Plane(o, (2, 2, 2))
>>> p == p2
False
>>> p.is_coplanar(p2)
True
"""
if isinstance(o, Plane):
return not cancel(self.equation(x, y, z)/o.equation(x, y, z)).has(x, y, z)
if isinstance(o, Point3D):
return o in self
elif isinstance(o, LinearEntity3D):
return all(i in self for i in self)
elif isinstance(o, GeometryEntity): # XXX should only be handling 2D objects now
return all(i == 0 for i in self.normal_vector[:2])
def is_parallel(self, l):
"""Is the given geometric entity parallel to the plane?
Parameters
==========
LinearEntity3D or Plane
Returns
=======
Boolean
Examples
========
>>> from sympy import Plane, Point3D
>>> a = Plane(Point3D(1,4,6), normal_vector=(2, 4, 6))
>>> b = Plane(Point3D(3,1,3), normal_vector=(4, 8, 12))
>>> a.is_parallel(b)
True
"""
if isinstance(l, LinearEntity3D):
a = l.direction_ratio
b = self.normal_vector
c = sum(i*j for i, j in zip(a, b))
if c == 0:
return True
else:
return False
elif isinstance(l, Plane):
a = Matrix(l.normal_vector)
b = Matrix(self.normal_vector)
if a.cross(b).is_zero_matrix:
return True
else:
return False
def is_perpendicular(self, l):
"""Is the given geometric entity perpendicualar to the given plane?
Parameters
==========
LinearEntity3D or Plane
Returns
=======
Boolean
Examples
========
>>> from sympy import Plane, Point3D
>>> a = Plane(Point3D(1,4,6), normal_vector=(2, 4, 6))
>>> b = Plane(Point3D(2, 2, 2), normal_vector=(-1, 2, -1))
>>> a.is_perpendicular(b)
True
"""
if isinstance(l, LinearEntity3D):
a = Matrix(l.direction_ratio)
b = Matrix(self.normal_vector)
if a.cross(b).is_zero_matrix:
return True
else:
return False
elif isinstance(l, Plane):
a = Matrix(l.normal_vector)
b = Matrix(self.normal_vector)
if a.dot(b) == 0:
return True
else:
return False
else:
return False
@property
def normal_vector(self):
"""Normal vector of the given plane.
Examples
========
>>> from sympy import Point3D, Plane
>>> a = Plane(Point3D(1, 1, 1), Point3D(2, 3, 4), Point3D(2, 2, 2))
>>> a.normal_vector
(-1, 2, -1)
>>> a = Plane(Point3D(1, 1, 1), normal_vector=(1, 4, 7))
>>> a.normal_vector
(1, 4, 7)
"""
return self.args[1]
@property
def p1(self):
"""The only defining point of the plane. Others can be obtained from the
arbitrary_point method.
See Also
========
sympy.geometry.point.Point3D
Examples
========
>>> from sympy import Point3D, Plane
>>> a = Plane(Point3D(1, 1, 1), Point3D(2, 3, 4), Point3D(2, 2, 2))
>>> a.p1
Point3D(1, 1, 1)
"""
return self.args[0]
def parallel_plane(self, pt):
"""
Plane parallel to the given plane and passing through the point pt.
Parameters
==========
pt: Point3D
Returns
=======
Plane
Examples
========
>>> from sympy import Plane, Point3D
>>> a = Plane(Point3D(1, 4, 6), normal_vector=(2, 4, 6))
>>> a.parallel_plane(Point3D(2, 3, 5))
Plane(Point3D(2, 3, 5), (2, 4, 6))
"""
a = self.normal_vector
return Plane(pt, normal_vector=a)
def perpendicular_line(self, pt):
"""A line perpendicular to the given plane.
Parameters
==========
pt: Point3D
Returns
=======
Line3D
Examples
========
>>> from sympy import Plane, Point3D
>>> a = Plane(Point3D(1,4,6), normal_vector=(2, 4, 6))
>>> a.perpendicular_line(Point3D(9, 8, 7))
Line3D(Point3D(9, 8, 7), Point3D(11, 12, 13))
"""
a = self.normal_vector
return Line3D(pt, direction_ratio=a)
def perpendicular_plane(self, *pts):
"""
Return a perpendicular passing through the given points. If the
direction ratio between the points is the same as the Plane's normal
vector then, to select from the infinite number of possible planes,
a third point will be chosen on the z-axis (or the y-axis
if the normal vector is already parallel to the z-axis). If less than
two points are given they will be supplied as follows: if no point is
given then pt1 will be self.p1; if a second point is not given it will
be a point through pt1 on a line parallel to the z-axis (if the normal
is not already the z-axis, otherwise on the line parallel to the
y-axis).
Parameters
==========
pts: 0, 1 or 2 Point3D
Returns
=======
Plane
Examples
========
>>> from sympy import Plane, Point3D
>>> a, b = Point3D(0, 0, 0), Point3D(0, 1, 0)
>>> Z = (0, 0, 1)
>>> p = Plane(a, normal_vector=Z)
>>> p.perpendicular_plane(a, b)
Plane(Point3D(0, 0, 0), (1, 0, 0))
"""
if len(pts) > 2:
raise ValueError('No more than 2 pts should be provided.')
pts = list(pts)
if len(pts) == 0:
pts.append(self.p1)
if len(pts) == 1:
x, y, z = self.normal_vector
if x == y == 0:
dir = (0, 1, 0)
else:
dir = (0, 0, 1)
pts.append(pts[0] + Point3D(*dir))
p1, p2 = [Point(i, dim=3) for i in pts]
l = Line3D(p1, p2)
n = Line3D(p1, direction_ratio=self.normal_vector)
if l in n: # XXX should an error be raised instead?
# there are infinitely many perpendicular planes;
x, y, z = self.normal_vector
if x == y == 0:
# the z axis is the normal so pick a pt on the y-axis
p3 = Point3D(0, 1, 0) # case 1
else:
# else pick a pt on the z axis
p3 = Point3D(0, 0, 1) # case 2
# in case that point is already given, move it a bit
if p3 in l:
p3 *= 2 # case 3
else:
p3 = p1 + Point3D(*self.normal_vector) # case 4
return Plane(p1, p2, p3)
def projection_line(self, line):
"""Project the given line onto the plane through the normal plane
containing the line.
Parameters
==========
LinearEntity or LinearEntity3D
Returns
=======
Point3D, Line3D, Ray3D or Segment3D
Notes
=====
For the interaction between 2D and 3D lines(segments, rays), you should
convert the line to 3D by using this method. For example for finding the
intersection between a 2D and a 3D line, convert the 2D line to a 3D line
by projecting it on a required plane and then proceed to find the
intersection between those lines.
Examples
========
>>> from sympy import Plane, Line, Line3D, Point3D
>>> a = Plane(Point3D(1, 1, 1), normal_vector=(1, 1, 1))
>>> b = Line(Point3D(1, 1), Point3D(2, 2))
>>> a.projection_line(b)
Line3D(Point3D(4/3, 4/3, 1/3), Point3D(5/3, 5/3, -1/3))
>>> c = Line3D(Point3D(1, 1, 1), Point3D(2, 2, 2))
>>> a.projection_line(c)
Point3D(1, 1, 1)
"""
if not isinstance(line, (LinearEntity, LinearEntity3D)):
raise NotImplementedError('Enter a linear entity only')
a, b = self.projection(line.p1), self.projection(line.p2)
if a == b:
# projection does not imply intersection so for
# this case (line parallel to plane's normal) we
# return the projection point
return a
if isinstance(line, (Line, Line3D)):
return Line3D(a, b)
if isinstance(line, (Ray, Ray3D)):
return Ray3D(a, b)
if isinstance(line, (Segment, Segment3D)):
return Segment3D(a, b)
def projection(self, pt):
"""Project the given point onto the plane along the plane normal.
Parameters
==========
Point or Point3D
Returns
=======
Point3D
Examples
========
>>> from sympy import Plane, Point3D
>>> A = Plane(Point3D(1, 1, 2), normal_vector=(1, 1, 1))
The projection is along the normal vector direction, not the z
axis, so (1, 1) does not project to (1, 1, 2) on the plane A:
>>> b = Point3D(1, 1)
>>> A.projection(b)
Point3D(5/3, 5/3, 2/3)
>>> _ in A
True
But the point (1, 1, 2) projects to (1, 1) on the XY-plane:
>>> XY = Plane((0, 0, 0), (0, 0, 1))
>>> XY.projection((1, 1, 2))
Point3D(1, 1, 0)
"""
rv = Point(pt, dim=3)
if rv in self:
return rv
return self.intersection(Line3D(rv, rv + Point3D(self.normal_vector)))[0]
def random_point(self, seed=None):
""" Returns a random point on the Plane.
Returns
=======
Point3D
Examples
========
>>> from sympy import Plane
>>> p = Plane((1, 0, 0), normal_vector=(0, 1, 0))
>>> r = p.random_point(seed=42) # seed value is optional
>>> r.n(3)
Point3D(2.29, 0, -1.35)
The random point can be moved to lie on the circle of radius
1 centered on p1:
>>> c = p.p1 + (r - p.p1).unit
>>> c.distance(p.p1).equals(1)
True
"""
if seed is not None:
rng = random.Random(seed)
else:
rng = random
params = {
x: 2*Rational(rng.gauss(0, 1)) - 1,
y: 2*Rational(rng.gauss(0, 1)) - 1}
return self.arbitrary_point(x, y).subs(params)
def parameter_value(self, other, u, v=None):
"""Return the parameter(s) corresponding to the given point.
Examples
========
>>> from sympy import pi, Plane
>>> from sympy.abc import t, u, v
>>> p = Plane((2, 0, 0), (0, 0, 1), (0, 1, 0))
By default, the parameter value returned defines a point
that is a distance of 1 from the Plane's p1 value and
in line with the given point:
>>> on_circle = p.arbitrary_point(t).subs(t, pi/4)
>>> on_circle.distance(p.p1)
1
>>> p.parameter_value(on_circle, t)
{t: pi/4}
Moving the point twice as far from p1 does not change
the parameter value:
>>> off_circle = p.p1 + (on_circle - p.p1)*2
>>> off_circle.distance(p.p1)
2
>>> p.parameter_value(off_circle, t)
{t: pi/4}
If the 2-value parameter is desired, supply the two
parameter symbols and a replacement dictionary will
be returned:
>>> p.parameter_value(on_circle, u, v)
{u: sqrt(10)/10, v: sqrt(10)/30}
>>> p.parameter_value(off_circle, u, v)
{u: sqrt(10)/5, v: sqrt(10)/15}
"""
if not isinstance(other, GeometryEntity):
other = Point(other, dim=self.ambient_dimension)
if not isinstance(other, Point):
raise ValueError("other must be a point")
if other == self.p1:
return other
if isinstance(u, Symbol) and v is None:
delta = self.arbitrary_point(u) - self.p1
eq = delta - (other - self.p1).unit
sol = solve(eq, u, dict=True)
elif isinstance(u, Symbol) and isinstance(v, Symbol):
pt = self.arbitrary_point(u, v)
sol = solve(pt - other, (u, v), dict=True)
else:
raise ValueError('expecting 1 or 2 symbols')
if not sol:
raise ValueError("Given point is not on %s" % func_name(self))
return sol[0] # {t: tval} or {u: uval, v: vval}
@property
def ambient_dimension(self):
return self.p1.ambient_dimension

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,120 @@
from sympy.core.containers import Tuple
from sympy.core.numbers import (Rational, pi)
from sympy.core.singleton import S
from sympy.core.symbol import (Symbol, symbols)
from sympy.functions.elementary.hyperbolic import asinh
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.geometry import Curve, Line, Point, Ellipse, Ray, Segment, Circle, Polygon, RegularPolygon
from sympy.testing.pytest import raises, slow
def test_curve():
x = Symbol('x', real=True)
s = Symbol('s')
z = Symbol('z')
# this curve is independent of the indicated parameter
c = Curve([2*s, s**2], (z, 0, 2))
assert c.parameter == z
assert c.functions == (2*s, s**2)
assert c.arbitrary_point() == Point(2*s, s**2)
assert c.arbitrary_point(z) == Point(2*s, s**2)
# this is how it is normally used
c = Curve([2*s, s**2], (s, 0, 2))
assert c.parameter == s
assert c.functions == (2*s, s**2)
t = Symbol('t')
# the t returned as assumptions
assert c.arbitrary_point() != Point(2*t, t**2)
t = Symbol('t', real=True)
# now t has the same assumptions so the test passes
assert c.arbitrary_point() == Point(2*t, t**2)
assert c.arbitrary_point(z) == Point(2*z, z**2)
assert c.arbitrary_point(c.parameter) == Point(2*s, s**2)
assert c.arbitrary_point(None) == Point(2*s, s**2)
assert c.plot_interval() == [t, 0, 2]
assert c.plot_interval(z) == [z, 0, 2]
assert Curve([x, x], (x, 0, 1)).rotate(pi/2) == Curve([-x, x], (x, 0, 1))
assert Curve([x, x], (x, 0, 1)).rotate(pi/2, (1, 2)).scale(2, 3).translate(
1, 3).arbitrary_point(s) == \
Line((0, 0), (1, 1)).rotate(pi/2, (1, 2)).scale(2, 3).translate(
1, 3).arbitrary_point(s) == \
Point(-2*s + 7, 3*s + 6)
raises(ValueError, lambda: Curve((s), (s, 1, 2)))
raises(ValueError, lambda: Curve((x, x * 2), (1, x)))
raises(ValueError, lambda: Curve((s, s + t), (s, 1, 2)).arbitrary_point())
raises(ValueError, lambda: Curve((s, s + t), (t, 1, 2)).arbitrary_point(s))
@slow
def test_free_symbols():
a, b, c, d, e, f, s = symbols('a:f,s')
assert Point(a, b).free_symbols == {a, b}
assert Line((a, b), (c, d)).free_symbols == {a, b, c, d}
assert Ray((a, b), (c, d)).free_symbols == {a, b, c, d}
assert Ray((a, b), angle=c).free_symbols == {a, b, c}
assert Segment((a, b), (c, d)).free_symbols == {a, b, c, d}
assert Line((a, b), slope=c).free_symbols == {a, b, c}
assert Curve((a*s, b*s), (s, c, d)).free_symbols == {a, b, c, d}
assert Ellipse((a, b), c, d).free_symbols == {a, b, c, d}
assert Ellipse((a, b), c, eccentricity=d).free_symbols == \
{a, b, c, d}
assert Ellipse((a, b), vradius=c, eccentricity=d).free_symbols == \
{a, b, c, d}
assert Circle((a, b), c).free_symbols == {a, b, c}
assert Circle((a, b), (c, d), (e, f)).free_symbols == \
{e, d, c, b, f, a}
assert Polygon((a, b), (c, d), (e, f)).free_symbols == \
{e, b, d, f, a, c}
assert RegularPolygon((a, b), c, d, e).free_symbols == {e, a, b, c, d}
def test_transform():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
c = Curve((x, x**2), (x, 0, 1))
cout = Curve((2*x - 4, 3*x**2 - 10), (x, 0, 1))
pts = [Point(0, 0), Point(S.Half, Rational(1, 4)), Point(1, 1)]
pts_out = [Point(-4, -10), Point(-3, Rational(-37, 4)), Point(-2, -7)]
assert c.scale(2, 3, (4, 5)) == cout
assert [c.subs(x, xi/2) for xi in Tuple(0, 1, 2)] == pts
assert [cout.subs(x, xi/2) for xi in Tuple(0, 1, 2)] == pts_out
assert Curve((x + y, 3*x), (x, 0, 1)).subs(y, S.Half) == \
Curve((x + S.Half, 3*x), (x, 0, 1))
assert Curve((x, 3*x), (x, 0, 1)).translate(4, 5) == \
Curve((x + 4, 3*x + 5), (x, 0, 1))
def test_length():
t = Symbol('t', real=True)
c1 = Curve((t, 0), (t, 0, 1))
assert c1.length == 1
c2 = Curve((t, t), (t, 0, 1))
assert c2.length == sqrt(2)
c3 = Curve((t ** 2, t), (t, 2, 5))
assert c3.length == -sqrt(17) - asinh(4) / 4 + asinh(10) / 4 + 5 * sqrt(101) / 2
def test_parameter_value():
t = Symbol('t')
C = Curve([2*t, t**2], (t, 0, 2))
assert C.parameter_value((2, 1), t) == {t: 1}
raises(ValueError, lambda: C.parameter_value((2, 0), t))
def test_issue_17997():
t, s = symbols('t s')
c = Curve((t, t**2), (t, 0, 10))
p = Curve([2*s, s**2], (s, 0, 2))
assert c(2) == Point(2, 4)
assert p(1) == Point(2, 1)

View File

@ -0,0 +1,613 @@
from sympy.core import expand
from sympy.core.numbers import (Rational, oo, pi)
from sympy.core.relational import Eq
from sympy.core.singleton import S
from sympy.core.symbol import (Symbol, symbols)
from sympy.functions.elementary.complexes import Abs
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.trigonometric import sec
from sympy.geometry.line import Segment2D
from sympy.geometry.point import Point2D
from sympy.geometry import (Circle, Ellipse, GeometryError, Line, Point,
Polygon, Ray, RegularPolygon, Segment,
Triangle, intersection)
from sympy.testing.pytest import raises, slow
from sympy.integrals.integrals import integrate
from sympy.functions.special.elliptic_integrals import elliptic_e
from sympy.functions.elementary.miscellaneous import Max
def test_ellipse_equation_using_slope():
from sympy.abc import x, y
e1 = Ellipse(Point(1, 0), 3, 2)
assert str(e1.equation(_slope=1)) == str((-x + y + 1)**2/8 + (x + y - 1)**2/18 - 1)
e2 = Ellipse(Point(0, 0), 4, 1)
assert str(e2.equation(_slope=1)) == str((-x + y)**2/2 + (x + y)**2/32 - 1)
e3 = Ellipse(Point(1, 5), 6, 2)
assert str(e3.equation(_slope=2)) == str((-2*x + y - 3)**2/20 + (x + 2*y - 11)**2/180 - 1)
def test_object_from_equation():
from sympy.abc import x, y, a, b, c, d, e
assert Circle(x**2 + y**2 + 3*x + 4*y - 8) == Circle(Point2D(S(-3) / 2, -2), sqrt(57) / 2)
assert Circle(x**2 + y**2 + 6*x + 8*y + 25) == Circle(Point2D(-3, -4), 0)
assert Circle(a**2 + b**2 + 6*a + 8*b + 25, x='a', y='b') == Circle(Point2D(-3, -4), 0)
assert Circle(x**2 + y**2 - 25) == Circle(Point2D(0, 0), 5)
assert Circle(x**2 + y**2) == Circle(Point2D(0, 0), 0)
assert Circle(a**2 + b**2, x='a', y='b') == Circle(Point2D(0, 0), 0)
assert Circle(x**2 + y**2 + 6*x + 8) == Circle(Point2D(-3, 0), 1)
assert Circle(x**2 + y**2 + 6*y + 8) == Circle(Point2D(0, -3), 1)
assert Circle((x - 1)**2 + y**2 - 9) == Circle(Point2D(1, 0), 3)
assert Circle(6*(x**2) + 6*(y**2) + 6*x + 8*y - 25) == Circle(Point2D(Rational(-1, 2), Rational(-2, 3)), 5*sqrt(7)/6)
assert Circle(Eq(a**2 + b**2, 25), x='a', y=b) == Circle(Point2D(0, 0), 5)
raises(GeometryError, lambda: Circle(x**2 + y**2 + 3*x + 4*y + 26))
raises(GeometryError, lambda: Circle(x**2 + y**2 + 25))
raises(GeometryError, lambda: Circle(a**2 + b**2 + 25, x='a', y='b'))
raises(GeometryError, lambda: Circle(x**2 + 6*y + 8))
raises(GeometryError, lambda: Circle(6*(x ** 2) + 4*(y**2) + 6*x + 8*y + 25))
raises(ValueError, lambda: Circle(a**2 + b**2 + 3*a + 4*b - 8))
# .equation() adds 'real=True' assumption; '==' would fail if assumptions differed
x, y = symbols('x y', real=True)
eq = a*x**2 + a*y**2 + c*x + d*y + e
assert expand(Circle(eq).equation()*a) == eq
@slow
def test_ellipse_geom():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
t = Symbol('t', real=True)
y1 = Symbol('y1', real=True)
half = S.Half
p1 = Point(0, 0)
p2 = Point(1, 1)
p4 = Point(0, 1)
e1 = Ellipse(p1, 1, 1)
e2 = Ellipse(p2, half, 1)
e3 = Ellipse(p1, y1, y1)
c1 = Circle(p1, 1)
c2 = Circle(p2, 1)
c3 = Circle(Point(sqrt(2), sqrt(2)), 1)
l1 = Line(p1, p2)
# Test creation with three points
cen, rad = Point(3*half, 2), 5*half
assert Circle(Point(0, 0), Point(3, 0), Point(0, 4)) == Circle(cen, rad)
assert Circle(Point(0, 0), Point(1, 1), Point(2, 2)) == Segment2D(Point2D(0, 0), Point2D(2, 2))
raises(ValueError, lambda: Ellipse(None, None, None, 1))
raises(ValueError, lambda: Ellipse())
raises(GeometryError, lambda: Circle(Point(0, 0)))
raises(GeometryError, lambda: Circle(Symbol('x')*Symbol('y')))
# Basic Stuff
assert Ellipse(None, 1, 1).center == Point(0, 0)
assert e1 == c1
assert e1 != e2
assert e1 != l1
assert p4 in e1
assert e1 in e1
assert e2 in e2
assert 1 not in e2
assert p2 not in e2
assert e1.area == pi
assert e2.area == pi/2
assert e3.area == pi*y1*abs(y1)
assert c1.area == e1.area
assert c1.circumference == e1.circumference
assert e3.circumference == 2*pi*y1
assert e1.plot_interval() == e2.plot_interval() == [t, -pi, pi]
assert e1.plot_interval(x) == e2.plot_interval(x) == [x, -pi, pi]
assert c1.minor == 1
assert c1.major == 1
assert c1.hradius == 1
assert c1.vradius == 1
assert Ellipse((1, 1), 0, 0) == Point(1, 1)
assert Ellipse((1, 1), 1, 0) == Segment(Point(0, 1), Point(2, 1))
assert Ellipse((1, 1), 0, 1) == Segment(Point(1, 0), Point(1, 2))
# Private Functions
assert hash(c1) == hash(Circle(Point(1, 0), Point(0, 1), Point(0, -1)))
assert c1 in e1
assert (Line(p1, p2) in e1) is False
assert e1.__cmp__(e1) == 0
assert e1.__cmp__(Point(0, 0)) > 0
# Encloses
assert e1.encloses(Segment(Point(-0.5, -0.5), Point(0.5, 0.5))) is True
assert e1.encloses(Line(p1, p2)) is False
assert e1.encloses(Ray(p1, p2)) is False
assert e1.encloses(e1) is False
assert e1.encloses(
Polygon(Point(-0.5, -0.5), Point(-0.5, 0.5), Point(0.5, 0.5))) is True
assert e1.encloses(RegularPolygon(p1, 0.5, 3)) is True
assert e1.encloses(RegularPolygon(p1, 5, 3)) is False
assert e1.encloses(RegularPolygon(p2, 5, 3)) is False
assert e2.arbitrary_point() in e2
raises(ValueError, lambda: Ellipse(Point(x, y), 1, 1).arbitrary_point(parameter='x'))
# Foci
f1, f2 = Point(sqrt(12), 0), Point(-sqrt(12), 0)
ef = Ellipse(Point(0, 0), 4, 2)
assert ef.foci in [(f1, f2), (f2, f1)]
# Tangents
v = sqrt(2) / 2
p1_1 = Point(v, v)
p1_2 = p2 + Point(half, 0)
p1_3 = p2 + Point(0, 1)
assert e1.tangent_lines(p4) == c1.tangent_lines(p4)
assert e2.tangent_lines(p1_2) == [Line(Point(Rational(3, 2), 1), Point(Rational(3, 2), S.Half))]
assert e2.tangent_lines(p1_3) == [Line(Point(1, 2), Point(Rational(5, 4), 2))]
assert c1.tangent_lines(p1_1) != [Line(p1_1, Point(0, sqrt(2)))]
assert c1.tangent_lines(p1) == []
assert e2.is_tangent(Line(p1_2, p2 + Point(half, 1)))
assert e2.is_tangent(Line(p1_3, p2 + Point(half, 1)))
assert c1.is_tangent(Line(p1_1, Point(0, sqrt(2))))
assert e1.is_tangent(Line(Point(0, 0), Point(1, 1))) is False
assert c1.is_tangent(e1) is True
assert c1.is_tangent(Ellipse(Point(2, 0), 1, 1)) is True
assert c1.is_tangent(
Polygon(Point(1, 1), Point(1, -1), Point(2, 0))) is False
assert c1.is_tangent(
Polygon(Point(1, 1), Point(1, 0), Point(2, 0))) is False
assert Circle(Point(5, 5), 3).is_tangent(Circle(Point(0, 5), 1)) is False
assert Ellipse(Point(5, 5), 2, 1).tangent_lines(Point(0, 0)) == \
[Line(Point(0, 0), Point(Rational(77, 25), Rational(132, 25))),
Line(Point(0, 0), Point(Rational(33, 5), Rational(22, 5)))]
assert Ellipse(Point(5, 5), 2, 1).tangent_lines(Point(3, 4)) == \
[Line(Point(3, 4), Point(4, 4)), Line(Point(3, 4), Point(3, 5))]
assert Circle(Point(5, 5), 2).tangent_lines(Point(3, 3)) == \
[Line(Point(3, 3), Point(4, 3)), Line(Point(3, 3), Point(3, 4))]
assert Circle(Point(5, 5), 2).tangent_lines(Point(5 - 2*sqrt(2), 5)) == \
[Line(Point(5 - 2*sqrt(2), 5), Point(5 - sqrt(2), 5 - sqrt(2))),
Line(Point(5 - 2*sqrt(2), 5), Point(5 - sqrt(2), 5 + sqrt(2))), ]
assert Circle(Point(5, 5), 5).tangent_lines(Point(4, 0)) == \
[Line(Point(4, 0), Point(Rational(40, 13), Rational(5, 13))),
Line(Point(4, 0), Point(5, 0))]
assert Circle(Point(5, 5), 5).tangent_lines(Point(0, 6)) == \
[Line(Point(0, 6), Point(0, 7)),
Line(Point(0, 6), Point(Rational(5, 13), Rational(90, 13)))]
# for numerical calculations, we shouldn't demand exact equality,
# so only test up to the desired precision
def lines_close(l1, l2, prec):
""" tests whether l1 and 12 are within 10**(-prec)
of each other """
return abs(l1.p1 - l2.p1) < 10**(-prec) and abs(l1.p2 - l2.p2) < 10**(-prec)
def line_list_close(ll1, ll2, prec):
return all(lines_close(l1, l2, prec) for l1, l2 in zip(ll1, ll2))
e = Ellipse(Point(0, 0), 2, 1)
assert e.normal_lines(Point(0, 0)) == \
[Line(Point(0, 0), Point(0, 1)), Line(Point(0, 0), Point(1, 0))]
assert e.normal_lines(Point(1, 0)) == \
[Line(Point(0, 0), Point(1, 0))]
assert e.normal_lines((0, 1)) == \
[Line(Point(0, 0), Point(0, 1))]
assert line_list_close(e.normal_lines(Point(1, 1), 2), [
Line(Point(Rational(-51, 26), Rational(-1, 5)), Point(Rational(-25, 26), Rational(17, 83))),
Line(Point(Rational(28, 29), Rational(-7, 8)), Point(Rational(57, 29), Rational(-9, 2)))], 2)
# test the failure of Poly.intervals and checks a point on the boundary
p = Point(sqrt(3), S.Half)
assert p in e
assert line_list_close(e.normal_lines(p, 2), [
Line(Point(Rational(-341, 171), Rational(-1, 13)), Point(Rational(-170, 171), Rational(5, 64))),
Line(Point(Rational(26, 15), Rational(-1, 2)), Point(Rational(41, 15), Rational(-43, 26)))], 2)
# be sure to use the slope that isn't undefined on boundary
e = Ellipse((0, 0), 2, 2*sqrt(3)/3)
assert line_list_close(e.normal_lines((1, 1), 2), [
Line(Point(Rational(-64, 33), Rational(-20, 71)), Point(Rational(-31, 33), Rational(2, 13))),
Line(Point(1, -1), Point(2, -4))], 2)
# general ellipse fails except under certain conditions
e = Ellipse((0, 0), x, 1)
assert e.normal_lines((x + 1, 0)) == [Line(Point(0, 0), Point(1, 0))]
raises(NotImplementedError, lambda: e.normal_lines((x + 1, 1)))
# Properties
major = 3
minor = 1
e4 = Ellipse(p2, minor, major)
assert e4.focus_distance == sqrt(major**2 - minor**2)
ecc = e4.focus_distance / major
assert e4.eccentricity == ecc
assert e4.periapsis == major*(1 - ecc)
assert e4.apoapsis == major*(1 + ecc)
assert e4.semilatus_rectum == major*(1 - ecc ** 2)
# independent of orientation
e4 = Ellipse(p2, major, minor)
assert e4.focus_distance == sqrt(major**2 - minor**2)
ecc = e4.focus_distance / major
assert e4.eccentricity == ecc
assert e4.periapsis == major*(1 - ecc)
assert e4.apoapsis == major*(1 + ecc)
# Intersection
l1 = Line(Point(1, -5), Point(1, 5))
l2 = Line(Point(-5, -1), Point(5, -1))
l3 = Line(Point(-1, -1), Point(1, 1))
l4 = Line(Point(-10, 0), Point(0, 10))
pts_c1_l3 = [Point(sqrt(2)/2, sqrt(2)/2), Point(-sqrt(2)/2, -sqrt(2)/2)]
assert intersection(e2, l4) == []
assert intersection(c1, Point(1, 0)) == [Point(1, 0)]
assert intersection(c1, l1) == [Point(1, 0)]
assert intersection(c1, l2) == [Point(0, -1)]
assert intersection(c1, l3) in [pts_c1_l3, [pts_c1_l3[1], pts_c1_l3[0]]]
assert intersection(c1, c2) == [Point(0, 1), Point(1, 0)]
assert intersection(c1, c3) == [Point(sqrt(2)/2, sqrt(2)/2)]
assert e1.intersection(l1) == [Point(1, 0)]
assert e2.intersection(l4) == []
assert e1.intersection(Circle(Point(0, 2), 1)) == [Point(0, 1)]
assert e1.intersection(Circle(Point(5, 0), 1)) == []
assert e1.intersection(Ellipse(Point(2, 0), 1, 1)) == [Point(1, 0)]
assert e1.intersection(Ellipse(Point(5, 0), 1, 1)) == []
assert e1.intersection(Point(2, 0)) == []
assert e1.intersection(e1) == e1
assert intersection(Ellipse(Point(0, 0), 2, 1), Ellipse(Point(3, 0), 1, 2)) == [Point(2, 0)]
assert intersection(Circle(Point(0, 0), 2), Circle(Point(3, 0), 1)) == [Point(2, 0)]
assert intersection(Circle(Point(0, 0), 2), Circle(Point(7, 0), 1)) == []
assert intersection(Ellipse(Point(0, 0), 5, 17), Ellipse(Point(4, 0), 1, 0.2)
) == [Point(5.0, 0, evaluate=False)]
assert intersection(Ellipse(Point(0, 0), 5, 17), Ellipse(Point(4, 0), 0.999, 0.2)) == []
assert Circle((0, 0), S.Half).intersection(
Triangle((-1, 0), (1, 0), (0, 1))) == [
Point(Rational(-1, 2), 0), Point(S.Half, 0)]
raises(TypeError, lambda: intersection(e2, Line((0, 0, 0), (0, 0, 1))))
raises(TypeError, lambda: intersection(e2, Rational(12)))
raises(TypeError, lambda: Ellipse.intersection(e2, 1))
# some special case intersections
csmall = Circle(p1, 3)
cbig = Circle(p1, 5)
cout = Circle(Point(5, 5), 1)
# one circle inside of another
assert csmall.intersection(cbig) == []
# separate circles
assert csmall.intersection(cout) == []
# coincident circles
assert csmall.intersection(csmall) == csmall
v = sqrt(2)
t1 = Triangle(Point(0, v), Point(0, -v), Point(v, 0))
points = intersection(t1, c1)
assert len(points) == 4
assert Point(0, 1) in points
assert Point(0, -1) in points
assert Point(v/2, v/2) in points
assert Point(v/2, -v/2) in points
circ = Circle(Point(0, 0), 5)
elip = Ellipse(Point(0, 0), 5, 20)
assert intersection(circ, elip) in \
[[Point(5, 0), Point(-5, 0)], [Point(-5, 0), Point(5, 0)]]
assert elip.tangent_lines(Point(0, 0)) == []
elip = Ellipse(Point(0, 0), 3, 2)
assert elip.tangent_lines(Point(3, 0)) == \
[Line(Point(3, 0), Point(3, -12))]
e1 = Ellipse(Point(0, 0), 5, 10)
e2 = Ellipse(Point(2, 1), 4, 8)
a = Rational(53, 17)
c = 2*sqrt(3991)/17
ans = [Point(a - c/8, a/2 + c), Point(a + c/8, a/2 - c)]
assert e1.intersection(e2) == ans
e2 = Ellipse(Point(x, y), 4, 8)
c = sqrt(3991)
ans = [Point(-c/68 + a, c*Rational(2, 17) + a/2), Point(c/68 + a, c*Rational(-2, 17) + a/2)]
assert [p.subs({x: 2, y:1}) for p in e1.intersection(e2)] == ans
# Combinations of above
assert e3.is_tangent(e3.tangent_lines(p1 + Point(y1, 0))[0])
e = Ellipse((1, 2), 3, 2)
assert e.tangent_lines(Point(10, 0)) == \
[Line(Point(10, 0), Point(1, 0)),
Line(Point(10, 0), Point(Rational(14, 5), Rational(18, 5)))]
# encloses_point
e = Ellipse((0, 0), 1, 2)
assert e.encloses_point(e.center)
assert e.encloses_point(e.center + Point(0, e.vradius - Rational(1, 10)))
assert e.encloses_point(e.center + Point(e.hradius - Rational(1, 10), 0))
assert e.encloses_point(e.center + Point(e.hradius, 0)) is False
assert e.encloses_point(
e.center + Point(e.hradius + Rational(1, 10), 0)) is False
e = Ellipse((0, 0), 2, 1)
assert e.encloses_point(e.center)
assert e.encloses_point(e.center + Point(0, e.vradius - Rational(1, 10)))
assert e.encloses_point(e.center + Point(e.hradius - Rational(1, 10), 0))
assert e.encloses_point(e.center + Point(e.hradius, 0)) is False
assert e.encloses_point(
e.center + Point(e.hradius + Rational(1, 10), 0)) is False
assert c1.encloses_point(Point(1, 0)) is False
assert c1.encloses_point(Point(0.3, 0.4)) is True
assert e.scale(2, 3) == Ellipse((0, 0), 4, 3)
assert e.scale(3, 6) == Ellipse((0, 0), 6, 6)
assert e.rotate(pi) == e
assert e.rotate(pi, (1, 2)) == Ellipse(Point(2, 4), 2, 1)
raises(NotImplementedError, lambda: e.rotate(pi/3))
# Circle rotation tests (Issue #11743)
# Link - https://github.com/sympy/sympy/issues/11743
cir = Circle(Point(1, 0), 1)
assert cir.rotate(pi/2) == Circle(Point(0, 1), 1)
assert cir.rotate(pi/3) == Circle(Point(S.Half, sqrt(3)/2), 1)
assert cir.rotate(pi/3, Point(1, 0)) == Circle(Point(1, 0), 1)
assert cir.rotate(pi/3, Point(0, 1)) == Circle(Point(S.Half + sqrt(3)/2, S.Half + sqrt(3)/2), 1)
def test_construction():
e1 = Ellipse(hradius=2, vradius=1, eccentricity=None)
assert e1.eccentricity == sqrt(3)/2
e2 = Ellipse(hradius=2, vradius=None, eccentricity=sqrt(3)/2)
assert e2.vradius == 1
e3 = Ellipse(hradius=None, vradius=1, eccentricity=sqrt(3)/2)
assert e3.hradius == 2
# filter(None, iterator) filters out anything falsey, including 0
# eccentricity would be filtered out in this case and the constructor would throw an error
e4 = Ellipse(Point(0, 0), hradius=1, eccentricity=0)
assert e4.vradius == 1
#tests for eccentricity > 1
raises(GeometryError, lambda: Ellipse(Point(3, 1), hradius=3, eccentricity = S(3)/2))
raises(GeometryError, lambda: Ellipse(Point(3, 1), hradius=3, eccentricity=sec(5)))
raises(GeometryError, lambda: Ellipse(Point(3, 1), hradius=3, eccentricity=S.Pi-S(2)))
#tests for eccentricity = 1
#if vradius is not defined
assert Ellipse(None, 1, None, 1).length == 2
#if hradius is not defined
raises(GeometryError, lambda: Ellipse(None, None, 1, eccentricity = 1))
#tests for eccentricity < 0
raises(GeometryError, lambda: Ellipse(Point(3, 1), hradius=3, eccentricity = -3))
raises(GeometryError, lambda: Ellipse(Point(3, 1), hradius=3, eccentricity = -0.5))
def test_ellipse_random_point():
y1 = Symbol('y1', real=True)
e3 = Ellipse(Point(0, 0), y1, y1)
rx, ry = Symbol('rx'), Symbol('ry')
for ind in range(0, 5):
r = e3.random_point()
# substitution should give zero*y1**2
assert e3.equation(rx, ry).subs(zip((rx, ry), r.args)).equals(0)
# test for the case with seed
r = e3.random_point(seed=1)
assert e3.equation(rx, ry).subs(zip((rx, ry), r.args)).equals(0)
def test_repr():
assert repr(Circle((0, 1), 2)) == 'Circle(Point2D(0, 1), 2)'
def test_transform():
c = Circle((1, 1), 2)
assert c.scale(-1) == Circle((-1, 1), 2)
assert c.scale(y=-1) == Circle((1, -1), 2)
assert c.scale(2) == Ellipse((2, 1), 4, 2)
assert Ellipse((0, 0), 2, 3).scale(2, 3, (4, 5)) == \
Ellipse(Point(-4, -10), 4, 9)
assert Circle((0, 0), 2).scale(2, 3, (4, 5)) == \
Ellipse(Point(-4, -10), 4, 6)
assert Ellipse((0, 0), 2, 3).scale(3, 3, (4, 5)) == \
Ellipse(Point(-8, -10), 6, 9)
assert Circle((0, 0), 2).scale(3, 3, (4, 5)) == \
Circle(Point(-8, -10), 6)
assert Circle(Point(-8, -10), 6).scale(Rational(1, 3), Rational(1, 3), (4, 5)) == \
Circle((0, 0), 2)
assert Circle((0, 0), 2).translate(4, 5) == \
Circle((4, 5), 2)
assert Circle((0, 0), 2).scale(3, 3) == \
Circle((0, 0), 6)
def test_bounds():
e1 = Ellipse(Point(0, 0), 3, 5)
e2 = Ellipse(Point(2, -2), 7, 7)
c1 = Circle(Point(2, -2), 7)
c2 = Circle(Point(-2, 0), Point(0, 2), Point(2, 0))
assert e1.bounds == (-3, -5, 3, 5)
assert e2.bounds == (-5, -9, 9, 5)
assert c1.bounds == (-5, -9, 9, 5)
assert c2.bounds == (-2, -2, 2, 2)
def test_reflect():
b = Symbol('b')
m = Symbol('m')
l = Line((0, b), slope=m)
t1 = Triangle((0, 0), (1, 0), (2, 3))
assert t1.area == -t1.reflect(l).area
e = Ellipse((1, 0), 1, 2)
assert e.area == -e.reflect(Line((1, 0), slope=0)).area
assert e.area == -e.reflect(Line((1, 0), slope=oo)).area
raises(NotImplementedError, lambda: e.reflect(Line((1, 0), slope=m)))
assert Circle((0, 1), 1).reflect(Line((0, 0), (1, 1))) == Circle(Point2D(1, 0), -1)
def test_is_tangent():
e1 = Ellipse(Point(0, 0), 3, 5)
c1 = Circle(Point(2, -2), 7)
assert e1.is_tangent(Point(0, 0)) is False
assert e1.is_tangent(Point(3, 0)) is False
assert e1.is_tangent(e1) is True
assert e1.is_tangent(Ellipse((0, 0), 1, 2)) is False
assert e1.is_tangent(Ellipse((0, 0), 3, 2)) is True
assert c1.is_tangent(Ellipse((2, -2), 7, 1)) is True
assert c1.is_tangent(Circle((11, -2), 2)) is True
assert c1.is_tangent(Circle((7, -2), 2)) is True
assert c1.is_tangent(Ray((-5, -2), (-15, -20))) is False
assert c1.is_tangent(Ray((-3, -2), (-15, -20))) is False
assert c1.is_tangent(Ray((-3, -22), (15, 20))) is False
assert c1.is_tangent(Ray((9, 20), (9, -20))) is True
assert c1.is_tangent(Ray((2, 5), (9, 5))) is True
assert c1.is_tangent(Segment((2, 5), (9, 5))) is True
assert e1.is_tangent(Segment((2, 2), (-7, 7))) is False
assert e1.is_tangent(Segment((0, 0), (1, 2))) is False
assert c1.is_tangent(Segment((0, 0), (-5, -2))) is False
assert e1.is_tangent(Segment((3, 0), (12, 12))) is False
assert e1.is_tangent(Segment((12, 12), (3, 0))) is False
assert e1.is_tangent(Segment((-3, 0), (3, 0))) is False
assert e1.is_tangent(Segment((-3, 5), (3, 5))) is True
assert e1.is_tangent(Line((10, 0), (10, 10))) is False
assert e1.is_tangent(Line((0, 0), (1, 1))) is False
assert e1.is_tangent(Line((-3, 0), (-2.99, -0.001))) is False
assert e1.is_tangent(Line((-3, 0), (-3, 1))) is True
assert e1.is_tangent(Polygon((0, 0), (5, 5), (5, -5))) is False
assert e1.is_tangent(Polygon((-100, -50), (-40, -334), (-70, -52))) is False
assert e1.is_tangent(Polygon((-3, 0), (3, 0), (0, 1))) is False
assert e1.is_tangent(Polygon((-3, 0), (3, 0), (0, 5))) is False
assert e1.is_tangent(Polygon((-3, 0), (0, -5), (3, 0), (0, 5))) is False
assert e1.is_tangent(Polygon((-3, -5), (-3, 5), (3, 5), (3, -5))) is True
assert c1.is_tangent(Polygon((-3, -5), (-3, 5), (3, 5), (3, -5))) is False
assert e1.is_tangent(Polygon((0, 0), (3, 0), (7, 7), (0, 5))) is False
assert e1.is_tangent(Polygon((3, 12), (3, -12), (6, 5))) is False
assert e1.is_tangent(Polygon((3, 12), (3, -12), (0, -5), (0, 5))) is False
assert e1.is_tangent(Polygon((3, 0), (5, 7), (6, -5))) is False
assert c1.is_tangent(Segment((0, 0), (-5, -2))) is False
assert e1.is_tangent(Segment((-3, 0), (3, 0))) is False
assert e1.is_tangent(Segment((-3, 5), (3, 5))) is True
assert e1.is_tangent(Polygon((0, 0), (5, 5), (5, -5))) is False
assert e1.is_tangent(Polygon((-100, -50), (-40, -334), (-70, -52))) is False
assert e1.is_tangent(Polygon((-3, -5), (-3, 5), (3, 5), (3, -5))) is True
assert c1.is_tangent(Polygon((-3, -5), (-3, 5), (3, 5), (3, -5))) is False
assert e1.is_tangent(Polygon((3, 12), (3, -12), (0, -5), (0, 5))) is False
assert e1.is_tangent(Polygon((3, 0), (5, 7), (6, -5))) is False
raises(TypeError, lambda: e1.is_tangent(Point(0, 0, 0)))
raises(TypeError, lambda: e1.is_tangent(Rational(5)))
def test_parameter_value():
t = Symbol('t')
e = Ellipse(Point(0, 0), 3, 5)
assert e.parameter_value((3, 0), t) == {t: 0}
raises(ValueError, lambda: e.parameter_value((4, 0), t))
@slow
def test_second_moment_of_area():
x, y = symbols('x, y')
e = Ellipse(Point(0, 0), 5, 4)
I_yy = 2*4*integrate(sqrt(25 - x**2)*x**2, (x, -5, 5))/5
I_xx = 2*5*integrate(sqrt(16 - y**2)*y**2, (y, -4, 4))/4
Y = 3*sqrt(1 - x**2/5**2)
I_xy = integrate(integrate(y, (y, -Y, Y))*x, (x, -5, 5))
assert I_yy == e.second_moment_of_area()[1]
assert I_xx == e.second_moment_of_area()[0]
assert I_xy == e.second_moment_of_area()[2]
#checking for other point
t1 = e.second_moment_of_area(Point(6,5))
t2 = (580*pi, 845*pi, 600*pi)
assert t1==t2
def test_section_modulus_and_polar_second_moment_of_area():
d = Symbol('d', positive=True)
c = Circle((3, 7), 8)
assert c.polar_second_moment_of_area() == 2048*pi
assert c.section_modulus() == (128*pi, 128*pi)
c = Circle((2, 9), d/2)
assert c.polar_second_moment_of_area() == pi*d**3*Abs(d)/64 + pi*d*Abs(d)**3/64
assert c.section_modulus() == (pi*d**3/S(32), pi*d**3/S(32))
a, b = symbols('a, b', positive=True)
e = Ellipse((4, 6), a, b)
assert e.section_modulus() == (pi*a*b**2/S(4), pi*a**2*b/S(4))
assert e.polar_second_moment_of_area() == pi*a**3*b/S(4) + pi*a*b**3/S(4)
e = e.rotate(pi/2) # no change in polar and section modulus
assert e.section_modulus() == (pi*a**2*b/S(4), pi*a*b**2/S(4))
assert e.polar_second_moment_of_area() == pi*a**3*b/S(4) + pi*a*b**3/S(4)
e = Ellipse((a, b), 2, 6)
assert e.section_modulus() == (18*pi, 6*pi)
assert e.polar_second_moment_of_area() == 120*pi
e = Ellipse(Point(0, 0), 2, 2)
assert e.section_modulus() == (2*pi, 2*pi)
assert e.section_modulus(Point(2, 2)) == (2*pi, 2*pi)
assert e.section_modulus((2, 2)) == (2*pi, 2*pi)
def test_circumference():
M = Symbol('M')
m = Symbol('m')
assert Ellipse(Point(0, 0), M, m).circumference == 4 * M * elliptic_e((M ** 2 - m ** 2) / M**2)
assert Ellipse(Point(0, 0), 5, 4).circumference == 20 * elliptic_e(S(9) / 25)
# circle
assert Ellipse(None, 1, None, 0).circumference == 2*pi
# test numerically
assert abs(Ellipse(None, hradius=5, vradius=3).circumference.evalf(16) - 25.52699886339813) < 1e-10
def test_issue_15259():
assert Circle((1, 2), 0) == Point(1, 2)
def test_issue_15797_equals():
Ri = 0.024127189424130748
Ci = (0.0864931002830291, 0.0819863295239654)
A = Point(0, 0.0578591400998346)
c = Circle(Ci, Ri) # evaluated
assert c.is_tangent(c.tangent_lines(A)[0]) == True
assert c.center.x.is_Rational
assert c.center.y.is_Rational
assert c.radius.is_Rational
u = Circle(Ci, Ri, evaluate=False) # unevaluated
assert u.center.x.is_Float
assert u.center.y.is_Float
assert u.radius.is_Float
def test_auxiliary_circle():
x, y, a, b = symbols('x y a b')
e = Ellipse((x, y), a, b)
# the general result
assert e.auxiliary_circle() == Circle((x, y), Max(a, b))
# a special case where Ellipse is a Circle
assert Circle((3, 4), 8).auxiliary_circle() == Circle((3, 4), 8)
def test_director_circle():
x, y, a, b = symbols('x y a b')
e = Ellipse((x, y), a, b)
# the general result
assert e.director_circle() == Circle((x, y), sqrt(a**2 + b**2))
# a special case where Ellipse is a Circle
assert Circle((3, 4), 8).director_circle() == Circle((3, 4), 8*sqrt(2))
def test_evolute():
#ellipse centered at h,k
x, y, h, k = symbols('x y h k',real = True)
a, b = symbols('a b')
e = Ellipse(Point(h, k), a, b)
t1 = (e.hradius*(x - e.center.x))**Rational(2, 3)
t2 = (e.vradius*(y - e.center.y))**Rational(2, 3)
E = t1 + t2 - (e.hradius**2 - e.vradius**2)**Rational(2, 3)
assert e.evolute() == E
#Numerical Example
e = Ellipse(Point(1, 1), 6, 3)
t1 = (6*(x - 1))**Rational(2, 3)
t2 = (3*(y - 1))**Rational(2, 3)
E = t1 + t2 - (27)**Rational(2, 3)
assert e.evolute() == E
def test_svg():
e1 = Ellipse(Point(1, 0), 3, 2)
assert e1._svg(2, "#FFAAFF") == '<ellipse fill="#FFAAFF" stroke="#555555" stroke-width="4.0" opacity="0.6" cx="1.00000000000000" cy="0" rx="3.00000000000000" ry="2.00000000000000"/>'

View File

@ -0,0 +1,120 @@
from sympy.core.numbers import (Rational, pi)
from sympy.core.singleton import S
from sympy.core.symbol import Symbol
from sympy.geometry import (Circle, Ellipse, Point, Line, Parabola,
Polygon, Ray, RegularPolygon, Segment, Triangle, Plane, Curve)
from sympy.geometry.entity import scale, GeometryEntity
from sympy.testing.pytest import raises
def test_entity():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
assert GeometryEntity(x, y) in GeometryEntity(x, y)
raises(NotImplementedError, lambda: Point(0, 0) in GeometryEntity(x, y))
assert GeometryEntity(x, y) == GeometryEntity(x, y)
assert GeometryEntity(x, y).equals(GeometryEntity(x, y))
c = Circle((0, 0), 5)
assert GeometryEntity.encloses(c, Point(0, 0))
assert GeometryEntity.encloses(c, Segment((0, 0), (1, 1)))
assert GeometryEntity.encloses(c, Line((0, 0), (1, 1))) is False
assert GeometryEntity.encloses(c, Circle((0, 0), 4))
assert GeometryEntity.encloses(c, Polygon(Point(0, 0), Point(1, 0), Point(0, 1)))
assert GeometryEntity.encloses(c, RegularPolygon(Point(8, 8), 1, 3)) is False
def test_svg():
a = Symbol('a')
b = Symbol('b')
d = Symbol('d')
entity = Circle(Point(a, b), d)
assert entity._repr_svg_() is None
entity = Circle(Point(0, 0), S.Infinity)
assert entity._repr_svg_() is None
def test_subs():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
p = Point(x, 2)
q = Point(1, 1)
r = Point(3, 4)
for o in [p,
Segment(p, q),
Ray(p, q),
Line(p, q),
Triangle(p, q, r),
RegularPolygon(p, 3, 6),
Polygon(p, q, r, Point(5, 4)),
Circle(p, 3),
Ellipse(p, 3, 4)]:
assert 'y' in str(o.subs(x, y))
assert p.subs({x: 1}) == Point(1, 2)
assert Point(1, 2).subs(Point(1, 2), Point(3, 4)) == Point(3, 4)
assert Point(1, 2).subs((1, 2), Point(3, 4)) == Point(3, 4)
assert Point(1, 2).subs(Point(1, 2), Point(3, 4)) == Point(3, 4)
assert Point(1, 2).subs({(1, 2)}) == Point(2, 2)
raises(ValueError, lambda: Point(1, 2).subs(1))
raises(ValueError, lambda: Point(1, 1).subs((Point(1, 1), Point(1,
2)), 1, 2))
def test_transform():
assert scale(1, 2, (3, 4)).tolist() == \
[[1, 0, 0], [0, 2, 0], [0, -4, 1]]
def test_reflect_entity_overrides():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
b = Symbol('b')
m = Symbol('m')
l = Line((0, b), slope=m)
p = Point(x, y)
r = p.reflect(l)
c = Circle((x, y), 3)
cr = c.reflect(l)
assert cr == Circle(r, -3)
assert c.area == -cr.area
pent = RegularPolygon((1, 2), 1, 5)
slope = S.ComplexInfinity
while slope is S.ComplexInfinity:
slope = Rational(*(x._random()/2).as_real_imag())
l = Line(pent.vertices[1], slope=slope)
rpent = pent.reflect(l)
assert rpent.center == pent.center.reflect(l)
rvert = [i.reflect(l) for i in pent.vertices]
for v in rpent.vertices:
for i in range(len(rvert)):
ri = rvert[i]
if ri.equals(v):
rvert.remove(ri)
break
assert not rvert
assert pent.area.equals(-rpent.area)
def test_geometry_EvalfMixin():
x = pi
t = Symbol('t')
for g in [
Point(x, x),
Plane(Point(0, x, 0), (0, 0, x)),
Curve((x*t, x), (t, 0, x)),
Ellipse((x, x), x, -x),
Circle((x, x), x),
Line((0, x), (x, 0)),
Segment((0, x), (x, 0)),
Ray((0, x), (x, 0)),
Parabola((0, x), Line((-x, 0), (x, 0))),
Polygon((0, 0), (0, x), (x, 0), (x, x)),
RegularPolygon((0, x), x, 4, x),
Triangle((0, 0), (x, 0), (x, x)),
]:
assert str(g).replace('pi', '3.1') == str(g.n(2))

View File

@ -0,0 +1,38 @@
from sympy.core.numbers import Rational
from sympy.core.singleton import S
from sympy.geometry import Circle, Line, Point, Polygon, Segment
from sympy.sets import FiniteSet, Union, Intersection, EmptySet
def test_booleans():
""" test basic unions and intersections """
half = S.Half
p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
p5, p6, p7 = map(Point, [(3, 2), (1, -1), (0, 2)])
l1 = Line(Point(0,0), Point(1,1))
l2 = Line(Point(half, half), Point(5,5))
l3 = Line(p2, p3)
l4 = Line(p3, p4)
poly1 = Polygon(p1, p2, p3, p4)
poly2 = Polygon(p5, p6, p7)
poly3 = Polygon(p1, p2, p5)
assert Union(l1, l2).equals(l1)
assert Intersection(l1, l2).equals(l1)
assert Intersection(l1, l4) == FiniteSet(Point(1,1))
assert Intersection(Union(l1, l4), l3) == FiniteSet(Point(Rational(-1, 3), Rational(-1, 3)), Point(5, 1))
assert Intersection(l1, FiniteSet(Point(7,-7))) == EmptySet
assert Intersection(Circle(Point(0,0), 3), Line(p1,p2)) == FiniteSet(Point(-3,0), Point(3,0))
assert Intersection(l1, FiniteSet(p1)) == FiniteSet(p1)
assert Union(l1, FiniteSet(p1)) == l1
fs = FiniteSet(Point(Rational(1, 3), 1), Point(Rational(2, 3), 0), Point(Rational(9, 5), Rational(1, 5)), Point(Rational(7, 3), 1))
# test the intersection of polygons
assert Intersection(poly1, poly2) == fs
# make sure if we union polygons with subsets, the subsets go away
assert Union(poly1, poly2, fs) == Union(poly1, poly2)
# make sure that if we union with a FiniteSet that isn't a subset,
# that the points in the intersection stop being listed
assert Union(poly1, FiniteSet(Point(0,0), Point(3,5))) == Union(poly1, FiniteSet(Point(3,5)))
# intersect two polygons that share an edge
assert Intersection(poly1, poly3) == Union(FiniteSet(Point(Rational(3, 2), 1), Point(2, 1)), Segment(Point(0, 0), Point(1, 0)))

View File

@ -0,0 +1,861 @@
from sympy.core.numbers import (Float, Rational, oo, pi)
from sympy.core.relational import Eq
from sympy.core.singleton import S
from sympy.core.symbol import (Symbol, symbols)
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.trigonometric import (acos, cos, sin)
from sympy.sets import EmptySet
from sympy.simplify.simplify import simplify
from sympy.functions.elementary.trigonometric import tan
from sympy.geometry import (Circle, GeometryError, Line, Point, Ray,
Segment, Triangle, intersection, Point3D, Line3D, Ray3D, Segment3D,
Point2D, Line2D, Plane)
from sympy.geometry.line import Undecidable
from sympy.geometry.polygon import _asa as asa
from sympy.utilities.iterables import cartes
from sympy.testing.pytest import raises, warns
x = Symbol('x', real=True)
y = Symbol('y', real=True)
z = Symbol('z', real=True)
k = Symbol('k', real=True)
x1 = Symbol('x1', real=True)
y1 = Symbol('y1', real=True)
t = Symbol('t', real=True)
a, b = symbols('a,b', real=True)
m = symbols('m', real=True)
def test_object_from_equation():
from sympy.abc import x, y, a, b
assert Line(3*x + y + 18) == Line2D(Point2D(0, -18), Point2D(1, -21))
assert Line(3*x + 5 * y + 1) == Line2D(
Point2D(0, Rational(-1, 5)), Point2D(1, Rational(-4, 5)))
assert Line(3*a + b + 18, x="a", y="b") == Line2D(
Point2D(0, -18), Point2D(1, -21))
assert Line(3*x + y) == Line2D(Point2D(0, 0), Point2D(1, -3))
assert Line(x + y) == Line2D(Point2D(0, 0), Point2D(1, -1))
assert Line(Eq(3*a + b, -18), x="a", y=b) == Line2D(
Point2D(0, -18), Point2D(1, -21))
# issue 22361
assert Line(x - 1) == Line2D(Point2D(1, 0), Point2D(1, 1))
assert Line(2*x - 2, y=x) == Line2D(Point2D(0, 1), Point2D(1, 1))
assert Line(y) == Line2D(Point2D(0, 0), Point2D(1, 0))
assert Line(2*y, x=y) == Line2D(Point2D(0, 0), Point2D(0, 1))
assert Line(y, x=y) == Line2D(Point2D(0, 0), Point2D(0, 1))
raises(ValueError, lambda: Line(x / y))
raises(ValueError, lambda: Line(a / b, x='a', y='b'))
raises(ValueError, lambda: Line(y / x))
raises(ValueError, lambda: Line(b / a, x='a', y='b'))
raises(ValueError, lambda: Line((x + 1)**2 + y))
def feq(a, b):
"""Test if two floating point values are 'equal'."""
t_float = Float("1.0E-10")
return -t_float < a - b < t_float
def test_angle_between():
a = Point(1, 2, 3, 4)
b = a.orthogonal_direction
o = a.origin
assert feq(Line.angle_between(Line(Point(0, 0), Point(1, 1)),
Line(Point(0, 0), Point(5, 0))).evalf(), pi.evalf() / 4)
assert Line(a, o).angle_between(Line(b, o)) == pi / 2
z = Point3D(0, 0, 0)
assert Line3D.angle_between(Line3D(z, Point3D(1, 1, 1)),
Line3D(z, Point3D(5, 0, 0))) == acos(sqrt(3) / 3)
# direction of points is used to determine angle
assert Line3D.angle_between(Line3D(z, Point3D(1, 1, 1)),
Line3D(Point3D(5, 0, 0), z)) == acos(-sqrt(3) / 3)
def test_closing_angle():
a = Ray((0, 0), angle=0)
b = Ray((1, 2), angle=pi/2)
assert a.closing_angle(b) == -pi/2
assert b.closing_angle(a) == pi/2
assert a.closing_angle(a) == 0
def test_smallest_angle():
a = Line(Point(1, 1), Point(1, 2))
b = Line(Point(1, 1),Point(2, 3))
assert a.smallest_angle_between(b) == acos(2*sqrt(5)/5)
def test_svg():
a = Line(Point(1, 1),Point(1, 2))
assert a._svg() == '<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" stroke-width="2.0" opacity="0.6" d="M 1.00000000000000,1.00000000000000 L 1.00000000000000,2.00000000000000" marker-start="url(#markerReverseArrow)" marker-end="url(#markerArrow)"/>'
a = Segment(Point(1, 0),Point(1, 1))
assert a._svg() == '<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" stroke-width="2.0" opacity="0.6" d="M 1.00000000000000,0 L 1.00000000000000,1.00000000000000" />'
a = Ray(Point(2, 3), Point(3, 5))
assert a._svg() == '<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" stroke-width="2.0" opacity="0.6" d="M 2.00000000000000,3.00000000000000 L 3.00000000000000,5.00000000000000" marker-start="url(#markerCircle)" marker-end="url(#markerArrow)"/>'
def test_arbitrary_point():
l1 = Line3D(Point3D(0, 0, 0), Point3D(1, 1, 1))
l2 = Line(Point(x1, x1), Point(y1, y1))
assert l2.arbitrary_point() in l2
assert Ray((1, 1), angle=pi / 4).arbitrary_point() == \
Point(t + 1, t + 1)
assert Segment((1, 1), (2, 3)).arbitrary_point() == Point(1 + t, 1 + 2 * t)
assert l1.perpendicular_segment(l1.arbitrary_point()) == l1.arbitrary_point()
assert Ray3D((1, 1, 1), direction_ratio=[1, 2, 3]).arbitrary_point() == \
Point3D(t + 1, 2 * t + 1, 3 * t + 1)
assert Segment3D(Point3D(0, 0, 0), Point3D(1, 1, 1)).midpoint == \
Point3D(S.Half, S.Half, S.Half)
assert Segment3D(Point3D(x1, x1, x1), Point3D(y1, y1, y1)).length == sqrt(3) * sqrt((x1 - y1) ** 2)
assert Segment3D((1, 1, 1), (2, 3, 4)).arbitrary_point() == \
Point3D(t + 1, 2 * t + 1, 3 * t + 1)
raises(ValueError, (lambda: Line((x, 1), (2, 3)).arbitrary_point(x)))
def test_are_concurrent_2d():
l1 = Line(Point(0, 0), Point(1, 1))
l2 = Line(Point(x1, x1), Point(x1, 1 + x1))
assert Line.are_concurrent(l1) is False
assert Line.are_concurrent(l1, l2)
assert Line.are_concurrent(l1, l1, l1, l2)
assert Line.are_concurrent(l1, l2, Line(Point(5, x1), Point(Rational(-3, 5), x1)))
assert Line.are_concurrent(l1, Line(Point(0, 0), Point(-x1, x1)), l2) is False
def test_are_concurrent_3d():
p1 = Point3D(0, 0, 0)
l1 = Line(p1, Point3D(1, 1, 1))
parallel_1 = Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0))
parallel_2 = Line3D(Point3D(0, 1, 0), Point3D(1, 1, 0))
assert Line3D.are_concurrent(l1) is False
assert Line3D.are_concurrent(l1, Line(Point3D(x1, x1, x1), Point3D(y1, y1, y1))) is False
assert Line3D.are_concurrent(l1, Line3D(p1, Point3D(x1, x1, x1)),
Line(Point3D(x1, x1, x1), Point3D(x1, 1 + x1, 1))) is True
assert Line3D.are_concurrent(parallel_1, parallel_2) is False
def test_arguments():
"""Functions accepting `Point` objects in `geometry`
should also accept tuples, lists, and generators and
automatically convert them to points."""
from sympy.utilities.iterables import subsets
singles2d = ((1, 2), [1, 3], Point(1, 5))
doubles2d = subsets(singles2d, 2)
l2d = Line(Point2D(1, 2), Point2D(2, 3))
singles3d = ((1, 2, 3), [1, 2, 4], Point(1, 2, 6))
doubles3d = subsets(singles3d, 2)
l3d = Line(Point3D(1, 2, 3), Point3D(1, 1, 2))
singles4d = ((1, 2, 3, 4), [1, 2, 3, 5], Point(1, 2, 3, 7))
doubles4d = subsets(singles4d, 2)
l4d = Line(Point(1, 2, 3, 4), Point(2, 2, 2, 2))
# test 2D
test_single = ['contains', 'distance', 'equals', 'parallel_line', 'perpendicular_line', 'perpendicular_segment',
'projection', 'intersection']
for p in doubles2d:
Line2D(*p)
for func in test_single:
for p in singles2d:
getattr(l2d, func)(p)
# test 3D
for p in doubles3d:
Line3D(*p)
for func in test_single:
for p in singles3d:
getattr(l3d, func)(p)
# test 4D
for p in doubles4d:
Line(*p)
for func in test_single:
for p in singles4d:
getattr(l4d, func)(p)
def test_basic_properties_2d():
p1 = Point(0, 0)
p2 = Point(1, 1)
p10 = Point(2000, 2000)
p_r3 = Ray(p1, p2).random_point()
p_r4 = Ray(p2, p1).random_point()
l1 = Line(p1, p2)
l3 = Line(Point(x1, x1), Point(x1, 1 + x1))
l4 = Line(p1, Point(1, 0))
r1 = Ray(p1, Point(0, 1))
r2 = Ray(Point(0, 1), p1)
s1 = Segment(p1, p10)
p_s1 = s1.random_point()
assert Line((1, 1), slope=1) == Line((1, 1), (2, 2))
assert Line((1, 1), slope=oo) == Line((1, 1), (1, 2))
assert Line((1, 1), slope=oo).bounds == (1, 1, 1, 2)
assert Line((1, 1), slope=-oo) == Line((1, 1), (1, 2))
assert Line(p1, p2).scale(2, 1) == Line(p1, Point(2, 1))
assert Line(p1, p2) == Line(p1, p2)
assert Line(p1, p2) != Line(p2, p1)
assert l1 != Line(Point(x1, x1), Point(y1, y1))
assert l1 != l3
assert Line(p1, p10) != Line(p10, p1)
assert Line(p1, p10) != p1
assert p1 in l1 # is p1 on the line l1?
assert p1 not in l3
assert s1 in Line(p1, p10)
assert Ray(Point(0, 0), Point(0, 1)) in Ray(Point(0, 0), Point(0, 2))
assert Ray(Point(0, 0), Point(0, 2)) in Ray(Point(0, 0), Point(0, 1))
assert Ray(Point(0, 0), Point(0, 2)).xdirection == S.Zero
assert Ray(Point(0, 0), Point(1, 2)).xdirection == S.Infinity
assert Ray(Point(0, 0), Point(-1, 2)).xdirection == S.NegativeInfinity
assert Ray(Point(0, 0), Point(2, 0)).ydirection == S.Zero
assert Ray(Point(0, 0), Point(2, 2)).ydirection == S.Infinity
assert Ray(Point(0, 0), Point(2, -2)).ydirection == S.NegativeInfinity
assert (r1 in s1) is False
assert Segment(p1, p2) in s1
assert Ray(Point(x1, x1), Point(x1, 1 + x1)) != Ray(p1, Point(-1, 5))
assert Segment(p1, p2).midpoint == Point(S.Half, S.Half)
assert Segment(p1, Point(-x1, x1)).length == sqrt(2 * (x1 ** 2))
assert l1.slope == 1
assert l3.slope is oo
assert l4.slope == 0
assert Line(p1, Point(0, 1)).slope is oo
assert Line(r1.source, r1.random_point()).slope == r1.slope
assert Line(r2.source, r2.random_point()).slope == r2.slope
assert Segment(Point(0, -1), Segment(p1, Point(0, 1)).random_point()).slope == Segment(p1, Point(0, 1)).slope
assert l4.coefficients == (0, 1, 0)
assert Line((-x, x), (-x + 1, x - 1)).coefficients == (1, 1, 0)
assert Line(p1, Point(0, 1)).coefficients == (1, 0, 0)
# issue 7963
r = Ray((0, 0), angle=x)
assert r.subs(x, 3 * pi / 4) == Ray((0, 0), (-1, 1))
assert r.subs(x, 5 * pi / 4) == Ray((0, 0), (-1, -1))
assert r.subs(x, -pi / 4) == Ray((0, 0), (1, -1))
assert r.subs(x, pi / 2) == Ray((0, 0), (0, 1))
assert r.subs(x, -pi / 2) == Ray((0, 0), (0, -1))
for ind in range(0, 5):
assert l3.random_point() in l3
assert p_r3.x >= p1.x and p_r3.y >= p1.y
assert p_r4.x <= p2.x and p_r4.y <= p2.y
assert p1.x <= p_s1.x <= p10.x and p1.y <= p_s1.y <= p10.y
assert hash(s1) != hash(Segment(p10, p1))
assert s1.plot_interval() == [t, 0, 1]
assert Line(p1, p10).plot_interval() == [t, -5, 5]
assert Ray((0, 0), angle=pi / 4).plot_interval() == [t, 0, 10]
def test_basic_properties_3d():
p1 = Point3D(0, 0, 0)
p2 = Point3D(1, 1, 1)
p3 = Point3D(x1, x1, x1)
p5 = Point3D(x1, 1 + x1, 1)
l1 = Line3D(p1, p2)
l3 = Line3D(p3, p5)
r1 = Ray3D(p1, Point3D(-1, 5, 0))
r3 = Ray3D(p1, p2)
s1 = Segment3D(p1, p2)
assert Line3D((1, 1, 1), direction_ratio=[2, 3, 4]) == Line3D(Point3D(1, 1, 1), Point3D(3, 4, 5))
assert Line3D((1, 1, 1), direction_ratio=[1, 5, 7]) == Line3D(Point3D(1, 1, 1), Point3D(2, 6, 8))
assert Line3D((1, 1, 1), direction_ratio=[1, 2, 3]) == Line3D(Point3D(1, 1, 1), Point3D(2, 3, 4))
assert Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0)).direction_cosine == [1, 0, 0]
assert Line3D(Line3D(p1, Point3D(0, 1, 0))) == Line3D(p1, Point3D(0, 1, 0))
assert Ray3D(Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0))) == Ray3D(p1, Point3D(1, 0, 0))
assert Line3D(p1, p2) != Line3D(p2, p1)
assert l1 != l3
assert l1 != Line3D(p3, Point3D(y1, y1, y1))
assert r3 != r1
assert Ray3D(Point3D(0, 0, 0), Point3D(1, 1, 1)) in Ray3D(Point3D(0, 0, 0), Point3D(2, 2, 2))
assert Ray3D(Point3D(0, 0, 0), Point3D(2, 2, 2)) in Ray3D(Point3D(0, 0, 0), Point3D(1, 1, 1))
assert Ray3D(Point3D(0, 0, 0), Point3D(2, 2, 2)).xdirection == S.Infinity
assert Ray3D(Point3D(0, 0, 0), Point3D(2, 2, 2)).ydirection == S.Infinity
assert Ray3D(Point3D(0, 0, 0), Point3D(2, 2, 2)).zdirection == S.Infinity
assert Ray3D(Point3D(0, 0, 0), Point3D(-2, 2, 2)).xdirection == S.NegativeInfinity
assert Ray3D(Point3D(0, 0, 0), Point3D(2, -2, 2)).ydirection == S.NegativeInfinity
assert Ray3D(Point3D(0, 0, 0), Point3D(2, 2, -2)).zdirection == S.NegativeInfinity
assert Ray3D(Point3D(0, 0, 0), Point3D(0, 2, 2)).xdirection == S.Zero
assert Ray3D(Point3D(0, 0, 0), Point3D(2, 0, 2)).ydirection == S.Zero
assert Ray3D(Point3D(0, 0, 0), Point3D(2, 2, 0)).zdirection == S.Zero
assert p1 in l1
assert p1 not in l3
assert l1.direction_ratio == [1, 1, 1]
assert s1.midpoint == Point3D(S.Half, S.Half, S.Half)
# Test zdirection
assert Ray3D(p1, Point3D(0, 0, -1)).zdirection is S.NegativeInfinity
def test_contains():
p1 = Point(0, 0)
r = Ray(p1, Point(4, 4))
r1 = Ray3D(p1, Point3D(0, 0, -1))
r2 = Ray3D(p1, Point3D(0, 1, 0))
r3 = Ray3D(p1, Point3D(0, 0, 1))
l = Line(Point(0, 1), Point(3, 4))
# Segment contains
assert Point(0, (a + b) / 2) in Segment((0, a), (0, b))
assert Point((a + b) / 2, 0) in Segment((a, 0), (b, 0))
assert Point3D(0, 1, 0) in Segment3D((0, 1, 0), (0, 1, 0))
assert Point3D(1, 0, 0) in Segment3D((1, 0, 0), (1, 0, 0))
assert Segment3D(Point3D(0, 0, 0), Point3D(1, 0, 0)).contains([]) is True
assert Segment3D(Point3D(0, 0, 0), Point3D(1, 0, 0)).contains(
Segment3D(Point3D(2, 2, 2), Point3D(3, 2, 2))) is False
# Line contains
assert l.contains(Point(0, 1)) is True
assert l.contains((0, 1)) is True
assert l.contains((0, 0)) is False
# Ray contains
assert r.contains(p1) is True
assert r.contains((1, 1)) is True
assert r.contains((1, 3)) is False
assert r.contains(Segment((1, 1), (2, 2))) is True
assert r.contains(Segment((1, 2), (2, 5))) is False
assert r.contains(Ray((2, 2), (3, 3))) is True
assert r.contains(Ray((2, 2), (3, 5))) is False
assert r1.contains(Segment3D(p1, Point3D(0, 0, -10))) is True
assert r1.contains(Segment3D(Point3D(1, 1, 1), Point3D(2, 2, 2))) is False
assert r2.contains(Point3D(0, 0, 0)) is True
assert r3.contains(Point3D(0, 0, 0)) is True
assert Ray3D(Point3D(1, 1, 1), Point3D(1, 0, 0)).contains([]) is False
assert Line3D((0, 0, 0), (x, y, z)).contains((2 * x, 2 * y, 2 * z))
with warns(UserWarning, test_stacklevel=False):
assert Line3D(p1, Point3D(0, 1, 0)).contains(Point(1.0, 1.0)) is False
with warns(UserWarning, test_stacklevel=False):
assert r3.contains(Point(1.0, 1.0)) is False
def test_contains_nonreal_symbols():
u, v, w, z = symbols('u, v, w, z')
l = Segment(Point(u, w), Point(v, z))
p = Point(u*Rational(2, 3) + v/3, w*Rational(2, 3) + z/3)
assert l.contains(p)
def test_distance_2d():
p1 = Point(0, 0)
p2 = Point(1, 1)
half = S.Half
s1 = Segment(Point(0, 0), Point(1, 1))
s2 = Segment(Point(half, half), Point(1, 0))
r = Ray(p1, p2)
assert s1.distance(Point(0, 0)) == 0
assert s1.distance((0, 0)) == 0
assert s2.distance(Point(0, 0)) == 2 ** half / 2
assert s2.distance(Point(Rational(3) / 2, Rational(3) / 2)) == 2 ** half
assert Line(p1, p2).distance(Point(-1, 1)) == sqrt(2)
assert Line(p1, p2).distance(Point(1, -1)) == sqrt(2)
assert Line(p1, p2).distance(Point(2, 2)) == 0
assert Line(p1, p2).distance((-1, 1)) == sqrt(2)
assert Line((0, 0), (0, 1)).distance(p1) == 0
assert Line((0, 0), (0, 1)).distance(p2) == 1
assert Line((0, 0), (1, 0)).distance(p1) == 0
assert Line((0, 0), (1, 0)).distance(p2) == 1
assert r.distance(Point(-1, -1)) == sqrt(2)
assert r.distance(Point(1, 1)) == 0
assert r.distance(Point(-1, 1)) == sqrt(2)
assert Ray((1, 1), (2, 2)).distance(Point(1.5, 3)) == 3 * sqrt(2) / 4
assert r.distance((1, 1)) == 0
def test_dimension_normalization():
with warns(UserWarning, test_stacklevel=False):
assert Ray((1, 1), (2, 1, 2)) == Ray((1, 1, 0), (2, 1, 2))
def test_distance_3d():
p1, p2 = Point3D(0, 0, 0), Point3D(1, 1, 1)
p3 = Point3D(Rational(3) / 2, Rational(3) / 2, Rational(3) / 2)
s1 = Segment3D(Point3D(0, 0, 0), Point3D(1, 1, 1))
s2 = Segment3D(Point3D(S.Half, S.Half, S.Half), Point3D(1, 0, 1))
r = Ray3D(p1, p2)
assert s1.distance(p1) == 0
assert s2.distance(p1) == sqrt(3) / 2
assert s2.distance(p3) == 2 * sqrt(6) / 3
assert s1.distance((0, 0, 0)) == 0
assert s2.distance((0, 0, 0)) == sqrt(3) / 2
assert s1.distance(p1) == 0
assert s2.distance(p1) == sqrt(3) / 2
assert s2.distance(p3) == 2 * sqrt(6) / 3
assert s1.distance((0, 0, 0)) == 0
assert s2.distance((0, 0, 0)) == sqrt(3) / 2
# Line to point
assert Line3D(p1, p2).distance(Point3D(-1, 1, 1)) == 2 * sqrt(6) / 3
assert Line3D(p1, p2).distance(Point3D(1, -1, 1)) == 2 * sqrt(6) / 3
assert Line3D(p1, p2).distance(Point3D(2, 2, 2)) == 0
assert Line3D(p1, p2).distance((2, 2, 2)) == 0
assert Line3D(p1, p2).distance((1, -1, 1)) == 2 * sqrt(6) / 3
assert Line3D((0, 0, 0), (0, 1, 0)).distance(p1) == 0
assert Line3D((0, 0, 0), (0, 1, 0)).distance(p2) == sqrt(2)
assert Line3D((0, 0, 0), (1, 0, 0)).distance(p1) == 0
assert Line3D((0, 0, 0), (1, 0, 0)).distance(p2) == sqrt(2)
# Line to line
assert Line3D((0, 0, 0), (1, 0, 0)).distance(Line3D((0, 0, 0), (0, 1, 2))) == 0
assert Line3D((0, 0, 0), (1, 0, 0)).distance(Line3D((0, 0, 0), (1, 0, 0))) == 0
assert Line3D((0, 0, 0), (1, 0, 0)).distance(Line3D((10, 0, 0), (10, 1, 2))) == 0
assert Line3D((0, 0, 0), (1, 0, 0)).distance(Line3D((0, 1, 0), (0, 1, 1))) == 1
# Line to plane
assert Line3D((0, 0, 0), (1, 0, 0)).distance(Plane((2, 0, 0), (0, 0, 1))) == 0
assert Line3D((0, 0, 0), (1, 0, 0)).distance(Plane((0, 1, 0), (0, 1, 0))) == 1
assert Line3D((0, 0, 0), (1, 0, 0)).distance(Plane((1, 1, 3), (1, 0, 0))) == 0
# Ray to point
assert r.distance(Point3D(-1, -1, -1)) == sqrt(3)
assert r.distance(Point3D(1, 1, 1)) == 0
assert r.distance((-1, -1, -1)) == sqrt(3)
assert r.distance((1, 1, 1)) == 0
assert Ray3D((0, 0, 0), (1, 1, 2)).distance((-1, -1, 2)) == 4 * sqrt(3) / 3
assert Ray3D((1, 1, 1), (2, 2, 2)).distance(Point3D(1.5, -3, -1)) == Rational(9) / 2
assert Ray3D((1, 1, 1), (2, 2, 2)).distance(Point3D(1.5, 3, 1)) == sqrt(78) / 6
def test_equals():
p1 = Point(0, 0)
p2 = Point(1, 1)
l1 = Line(p1, p2)
l2 = Line((0, 5), slope=m)
l3 = Line(Point(x1, x1), Point(x1, 1 + x1))
assert l1.perpendicular_line(p1.args).equals(Line(Point(0, 0), Point(1, -1)))
assert l1.perpendicular_line(p1).equals(Line(Point(0, 0), Point(1, -1)))
assert Line(Point(x1, x1), Point(y1, y1)).parallel_line(Point(-x1, x1)). \
equals(Line(Point(-x1, x1), Point(-y1, 2 * x1 - y1)))
assert l3.parallel_line(p1.args).equals(Line(Point(0, 0), Point(0, -1)))
assert l3.parallel_line(p1).equals(Line(Point(0, 0), Point(0, -1)))
assert (l2.distance(Point(2, 3)) - 2 * abs(m + 1) / sqrt(m ** 2 + 1)).equals(0)
assert Line3D(p1, Point3D(0, 1, 0)).equals(Point(1.0, 1.0)) is False
assert Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0)).equals(Line3D(Point3D(-5, 0, 0), Point3D(-1, 0, 0))) is True
assert Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0)).equals(Line3D(p1, Point3D(0, 1, 0))) is False
assert Ray3D(p1, Point3D(0, 0, -1)).equals(Point(1.0, 1.0)) is False
assert Ray3D(p1, Point3D(0, 0, -1)).equals(Ray3D(p1, Point3D(0, 0, -1))) is True
assert Line3D((0, 0), (t, t)).perpendicular_line(Point(0, 1, 0)).equals(
Line3D(Point3D(0, 1, 0), Point3D(S.Half, S.Half, 0)))
assert Line3D((0, 0), (t, t)).perpendicular_segment(Point(0, 1, 0)).equals(Segment3D((0, 1), (S.Half, S.Half)))
assert Line3D(p1, Point3D(0, 1, 0)).equals(Point(1.0, 1.0)) is False
def test_equation():
p1 = Point(0, 0)
p2 = Point(1, 1)
l1 = Line(p1, p2)
l3 = Line(Point(x1, x1), Point(x1, 1 + x1))
assert simplify(l1.equation()) in (x - y, y - x)
assert simplify(l3.equation()) in (x - x1, x1 - x)
assert simplify(l1.equation()) in (x - y, y - x)
assert simplify(l3.equation()) in (x - x1, x1 - x)
assert Line(p1, Point(1, 0)).equation(x=x, y=y) == y
assert Line(p1, Point(0, 1)).equation() == x
assert Line(Point(2, 0), Point(2, 1)).equation() == x - 2
assert Line(p2, Point(2, 1)).equation() == y - 1
assert Line3D(Point(x1, x1, x1), Point(y1, y1, y1)
).equation() == (-x + y, -x + z)
assert Line3D(Point(1, 2, 3), Point(2, 3, 4)
).equation() == (-x + y - 1, -x + z - 2)
assert Line3D(Point(1, 2, 3), Point(1, 3, 4)
).equation() == (x - 1, -y + z - 1)
assert Line3D(Point(1, 2, 3), Point(2, 2, 4)
).equation() == (y - 2, -x + z - 2)
assert Line3D(Point(1, 2, 3), Point(2, 3, 3)
).equation() == (-x + y - 1, z - 3)
assert Line3D(Point(1, 2, 3), Point(1, 2, 4)
).equation() == (x - 1, y - 2)
assert Line3D(Point(1, 2, 3), Point(1, 3, 3)
).equation() == (x - 1, z - 3)
assert Line3D(Point(1, 2, 3), Point(2, 2, 3)
).equation() == (y - 2, z - 3)
def test_intersection_2d():
p1 = Point(0, 0)
p2 = Point(1, 1)
p3 = Point(x1, x1)
p4 = Point(y1, y1)
l1 = Line(p1, p2)
l3 = Line(Point(0, 0), Point(3, 4))
r1 = Ray(Point(1, 1), Point(2, 2))
r2 = Ray(Point(0, 0), Point(3, 4))
r4 = Ray(p1, p2)
r6 = Ray(Point(0, 1), Point(1, 2))
r7 = Ray(Point(0.5, 0.5), Point(1, 1))
s1 = Segment(p1, p2)
s2 = Segment(Point(0.25, 0.25), Point(0.5, 0.5))
s3 = Segment(Point(0, 0), Point(3, 4))
assert intersection(l1, p1) == [p1]
assert intersection(l1, Point(x1, 1 + x1)) == []
assert intersection(l1, Line(p3, p4)) in [[l1], [Line(p3, p4)]]
assert intersection(l1, l1.parallel_line(Point(x1, 1 + x1))) == []
assert intersection(l3, l3) == [l3]
assert intersection(l3, r2) == [r2]
assert intersection(l3, s3) == [s3]
assert intersection(s3, l3) == [s3]
assert intersection(Segment(Point(-10, 10), Point(10, 10)), Segment(Point(-5, -5), Point(-5, 5))) == []
assert intersection(r2, l3) == [r2]
assert intersection(r1, Ray(Point(2, 2), Point(0, 0))) == [Segment(Point(1, 1), Point(2, 2))]
assert intersection(r1, Ray(Point(1, 1), Point(-1, -1))) == [Point(1, 1)]
assert intersection(r1, Segment(Point(0, 0), Point(2, 2))) == [Segment(Point(1, 1), Point(2, 2))]
assert r4.intersection(s2) == [s2]
assert r4.intersection(Segment(Point(2, 3), Point(3, 4))) == []
assert r4.intersection(Segment(Point(-1, -1), Point(0.5, 0.5))) == [Segment(p1, Point(0.5, 0.5))]
assert r4.intersection(Ray(p2, p1)) == [s1]
assert Ray(p2, p1).intersection(r6) == []
assert r4.intersection(r7) == r7.intersection(r4) == [r7]
assert Ray3D((0, 0), (3, 0)).intersection(Ray3D((1, 0), (3, 0))) == [Ray3D((1, 0), (3, 0))]
assert Ray3D((1, 0), (3, 0)).intersection(Ray3D((0, 0), (3, 0))) == [Ray3D((1, 0), (3, 0))]
assert Ray(Point(0, 0), Point(0, 4)).intersection(Ray(Point(0, 1), Point(0, -1))) == \
[Segment(Point(0, 0), Point(0, 1))]
assert Segment3D((0, 0), (3, 0)).intersection(
Segment3D((1, 0), (2, 0))) == [Segment3D((1, 0), (2, 0))]
assert Segment3D((1, 0), (2, 0)).intersection(
Segment3D((0, 0), (3, 0))) == [Segment3D((1, 0), (2, 0))]
assert Segment3D((0, 0), (3, 0)).intersection(
Segment3D((3, 0), (4, 0))) == [Point3D((3, 0))]
assert Segment3D((0, 0), (3, 0)).intersection(
Segment3D((2, 0), (5, 0))) == [Segment3D((2, 0), (3, 0))]
assert Segment3D((0, 0), (3, 0)).intersection(
Segment3D((-2, 0), (1, 0))) == [Segment3D((0, 0), (1, 0))]
assert Segment3D((0, 0), (3, 0)).intersection(
Segment3D((-2, 0), (0, 0))) == [Point3D(0, 0)]
assert s1.intersection(Segment(Point(1, 1), Point(2, 2))) == [Point(1, 1)]
assert s1.intersection(Segment(Point(0.5, 0.5), Point(1.5, 1.5))) == [Segment(Point(0.5, 0.5), p2)]
assert s1.intersection(Segment(Point(4, 4), Point(5, 5))) == []
assert s1.intersection(Segment(Point(-1, -1), p1)) == [p1]
assert s1.intersection(Segment(Point(-1, -1), Point(0.5, 0.5))) == [Segment(p1, Point(0.5, 0.5))]
assert s1.intersection(Line(Point(1, 0), Point(2, 1))) == []
assert s1.intersection(s2) == [s2]
assert s2.intersection(s1) == [s2]
assert asa(120, 8, 52) == \
Triangle(
Point(0, 0),
Point(8, 0),
Point(-4 * cos(19 * pi / 90) / sin(2 * pi / 45),
4 * sqrt(3) * cos(19 * pi / 90) / sin(2 * pi / 45)))
assert Line((0, 0), (1, 1)).intersection(Ray((1, 0), (1, 2))) == [Point(1, 1)]
assert Line((0, 0), (1, 1)).intersection(Segment((1, 0), (1, 2))) == [Point(1, 1)]
assert Ray((0, 0), (1, 1)).intersection(Ray((1, 0), (1, 2))) == [Point(1, 1)]
assert Ray((0, 0), (1, 1)).intersection(Segment((1, 0), (1, 2))) == [Point(1, 1)]
assert Ray((0, 0), (10, 10)).contains(Segment((1, 1), (2, 2))) is True
assert Segment((1, 1), (2, 2)) in Line((0, 0), (10, 10))
assert s1.intersection(Ray((1, 1), (4, 4))) == [Point(1, 1)]
# This test is disabled because it hangs after rref changes which simplify
# intermediate results and return a different representation from when the
# test was written.
# # 16628 - this should be fast
# p0 = Point2D(Rational(249, 5), Rational(497999, 10000))
# p1 = Point2D((-58977084786*sqrt(405639795226) + 2030690077184193 +
# 20112207807*sqrt(630547164901) + 99600*sqrt(255775022850776494562626))
# /(2000*sqrt(255775022850776494562626) + 1991998000*sqrt(405639795226)
# + 1991998000*sqrt(630547164901) + 1622561172902000),
# (-498000*sqrt(255775022850776494562626) - 995999*sqrt(630547164901) +
# 90004251917891999 +
# 496005510002*sqrt(405639795226))/(10000*sqrt(255775022850776494562626)
# + 9959990000*sqrt(405639795226) + 9959990000*sqrt(630547164901) +
# 8112805864510000))
# p2 = Point2D(Rational(497, 10), Rational(-497, 10))
# p3 = Point2D(Rational(-497, 10), Rational(-497, 10))
# l = Line(p0, p1)
# s = Segment(p2, p3)
# n = (-52673223862*sqrt(405639795226) - 15764156209307469 -
# 9803028531*sqrt(630547164901) +
# 33200*sqrt(255775022850776494562626))
# d = sqrt(405639795226) + 315274080450 + 498000*sqrt(
# 630547164901) + sqrt(255775022850776494562626)
# assert intersection(l, s) == [
# Point2D(n/d*Rational(3, 2000), Rational(-497, 10))]
def test_line_intersection():
# see also test_issue_11238 in test_matrices.py
x0 = tan(pi*Rational(13, 45))
x1 = sqrt(3)
x2 = x0**2
x, y = [8*x0/(x0 + x1), (24*x0 - 8*x1*x2)/(x2 - 3)]
assert Line(Point(0, 0), Point(1, -sqrt(3))).contains(Point(x, y)) is True
def test_intersection_3d():
p1 = Point3D(0, 0, 0)
p2 = Point3D(1, 1, 1)
l1 = Line3D(p1, p2)
l2 = Line3D(Point3D(0, 0, 0), Point3D(3, 4, 0))
r1 = Ray3D(Point3D(1, 1, 1), Point3D(2, 2, 2))
r2 = Ray3D(Point3D(0, 0, 0), Point3D(3, 4, 0))
s1 = Segment3D(Point3D(0, 0, 0), Point3D(3, 4, 0))
assert intersection(l1, p1) == [p1]
assert intersection(l1, Point3D(x1, 1 + x1, 1)) == []
assert intersection(l1, l1.parallel_line(p1)) == [Line3D(Point3D(0, 0, 0), Point3D(1, 1, 1))]
assert intersection(l2, r2) == [r2]
assert intersection(l2, s1) == [s1]
assert intersection(r2, l2) == [r2]
assert intersection(r1, Ray3D(Point3D(1, 1, 1), Point3D(-1, -1, -1))) == [Point3D(1, 1, 1)]
assert intersection(r1, Segment3D(Point3D(0, 0, 0), Point3D(2, 2, 2))) == [
Segment3D(Point3D(1, 1, 1), Point3D(2, 2, 2))]
assert intersection(Ray3D(Point3D(1, 0, 0), Point3D(-1, 0, 0)), Ray3D(Point3D(0, 1, 0), Point3D(0, -1, 0))) \
== [Point3D(0, 0, 0)]
assert intersection(r1, Ray3D(Point3D(2, 2, 2), Point3D(0, 0, 0))) == \
[Segment3D(Point3D(1, 1, 1), Point3D(2, 2, 2))]
assert intersection(s1, r2) == [s1]
assert Line3D(Point3D(4, 0, 1), Point3D(0, 4, 1)).intersection(Line3D(Point3D(0, 0, 1), Point3D(4, 4, 1))) == \
[Point3D(2, 2, 1)]
assert Line3D((0, 1, 2), (0, 2, 3)).intersection(Line3D((0, 1, 2), (0, 1, 1))) == [Point3D(0, 1, 2)]
assert Line3D((0, 0), (t, t)).intersection(Line3D((0, 1), (t, t))) == \
[Point3D(t, t)]
assert Ray3D(Point3D(0, 0, 0), Point3D(0, 4, 0)).intersection(Ray3D(Point3D(0, 1, 1), Point3D(0, -1, 1))) == []
def test_is_parallel():
p1 = Point3D(0, 0, 0)
p2 = Point3D(1, 1, 1)
p3 = Point3D(x1, x1, x1)
l2 = Line(Point(x1, x1), Point(y1, y1))
l2_1 = Line(Point(x1, x1), Point(x1, 1 + x1))
assert Line.is_parallel(Line(Point(0, 0), Point(1, 1)), l2)
assert Line.is_parallel(l2, Line(Point(x1, x1), Point(x1, 1 + x1))) is False
assert Line.is_parallel(l2, l2.parallel_line(Point(-x1, x1)))
assert Line.is_parallel(l2_1, l2_1.parallel_line(Point(0, 0)))
assert Line3D(p1, p2).is_parallel(Line3D(p1, p2)) # same as in 2D
assert Line3D(Point3D(4, 0, 1), Point3D(0, 4, 1)).is_parallel(Line3D(Point3D(0, 0, 1), Point3D(4, 4, 1))) is False
assert Line3D(p1, p2).parallel_line(p3) == Line3D(Point3D(x1, x1, x1),
Point3D(x1 + 1, x1 + 1, x1 + 1))
assert Line3D(p1, p2).parallel_line(p3.args) == \
Line3D(Point3D(x1, x1, x1), Point3D(x1 + 1, x1 + 1, x1 + 1))
assert Line3D(Point3D(4, 0, 1), Point3D(0, 4, 1)).is_parallel(Line3D(Point3D(0, 0, 1), Point3D(4, 4, 1))) is False
def test_is_perpendicular():
p1 = Point(0, 0)
p2 = Point(1, 1)
l1 = Line(p1, p2)
l2 = Line(Point(x1, x1), Point(y1, y1))
l1_1 = Line(p1, Point(-x1, x1))
# 2D
assert Line.is_perpendicular(l1, l1_1)
assert Line.is_perpendicular(l1, l2) is False
p = l1.random_point()
assert l1.perpendicular_segment(p) == p
# 3D
assert Line3D.is_perpendicular(Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0)),
Line3D(Point3D(0, 0, 0), Point3D(0, 1, 0))) is True
assert Line3D.is_perpendicular(Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0)),
Line3D(Point3D(0, 1, 0), Point3D(1, 1, 0))) is False
assert Line3D.is_perpendicular(Line3D(Point3D(0, 0, 0), Point3D(1, 1, 1)),
Line3D(Point3D(x1, x1, x1), Point3D(y1, y1, y1))) is False
def test_is_similar():
p1 = Point(2000, 2000)
p2 = p1.scale(2, 2)
r1 = Ray3D(Point3D(1, 1, 1), Point3D(1, 0, 0))
r2 = Ray(Point(0, 0), Point(0, 1))
s1 = Segment(Point(0, 0), p1)
assert s1.is_similar(Segment(p1, p2))
assert s1.is_similar(r2) is False
assert r1.is_similar(Line3D(Point3D(1, 1, 1), Point3D(1, 0, 0))) is True
assert r1.is_similar(Line3D(Point3D(0, 0, 0), Point3D(0, 1, 0))) is False
def test_length():
s2 = Segment3D(Point3D(x1, x1, x1), Point3D(y1, y1, y1))
assert Line(Point(0, 0), Point(1, 1)).length is oo
assert s2.length == sqrt(3) * sqrt((x1 - y1) ** 2)
assert Line3D(Point3D(0, 0, 0), Point3D(1, 1, 1)).length is oo
def test_projection():
p1 = Point(0, 0)
p2 = Point3D(0, 0, 0)
p3 = Point(-x1, x1)
l1 = Line(p1, Point(1, 1))
l2 = Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0))
l3 = Line3D(p2, Point3D(1, 1, 1))
r1 = Ray(Point(1, 1), Point(2, 2))
s1 = Segment(Point2D(0, 0), Point2D(0, 1))
s2 = Segment(Point2D(1, 0), Point2D(2, 1/2))
assert Line(Point(x1, x1), Point(y1, y1)).projection(Point(y1, y1)) == Point(y1, y1)
assert Line(Point(x1, x1), Point(x1, 1 + x1)).projection(Point(1, 1)) == Point(x1, 1)
assert Segment(Point(-2, 2), Point(0, 4)).projection(r1) == Segment(Point(-1, 3), Point(0, 4))
assert Segment(Point(0, 4), Point(-2, 2)).projection(r1) == Segment(Point(0, 4), Point(-1, 3))
assert s2.projection(s1) == EmptySet
assert l1.projection(p3) == p1
assert l1.projection(Ray(p1, Point(-1, 5))) == Ray(Point(0, 0), Point(2, 2))
assert l1.projection(Ray(p1, Point(-1, 1))) == p1
assert r1.projection(Ray(Point(1, 1), Point(-1, -1))) == Point(1, 1)
assert r1.projection(Ray(Point(0, 4), Point(-1, -5))) == Segment(Point(1, 1), Point(2, 2))
assert r1.projection(Segment(Point(-1, 5), Point(-5, -10))) == Segment(Point(1, 1), Point(2, 2))
assert r1.projection(Ray(Point(1, 1), Point(-1, -1))) == Point(1, 1)
assert r1.projection(Ray(Point(0, 4), Point(-1, -5))) == Segment(Point(1, 1), Point(2, 2))
assert r1.projection(Segment(Point(-1, 5), Point(-5, -10))) == Segment(Point(1, 1), Point(2, 2))
assert l3.projection(Ray3D(p2, Point3D(-1, 5, 0))) == Ray3D(Point3D(0, 0, 0), Point3D(Rational(4, 3), Rational(4, 3), Rational(4, 3)))
assert l3.projection(Ray3D(p2, Point3D(-1, 1, 1))) == Ray3D(Point3D(0, 0, 0), Point3D(Rational(1, 3), Rational(1, 3), Rational(1, 3)))
assert l2.projection(Point3D(5, 5, 0)) == Point3D(5, 0)
assert l2.projection(Line3D(Point3D(0, 1, 0), Point3D(1, 1, 0))).equals(l2)
def test_perpendicular_line():
# 3d - requires a particular orthogonal to be selected
p1, p2, p3 = Point(0, 0, 0), Point(2, 3, 4), Point(-2, 2, 0)
l = Line(p1, p2)
p = l.perpendicular_line(p3)
assert p.p1 == p3
assert p.p2 in l
# 2d - does not require special selection
p1, p2, p3 = Point(0, 0), Point(2, 3), Point(-2, 2)
l = Line(p1, p2)
p = l.perpendicular_line(p3)
assert p.p1 == p3
# p is directed from l to p3
assert p.direction.unit == (p3 - l.projection(p3)).unit
def test_perpendicular_bisector():
s1 = Segment(Point(0, 0), Point(1, 1))
aline = Line(Point(S.Half, S.Half), Point(Rational(3, 2), Rational(-1, 2)))
on_line = Segment(Point(S.Half, S.Half), Point(Rational(3, 2), Rational(-1, 2))).midpoint
assert s1.perpendicular_bisector().equals(aline)
assert s1.perpendicular_bisector(on_line).equals(Segment(s1.midpoint, on_line))
assert s1.perpendicular_bisector(on_line + (1, 0)).equals(aline)
def test_raises():
d, e = symbols('a,b', real=True)
s = Segment((d, 0), (e, 0))
raises(TypeError, lambda: Line((1, 1), 1))
raises(ValueError, lambda: Line(Point(0, 0), Point(0, 0)))
raises(Undecidable, lambda: Point(2 * d, 0) in s)
raises(ValueError, lambda: Ray3D(Point(1.0, 1.0)))
raises(ValueError, lambda: Line3D(Point3D(0, 0, 0), Point3D(0, 0, 0)))
raises(TypeError, lambda: Line3D((1, 1), 1))
raises(ValueError, lambda: Line3D(Point3D(0, 0, 0)))
raises(TypeError, lambda: Ray((1, 1), 1))
raises(GeometryError, lambda: Line(Point(0, 0), Point(1, 0))
.projection(Circle(Point(0, 0), 1)))
def test_ray_generation():
assert Ray((1, 1), angle=pi / 4) == Ray((1, 1), (2, 2))
assert Ray((1, 1), angle=pi / 2) == Ray((1, 1), (1, 2))
assert Ray((1, 1), angle=-pi / 2) == Ray((1, 1), (1, 0))
assert Ray((1, 1), angle=-3 * pi / 2) == Ray((1, 1), (1, 2))
assert Ray((1, 1), angle=5 * pi / 2) == Ray((1, 1), (1, 2))
assert Ray((1, 1), angle=5.0 * pi / 2) == Ray((1, 1), (1, 2))
assert Ray((1, 1), angle=pi) == Ray((1, 1), (0, 1))
assert Ray((1, 1), angle=3.0 * pi) == Ray((1, 1), (0, 1))
assert Ray((1, 1), angle=4.0 * pi) == Ray((1, 1), (2, 1))
assert Ray((1, 1), angle=0) == Ray((1, 1), (2, 1))
assert Ray((1, 1), angle=4.05 * pi) == Ray(Point(1, 1),
Point(2, -sqrt(5) * sqrt(2 * sqrt(5) + 10) / 4 - sqrt(
2 * sqrt(5) + 10) / 4 + 2 + sqrt(5)))
assert Ray((1, 1), angle=4.02 * pi) == Ray(Point(1, 1),
Point(2, 1 + tan(4.02 * pi)))
assert Ray((1, 1), angle=5) == Ray((1, 1), (2, 1 + tan(5)))
assert Ray3D((1, 1, 1), direction_ratio=[4, 4, 4]) == Ray3D(Point3D(1, 1, 1), Point3D(5, 5, 5))
assert Ray3D((1, 1, 1), direction_ratio=[1, 2, 3]) == Ray3D(Point3D(1, 1, 1), Point3D(2, 3, 4))
assert Ray3D((1, 1, 1), direction_ratio=[1, 1, 1]) == Ray3D(Point3D(1, 1, 1), Point3D(2, 2, 2))
def test_issue_7814():
circle = Circle(Point(x, 0), y)
line = Line(Point(k, z), slope=0)
_s = sqrt((y - z)*(y + z))
assert line.intersection(circle) == [Point2D(x + _s, z), Point2D(x - _s, z)]
def test_issue_2941():
def _check():
for f, g in cartes(*[(Line, Ray, Segment)] * 2):
l1 = f(a, b)
l2 = g(c, d)
assert l1.intersection(l2) == l2.intersection(l1)
# intersect at end point
c, d = (-2, -2), (-2, 0)
a, b = (0, 0), (1, 1)
_check()
# midline intersection
c, d = (-2, -3), (-2, 0)
_check()
def test_parameter_value():
t = Symbol('t')
p1, p2 = Point(0, 1), Point(5, 6)
l = Line(p1, p2)
assert l.parameter_value((5, 6), t) == {t: 1}
raises(ValueError, lambda: l.parameter_value((0, 0), t))
def test_bisectors():
r1 = Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0))
r2 = Line3D(Point3D(0, 0, 0), Point3D(0, 1, 0))
bisections = r1.bisectors(r2)
assert bisections == [Line3D(Point3D(0, 0, 0), Point3D(1, 1, 0)),
Line3D(Point3D(0, 0, 0), Point3D(1, -1, 0))]
ans = [Line3D(Point3D(0, 0, 0), Point3D(1, 0, 1)),
Line3D(Point3D(0, 0, 0), Point3D(-1, 0, 1))]
l1 = (0, 0, 0), (0, 0, 1)
l2 = (0, 0), (1, 0)
for a, b in cartes((Line, Segment, Ray), repeat=2):
assert a(*l1).bisectors(b(*l2)) == ans
def test_issue_8615():
a = Line3D(Point3D(6, 5, 0), Point3D(6, -6, 0))
b = Line3D(Point3D(6, -1, 19/10), Point3D(6, -1, 0))
assert a.intersection(b) == [Point3D(6, -1, 0)]
def test_issue_12598():
r1 = Ray(Point(0, 1), Point(0.98, 0.79).n(2))
r2 = Ray(Point(0, 0), Point(0.71, 0.71).n(2))
assert str(r1.intersection(r2)[0]) == 'Point2D(0.82, 0.82)'
l1 = Line((0, 0), (1, 1))
l2 = Segment((-1, 1), (0, -1)).n(2)
assert str(l1.intersection(l2)[0]) == 'Point2D(-0.33, -0.33)'
l2 = Segment((-1, 1), (-1/2, 1/2)).n(2)
assert not l1.intersection(l2)

View File

@ -0,0 +1,143 @@
from sympy.core.numbers import (Rational, oo)
from sympy.core.singleton import S
from sympy.core.symbol import symbols
from sympy.functions.elementary.complexes import sign
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.geometry.ellipse import (Circle, Ellipse)
from sympy.geometry.line import (Line, Ray2D, Segment2D)
from sympy.geometry.parabola import Parabola
from sympy.geometry.point import (Point, Point2D)
from sympy.testing.pytest import raises
from sympy.abc import x, y
def test_parabola_geom():
a, b = symbols('a b')
p1 = Point(0, 0)
p2 = Point(3, 7)
p3 = Point(0, 4)
p4 = Point(6, 0)
p5 = Point(a, a)
d1 = Line(Point(4, 0), Point(4, 9))
d2 = Line(Point(7, 6), Point(3, 6))
d3 = Line(Point(4, 0), slope=oo)
d4 = Line(Point(7, 6), slope=0)
d5 = Line(Point(b, a), slope=oo)
d6 = Line(Point(a, b), slope=0)
half = S.Half
pa1 = Parabola(None, d2)
pa2 = Parabola(directrix=d1)
pa3 = Parabola(p1, d1)
pa4 = Parabola(p2, d2)
pa5 = Parabola(p2, d4)
pa6 = Parabola(p3, d2)
pa7 = Parabola(p2, d1)
pa8 = Parabola(p4, d1)
pa9 = Parabola(p4, d3)
pa10 = Parabola(p5, d5)
pa11 = Parabola(p5, d6)
d = Line(Point(3, 7), Point(2, 9))
pa12 = Parabola(Point(7, 8), d)
pa12r = Parabola(Point(7, 8).reflect(d), d)
raises(ValueError, lambda:
Parabola(Point(7, 8, 9), Line(Point(6, 7), Point(7, 7))))
raises(ValueError, lambda:
Parabola(Point(0, 2), Line(Point(7, 2), Point(6, 2))))
raises(ValueError, lambda: Parabola(Point(7, 8), Point(3, 8)))
# Basic Stuff
assert pa1.focus == Point(0, 0)
assert pa1.ambient_dimension == S(2)
assert pa2 == pa3
assert pa4 != pa7
assert pa6 != pa7
assert pa6.focus == Point2D(0, 4)
assert pa6.focal_length == 1
assert pa6.p_parameter == -1
assert pa6.vertex == Point2D(0, 5)
assert pa6.eccentricity == 1
assert pa7.focus == Point2D(3, 7)
assert pa7.focal_length == half
assert pa7.p_parameter == -half
assert pa7.vertex == Point2D(7*half, 7)
assert pa4.focal_length == half
assert pa4.p_parameter == half
assert pa4.vertex == Point2D(3, 13*half)
assert pa8.focal_length == 1
assert pa8.p_parameter == 1
assert pa8.vertex == Point2D(5, 0)
assert pa4.focal_length == pa5.focal_length
assert pa4.p_parameter == pa5.p_parameter
assert pa4.vertex == pa5.vertex
assert pa4.equation() == pa5.equation()
assert pa8.focal_length == pa9.focal_length
assert pa8.p_parameter == pa9.p_parameter
assert pa8.vertex == pa9.vertex
assert pa8.equation() == pa9.equation()
assert pa10.focal_length == pa11.focal_length == sqrt((a - b) ** 2) / 2 # if a, b real == abs(a - b)/2
assert pa11.vertex == Point(*pa10.vertex[::-1]) == Point(a,
a - sqrt((a - b)**2)*sign(a - b)/2) # change axis x->y, y->x on pa10
aos = pa12.axis_of_symmetry
assert aos == Line(Point(7, 8), Point(5, 7))
assert pa12.directrix == Line(Point(3, 7), Point(2, 9))
assert pa12.directrix.angle_between(aos) == S.Pi/2
assert pa12.eccentricity == 1
assert pa12.equation(x, y) == (x - 7)**2 + (y - 8)**2 - (-2*x - y + 13)**2/5
assert pa12.focal_length == 9*sqrt(5)/10
assert pa12.focus == Point(7, 8)
assert pa12.p_parameter == 9*sqrt(5)/10
assert pa12.vertex == Point2D(S(26)/5, S(71)/10)
assert pa12r.focal_length == 9*sqrt(5)/10
assert pa12r.focus == Point(-S(1)/5, S(22)/5)
assert pa12r.p_parameter == -9*sqrt(5)/10
assert pa12r.vertex == Point(S(8)/5, S(53)/10)
def test_parabola_intersection():
l1 = Line(Point(1, -2), Point(-1,-2))
l2 = Line(Point(1, 2), Point(-1,2))
l3 = Line(Point(1, 0), Point(-1,0))
p1 = Point(0,0)
p2 = Point(0, -2)
p3 = Point(120, -12)
parabola1 = Parabola(p1, l1)
# parabola with parabola
assert parabola1.intersection(parabola1) == [parabola1]
assert parabola1.intersection(Parabola(p1, l2)) == [Point2D(-2, 0), Point2D(2, 0)]
assert parabola1.intersection(Parabola(p2, l3)) == [Point2D(0, -1)]
assert parabola1.intersection(Parabola(Point(16, 0), l1)) == [Point2D(8, 15)]
assert parabola1.intersection(Parabola(Point(0, 16), l1)) == [Point2D(-6, 8), Point2D(6, 8)]
assert parabola1.intersection(Parabola(p3, l3)) == []
# parabola with point
assert parabola1.intersection(p1) == []
assert parabola1.intersection(Point2D(0, -1)) == [Point2D(0, -1)]
assert parabola1.intersection(Point2D(4, 3)) == [Point2D(4, 3)]
# parabola with line
assert parabola1.intersection(Line(Point2D(-7, 3), Point(12, 3))) == [Point2D(-4, 3), Point2D(4, 3)]
assert parabola1.intersection(Line(Point(-4, -1), Point(4, -1))) == [Point(0, -1)]
assert parabola1.intersection(Line(Point(2, 0), Point(0, -2))) == [Point2D(2, 0)]
raises(TypeError, lambda: parabola1.intersection(Line(Point(0, 0, 0), Point(1, 1, 1))))
# parabola with segment
assert parabola1.intersection(Segment2D((-4, -5), (4, 3))) == [Point2D(0, -1), Point2D(4, 3)]
assert parabola1.intersection(Segment2D((0, -5), (0, 6))) == [Point2D(0, -1)]
assert parabola1.intersection(Segment2D((-12, -65), (14, -68))) == []
# parabola with ray
assert parabola1.intersection(Ray2D((-4, -5), (4, 3))) == [Point2D(0, -1), Point2D(4, 3)]
assert parabola1.intersection(Ray2D((0, 7), (1, 14))) == [Point2D(14 + 2*sqrt(57), 105 + 14*sqrt(57))]
assert parabola1.intersection(Ray2D((0, 7), (0, 14))) == []
# parabola with ellipse/circle
assert parabola1.intersection(Circle(p1, 2)) == [Point2D(-2, 0), Point2D(2, 0)]
assert parabola1.intersection(Circle(p2, 1)) == [Point2D(0, -1)]
assert parabola1.intersection(Ellipse(p2, 2, 1)) == [Point2D(0, -1)]
assert parabola1.intersection(Ellipse(Point(0, 19), 5, 7)) == []
assert parabola1.intersection(Ellipse((0, 3), 12, 4)) == [
Point2D(0, -1),
Point2D(-4*sqrt(17)/3, Rational(59, 9)),
Point2D(4*sqrt(17)/3, Rational(59, 9))]
# parabola with unsupported type
raises(TypeError, lambda: parabola1.intersection(2))

View File

@ -0,0 +1,268 @@
from sympy.core.numbers import (Rational, pi)
from sympy.core.singleton import S
from sympy.core.symbol import (Dummy, symbols)
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.trigonometric import (asin, cos, sin)
from sympy.geometry import Line, Point, Ray, Segment, Point3D, Line3D, Ray3D, Segment3D, Plane, Circle
from sympy.geometry.util import are_coplanar
from sympy.testing.pytest import raises
def test_plane():
x, y, z, u, v = symbols('x y z u v', real=True)
p1 = Point3D(0, 0, 0)
p2 = Point3D(1, 1, 1)
p3 = Point3D(1, 2, 3)
pl3 = Plane(p1, p2, p3)
pl4 = Plane(p1, normal_vector=(1, 1, 1))
pl4b = Plane(p1, p2)
pl5 = Plane(p3, normal_vector=(1, 2, 3))
pl6 = Plane(Point3D(2, 3, 7), normal_vector=(2, 2, 2))
pl7 = Plane(Point3D(1, -5, -6), normal_vector=(1, -2, 1))
pl8 = Plane(p1, normal_vector=(0, 0, 1))
pl9 = Plane(p1, normal_vector=(0, 12, 0))
pl10 = Plane(p1, normal_vector=(-2, 0, 0))
pl11 = Plane(p2, normal_vector=(0, 0, 1))
l1 = Line3D(Point3D(5, 0, 0), Point3D(1, -1, 1))
l2 = Line3D(Point3D(0, -2, 0), Point3D(3, 1, 1))
l3 = Line3D(Point3D(0, -1, 0), Point3D(5, -1, 9))
raises(ValueError, lambda: Plane(p1, p1, p1))
assert Plane(p1, p2, p3) != Plane(p1, p3, p2)
assert Plane(p1, p2, p3).is_coplanar(Plane(p1, p3, p2))
assert Plane(p1, p2, p3).is_coplanar(p1)
assert Plane(p1, p2, p3).is_coplanar(Circle(p1, 1)) is False
assert Plane(p1, normal_vector=(0, 0, 1)).is_coplanar(Circle(p1, 1))
assert pl3 == Plane(Point3D(0, 0, 0), normal_vector=(1, -2, 1))
assert pl3 != pl4
assert pl4 == pl4b
assert pl5 == Plane(Point3D(1, 2, 3), normal_vector=(1, 2, 3))
assert pl5.equation(x, y, z) == x + 2*y + 3*z - 14
assert pl3.equation(x, y, z) == x - 2*y + z
assert pl3.p1 == p1
assert pl4.p1 == p1
assert pl5.p1 == p3
assert pl4.normal_vector == (1, 1, 1)
assert pl5.normal_vector == (1, 2, 3)
assert p1 in pl3
assert p1 in pl4
assert p3 in pl5
assert pl3.projection(Point(0, 0)) == p1
p = pl3.projection(Point3D(1, 1, 0))
assert p == Point3D(Rational(7, 6), Rational(2, 3), Rational(1, 6))
assert p in pl3
l = pl3.projection_line(Line(Point(0, 0), Point(1, 1)))
assert l == Line3D(Point3D(0, 0, 0), Point3D(Rational(7, 6), Rational(2, 3), Rational(1, 6)))
assert l in pl3
# get a segment that does not intersect the plane which is also
# parallel to pl3's normal veector
t = Dummy()
r = pl3.random_point()
a = pl3.perpendicular_line(r).arbitrary_point(t)
s = Segment3D(a.subs(t, 1), a.subs(t, 2))
assert s.p1 not in pl3 and s.p2 not in pl3
assert pl3.projection_line(s).equals(r)
assert pl3.projection_line(Segment(Point(1, 0), Point(1, 1))) == \
Segment3D(Point3D(Rational(5, 6), Rational(1, 3), Rational(-1, 6)), Point3D(Rational(7, 6), Rational(2, 3), Rational(1, 6)))
assert pl6.projection_line(Ray(Point(1, 0), Point(1, 1))) == \
Ray3D(Point3D(Rational(14, 3), Rational(11, 3), Rational(11, 3)), Point3D(Rational(13, 3), Rational(13, 3), Rational(10, 3)))
assert pl3.perpendicular_line(r.args) == pl3.perpendicular_line(r)
assert pl3.is_parallel(pl6) is False
assert pl4.is_parallel(pl6)
assert pl3.is_parallel(Line(p1, p2))
assert pl6.is_parallel(l1) is False
assert pl3.is_perpendicular(pl6)
assert pl4.is_perpendicular(pl7)
assert pl6.is_perpendicular(pl7)
assert pl6.is_perpendicular(pl4) is False
assert pl6.is_perpendicular(l1) is False
assert pl6.is_perpendicular(Line((0, 0, 0), (1, 1, 1)))
assert pl6.is_perpendicular((1, 1)) is False
assert pl6.distance(pl6.arbitrary_point(u, v)) == 0
assert pl7.distance(pl7.arbitrary_point(u, v)) == 0
assert pl6.distance(pl6.arbitrary_point(t)) == 0
assert pl7.distance(pl7.arbitrary_point(t)) == 0
assert pl6.p1.distance(pl6.arbitrary_point(t)).simplify() == 1
assert pl7.p1.distance(pl7.arbitrary_point(t)).simplify() == 1
assert pl3.arbitrary_point(t) == Point3D(-sqrt(30)*sin(t)/30 + \
2*sqrt(5)*cos(t)/5, sqrt(30)*sin(t)/15 + sqrt(5)*cos(t)/5, sqrt(30)*sin(t)/6)
assert pl3.arbitrary_point(u, v) == Point3D(2*u - v, u + 2*v, 5*v)
assert pl7.distance(Point3D(1, 3, 5)) == 5*sqrt(6)/6
assert pl6.distance(Point3D(0, 0, 0)) == 4*sqrt(3)
assert pl6.distance(pl6.p1) == 0
assert pl7.distance(pl6) == 0
assert pl7.distance(l1) == 0
assert pl6.distance(Segment3D(Point3D(2, 3, 1), Point3D(1, 3, 4))) == \
pl6.distance(Point3D(1, 3, 4)) == 4*sqrt(3)/3
assert pl6.distance(Segment3D(Point3D(1, 3, 4), Point3D(0, 3, 7))) == \
pl6.distance(Point3D(0, 3, 7)) == 2*sqrt(3)/3
assert pl6.distance(Segment3D(Point3D(0, 3, 7), Point3D(-1, 3, 10))) == 0
assert pl6.distance(Segment3D(Point3D(-1, 3, 10), Point3D(-2, 3, 13))) == 0
assert pl6.distance(Segment3D(Point3D(-2, 3, 13), Point3D(-3, 3, 16))) == \
pl6.distance(Point3D(-2, 3, 13)) == 2*sqrt(3)/3
assert pl6.distance(Plane(Point3D(5, 5, 5), normal_vector=(8, 8, 8))) == sqrt(3)
assert pl6.distance(Ray3D(Point3D(1, 3, 4), direction_ratio=[1, 0, -3])) == 4*sqrt(3)/3
assert pl6.distance(Ray3D(Point3D(2, 3, 1), direction_ratio=[-1, 0, 3])) == 0
assert pl6.angle_between(pl3) == pi/2
assert pl6.angle_between(pl6) == 0
assert pl6.angle_between(pl4) == 0
assert pl7.angle_between(Line3D(Point3D(2, 3, 5), Point3D(2, 4, 6))) == \
-asin(sqrt(3)/6)
assert pl6.angle_between(Ray3D(Point3D(2, 4, 1), Point3D(6, 5, 3))) == \
asin(sqrt(7)/3)
assert pl7.angle_between(Segment3D(Point3D(5, 6, 1), Point3D(1, 2, 4))) == \
asin(7*sqrt(246)/246)
assert are_coplanar(l1, l2, l3) is False
assert are_coplanar(l1) is False
assert are_coplanar(Point3D(2, 7, 2), Point3D(0, 0, 2),
Point3D(1, 1, 2), Point3D(1, 2, 2))
assert are_coplanar(Plane(p1, p2, p3), Plane(p1, p3, p2))
assert Plane.are_concurrent(pl3, pl4, pl5) is False
assert Plane.are_concurrent(pl6) is False
raises(ValueError, lambda: Plane.are_concurrent(Point3D(0, 0, 0)))
raises(ValueError, lambda: Plane((1, 2, 3), normal_vector=(0, 0, 0)))
assert pl3.parallel_plane(Point3D(1, 2, 5)) == Plane(Point3D(1, 2, 5), \
normal_vector=(1, -2, 1))
# perpendicular_plane
p = Plane((0, 0, 0), (1, 0, 0))
# default
assert p.perpendicular_plane() == Plane(Point3D(0, 0, 0), (0, 1, 0))
# 1 pt
assert p.perpendicular_plane(Point3D(1, 0, 1)) == \
Plane(Point3D(1, 0, 1), (0, 1, 0))
# pts as tuples
assert p.perpendicular_plane((1, 0, 1), (1, 1, 1)) == \
Plane(Point3D(1, 0, 1), (0, 0, -1))
# more than two planes
raises(ValueError, lambda: p.perpendicular_plane((1, 0, 1), (1, 1, 1), (1, 1, 0)))
a, b = Point3D(0, 0, 0), Point3D(0, 1, 0)
Z = (0, 0, 1)
p = Plane(a, normal_vector=Z)
# case 4
assert p.perpendicular_plane(a, b) == Plane(a, (1, 0, 0))
n = Point3D(*Z)
# case 1
assert p.perpendicular_plane(a, n) == Plane(a, (-1, 0, 0))
# case 2
assert Plane(a, normal_vector=b.args).perpendicular_plane(a, a + b) == \
Plane(Point3D(0, 0, 0), (1, 0, 0))
# case 1&3
assert Plane(b, normal_vector=Z).perpendicular_plane(b, b + n) == \
Plane(Point3D(0, 1, 0), (-1, 0, 0))
# case 2&3
assert Plane(b, normal_vector=b.args).perpendicular_plane(n, n + b) == \
Plane(Point3D(0, 0, 1), (1, 0, 0))
p = Plane(a, normal_vector=(0, 0, 1))
assert p.perpendicular_plane() == Plane(a, normal_vector=(1, 0, 0))
assert pl6.intersection(pl6) == [pl6]
assert pl4.intersection(pl4.p1) == [pl4.p1]
assert pl3.intersection(pl6) == [
Line3D(Point3D(8, 4, 0), Point3D(2, 4, 6))]
assert pl3.intersection(Line3D(Point3D(1,2,4), Point3D(4,4,2))) == [
Point3D(2, Rational(8, 3), Rational(10, 3))]
assert pl3.intersection(Plane(Point3D(6, 0, 0), normal_vector=(2, -5, 3))
) == [Line3D(Point3D(-24, -12, 0), Point3D(-25, -13, -1))]
assert pl6.intersection(Ray3D(Point3D(2, 3, 1), Point3D(1, 3, 4))) == [
Point3D(-1, 3, 10)]
assert pl6.intersection(Segment3D(Point3D(2, 3, 1), Point3D(1, 3, 4))) == []
assert pl7.intersection(Line(Point(2, 3), Point(4, 2))) == [
Point3D(Rational(13, 2), Rational(3, 4), 0)]
r = Ray(Point(2, 3), Point(4, 2))
assert Plane((1,2,0), normal_vector=(0,0,1)).intersection(r) == [
Ray3D(Point(2, 3), Point(4, 2))]
assert pl9.intersection(pl8) == [Line3D(Point3D(0, 0, 0), Point3D(12, 0, 0))]
assert pl10.intersection(pl11) == [Line3D(Point3D(0, 0, 1), Point3D(0, 2, 1))]
assert pl4.intersection(pl8) == [Line3D(Point3D(0, 0, 0), Point3D(1, -1, 0))]
assert pl11.intersection(pl8) == []
assert pl9.intersection(pl11) == [Line3D(Point3D(0, 0, 1), Point3D(12, 0, 1))]
assert pl9.intersection(pl4) == [Line3D(Point3D(0, 0, 0), Point3D(12, 0, -12))]
assert pl3.random_point() in pl3
assert pl3.random_point(seed=1) in pl3
# test geometrical entity using equals
assert pl4.intersection(pl4.p1)[0].equals(pl4.p1)
assert pl3.intersection(pl6)[0].equals(Line3D(Point3D(8, 4, 0), Point3D(2, 4, 6)))
pl8 = Plane((1, 2, 0), normal_vector=(0, 0, 1))
assert pl8.intersection(Line3D(p1, (1, 12, 0)))[0].equals(Line((0, 0, 0), (0.1, 1.2, 0)))
assert pl8.intersection(Ray3D(p1, (1, 12, 0)))[0].equals(Ray((0, 0, 0), (1, 12, 0)))
assert pl8.intersection(Segment3D(p1, (21, 1, 0)))[0].equals(Segment3D(p1, (21, 1, 0)))
assert pl8.intersection(Plane(p1, normal_vector=(0, 0, 112)))[0].equals(pl8)
assert pl8.intersection(Plane(p1, normal_vector=(0, 12, 0)))[0].equals(
Line3D(p1, direction_ratio=(112 * pi, 0, 0)))
assert pl8.intersection(Plane(p1, normal_vector=(11, 0, 1)))[0].equals(
Line3D(p1, direction_ratio=(0, -11, 0)))
assert pl8.intersection(Plane(p1, normal_vector=(1, 0, 11)))[0].equals(
Line3D(p1, direction_ratio=(0, 11, 0)))
assert pl8.intersection(Plane(p1, normal_vector=(-1, -1, -11)))[0].equals(
Line3D(p1, direction_ratio=(1, -1, 0)))
assert pl3.random_point() in pl3
assert len(pl8.intersection(Ray3D(Point3D(0, 2, 3), Point3D(1, 0, 3)))) == 0
# check if two plane are equals
assert pl6.intersection(pl6)[0].equals(pl6)
assert pl8.equals(Plane(p1, normal_vector=(0, 12, 0))) is False
assert pl8.equals(pl8)
assert pl8.equals(Plane(p1, normal_vector=(0, 0, -12)))
assert pl8.equals(Plane(p1, normal_vector=(0, 0, -12*sqrt(3))))
assert pl8.equals(p1) is False
# issue 8570
l2 = Line3D(Point3D(Rational(50000004459633, 5000000000000),
Rational(-891926590718643, 1000000000000000),
Rational(231800966893633, 100000000000000)),
Point3D(Rational(50000004459633, 50000000000000),
Rational(-222981647679771, 250000000000000),
Rational(231800966893633, 100000000000000)))
p2 = Plane(Point3D(Rational(402775636372767, 100000000000000),
Rational(-97224357654973, 100000000000000),
Rational(216793600814789, 100000000000000)),
(-S('9.00000087501922'), -S('4.81170658872543e-13'),
S('0.0')))
assert str([i.n(2) for i in p2.intersection(l2)]) == \
'[Point3D(4.0, -0.89, 2.3)]'
def test_dimension_normalization():
A = Plane(Point3D(1, 1, 2), normal_vector=(1, 1, 1))
b = Point(1, 1)
assert A.projection(b) == Point(Rational(5, 3), Rational(5, 3), Rational(2, 3))
a, b = Point(0, 0), Point3D(0, 1)
Z = (0, 0, 1)
p = Plane(a, normal_vector=Z)
assert p.perpendicular_plane(a, b) == Plane(Point3D(0, 0, 0), (1, 0, 0))
assert Plane((1, 2, 1), (2, 1, 0), (3, 1, 2)
).intersection((2, 1)) == [Point(2, 1, 0)]
def test_parameter_value():
t, u, v = symbols("t, u v")
p1, p2, p3 = Point(0, 0, 0), Point(0, 0, 1), Point(0, 1, 0)
p = Plane(p1, p2, p3)
assert p.parameter_value((0, -3, 2), t) == {t: asin(2*sqrt(13)/13)}
assert p.parameter_value((0, -3, 2), u, v) == {u: 3, v: 2}
assert p.parameter_value(p1, t) == p1
raises(ValueError, lambda: p.parameter_value((1, 0, 0), t))
raises(ValueError, lambda: p.parameter_value(Line(Point(0, 0), Point(1, 1)), t))
raises(ValueError, lambda: p.parameter_value((0, -3, 2), t, 1))

View File

@ -0,0 +1,481 @@
from sympy.core.basic import Basic
from sympy.core.numbers import (I, Rational, pi)
from sympy.core.parameters import evaluate
from sympy.core.singleton import S
from sympy.core.symbol import Symbol
from sympy.core.sympify import sympify
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.geometry import Line, Point, Point2D, Point3D, Line3D, Plane
from sympy.geometry.entity import rotate, scale, translate, GeometryEntity
from sympy.matrices import Matrix
from sympy.utilities.iterables import subsets, permutations, cartes
from sympy.utilities.misc import Undecidable
from sympy.testing.pytest import raises, warns
def test_point():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
x1 = Symbol('x1', real=True)
x2 = Symbol('x2', real=True)
y1 = Symbol('y1', real=True)
y2 = Symbol('y2', real=True)
half = S.Half
p1 = Point(x1, x2)
p2 = Point(y1, y2)
p3 = Point(0, 0)
p4 = Point(1, 1)
p5 = Point(0, 1)
line = Line(Point(1, 0), slope=1)
assert p1 in p1
assert p1 not in p2
assert p2.y == y2
assert (p3 + p4) == p4
assert (p2 - p1) == Point(y1 - x1, y2 - x2)
assert -p2 == Point(-y1, -y2)
raises(TypeError, lambda: Point(1))
raises(ValueError, lambda: Point([1]))
raises(ValueError, lambda: Point(3, I))
raises(ValueError, lambda: Point(2*I, I))
raises(ValueError, lambda: Point(3 + I, I))
assert Point(34.05, sqrt(3)) == Point(Rational(681, 20), sqrt(3))
assert Point.midpoint(p3, p4) == Point(half, half)
assert Point.midpoint(p1, p4) == Point(half + half*x1, half + half*x2)
assert Point.midpoint(p2, p2) == p2
assert p2.midpoint(p2) == p2
assert p1.origin == Point(0, 0)
assert Point.distance(p3, p4) == sqrt(2)
assert Point.distance(p1, p1) == 0
assert Point.distance(p3, p2) == sqrt(p2.x**2 + p2.y**2)
raises(TypeError, lambda: Point.distance(p1, 0))
raises(TypeError, lambda: Point.distance(p1, GeometryEntity()))
# distance should be symmetric
assert p1.distance(line) == line.distance(p1)
assert p4.distance(line) == line.distance(p4)
assert Point.taxicab_distance(p4, p3) == 2
assert Point.canberra_distance(p4, p5) == 1
raises(ValueError, lambda: Point.canberra_distance(p3, p3))
p1_1 = Point(x1, x1)
p1_2 = Point(y2, y2)
p1_3 = Point(x1 + 1, x1)
assert Point.is_collinear(p3)
with warns(UserWarning, test_stacklevel=False):
assert Point.is_collinear(p3, Point(p3, dim=4))
assert p3.is_collinear()
assert Point.is_collinear(p3, p4)
assert Point.is_collinear(p3, p4, p1_1, p1_2)
assert Point.is_collinear(p3, p4, p1_1, p1_3) is False
assert Point.is_collinear(p3, p3, p4, p5) is False
raises(TypeError, lambda: Point.is_collinear(line))
raises(TypeError, lambda: p1_1.is_collinear(line))
assert p3.intersection(Point(0, 0)) == [p3]
assert p3.intersection(p4) == []
assert p3.intersection(line) == []
with warns(UserWarning, test_stacklevel=False):
assert Point.intersection(Point(0, 0, 0), Point(0, 0)) == [Point(0, 0, 0)]
x_pos = Symbol('x', positive=True)
p2_1 = Point(x_pos, 0)
p2_2 = Point(0, x_pos)
p2_3 = Point(-x_pos, 0)
p2_4 = Point(0, -x_pos)
p2_5 = Point(x_pos, 5)
assert Point.is_concyclic(p2_1)
assert Point.is_concyclic(p2_1, p2_2)
assert Point.is_concyclic(p2_1, p2_2, p2_3, p2_4)
for pts in permutations((p2_1, p2_2, p2_3, p2_5)):
assert Point.is_concyclic(*pts) is False
assert Point.is_concyclic(p4, p4 * 2, p4 * 3) is False
assert Point(0, 0).is_concyclic((1, 1), (2, 2), (2, 1)) is False
assert Point.is_concyclic(Point(0, 0, 0, 0), Point(1, 0, 0, 0), Point(1, 1, 0, 0), Point(1, 1, 1, 0)) is False
assert p1.is_scalar_multiple(p1)
assert p1.is_scalar_multiple(2*p1)
assert not p1.is_scalar_multiple(p2)
assert Point.is_scalar_multiple(Point(1, 1), (-1, -1))
assert Point.is_scalar_multiple(Point(0, 0), (0, -1))
# test when is_scalar_multiple can't be determined
raises(Undecidable, lambda: Point.is_scalar_multiple(Point(sympify("x1%y1"), sympify("x2%y2")), Point(0, 1)))
assert Point(0, 1).orthogonal_direction == Point(1, 0)
assert Point(1, 0).orthogonal_direction == Point(0, 1)
assert p1.is_zero is None
assert p3.is_zero
assert p4.is_zero is False
assert p1.is_nonzero is None
assert p3.is_nonzero is False
assert p4.is_nonzero
assert p4.scale(2, 3) == Point(2, 3)
assert p3.scale(2, 3) == p3
assert p4.rotate(pi, Point(0.5, 0.5)) == p3
assert p1.__radd__(p2) == p1.midpoint(p2).scale(2, 2)
assert (-p3).__rsub__(p4) == p3.midpoint(p4).scale(2, 2)
assert p4 * 5 == Point(5, 5)
assert p4 / 5 == Point(0.2, 0.2)
assert 5 * p4 == Point(5, 5)
raises(ValueError, lambda: Point(0, 0) + 10)
# Point differences should be simplified
assert Point(x*(x - 1), y) - Point(x**2 - x, y + 1) == Point(0, -1)
a, b = S.Half, Rational(1, 3)
assert Point(a, b).evalf(2) == \
Point(a.n(2), b.n(2), evaluate=False)
raises(ValueError, lambda: Point(1, 2) + 1)
# test project
assert Point.project((0, 1), (1, 0)) == Point(0, 0)
assert Point.project((1, 1), (1, 0)) == Point(1, 0)
raises(ValueError, lambda: Point.project(p1, Point(0, 0)))
# test transformations
p = Point(1, 0)
assert p.rotate(pi/2) == Point(0, 1)
assert p.rotate(pi/2, p) == p
p = Point(1, 1)
assert p.scale(2, 3) == Point(2, 3)
assert p.translate(1, 2) == Point(2, 3)
assert p.translate(1) == Point(2, 1)
assert p.translate(y=1) == Point(1, 2)
assert p.translate(*p.args) == Point(2, 2)
# Check invalid input for transform
raises(ValueError, lambda: p3.transform(p3))
raises(ValueError, lambda: p.transform(Matrix([[1, 0], [0, 1]])))
# test __contains__
assert 0 in Point(0, 0, 0, 0)
assert 1 not in Point(0, 0, 0, 0)
# test affine_rank
assert Point.affine_rank() == -1
def test_point3D():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
x1 = Symbol('x1', real=True)
x2 = Symbol('x2', real=True)
x3 = Symbol('x3', real=True)
y1 = Symbol('y1', real=True)
y2 = Symbol('y2', real=True)
y3 = Symbol('y3', real=True)
half = S.Half
p1 = Point3D(x1, x2, x3)
p2 = Point3D(y1, y2, y3)
p3 = Point3D(0, 0, 0)
p4 = Point3D(1, 1, 1)
p5 = Point3D(0, 1, 2)
assert p1 in p1
assert p1 not in p2
assert p2.y == y2
assert (p3 + p4) == p4
assert (p2 - p1) == Point3D(y1 - x1, y2 - x2, y3 - x3)
assert -p2 == Point3D(-y1, -y2, -y3)
assert Point(34.05, sqrt(3)) == Point(Rational(681, 20), sqrt(3))
assert Point3D.midpoint(p3, p4) == Point3D(half, half, half)
assert Point3D.midpoint(p1, p4) == Point3D(half + half*x1, half + half*x2,
half + half*x3)
assert Point3D.midpoint(p2, p2) == p2
assert p2.midpoint(p2) == p2
assert Point3D.distance(p3, p4) == sqrt(3)
assert Point3D.distance(p1, p1) == 0
assert Point3D.distance(p3, p2) == sqrt(p2.x**2 + p2.y**2 + p2.z**2)
p1_1 = Point3D(x1, x1, x1)
p1_2 = Point3D(y2, y2, y2)
p1_3 = Point3D(x1 + 1, x1, x1)
Point3D.are_collinear(p3)
assert Point3D.are_collinear(p3, p4)
assert Point3D.are_collinear(p3, p4, p1_1, p1_2)
assert Point3D.are_collinear(p3, p4, p1_1, p1_3) is False
assert Point3D.are_collinear(p3, p3, p4, p5) is False
assert p3.intersection(Point3D(0, 0, 0)) == [p3]
assert p3.intersection(p4) == []
assert p4 * 5 == Point3D(5, 5, 5)
assert p4 / 5 == Point3D(0.2, 0.2, 0.2)
assert 5 * p4 == Point3D(5, 5, 5)
raises(ValueError, lambda: Point3D(0, 0, 0) + 10)
# Test coordinate properties
assert p1.coordinates == (x1, x2, x3)
assert p2.coordinates == (y1, y2, y3)
assert p3.coordinates == (0, 0, 0)
assert p4.coordinates == (1, 1, 1)
assert p5.coordinates == (0, 1, 2)
assert p5.x == 0
assert p5.y == 1
assert p5.z == 2
# Point differences should be simplified
assert Point3D(x*(x - 1), y, 2) - Point3D(x**2 - x, y + 1, 1) == \
Point3D(0, -1, 1)
a, b, c = S.Half, Rational(1, 3), Rational(1, 4)
assert Point3D(a, b, c).evalf(2) == \
Point(a.n(2), b.n(2), c.n(2), evaluate=False)
raises(ValueError, lambda: Point3D(1, 2, 3) + 1)
# test transformations
p = Point3D(1, 1, 1)
assert p.scale(2, 3) == Point3D(2, 3, 1)
assert p.translate(1, 2) == Point3D(2, 3, 1)
assert p.translate(1) == Point3D(2, 1, 1)
assert p.translate(z=1) == Point3D(1, 1, 2)
assert p.translate(*p.args) == Point3D(2, 2, 2)
# Test __new__
assert Point3D(0.1, 0.2, evaluate=False, on_morph='ignore').args[0].is_Float
# Test length property returns correctly
assert p.length == 0
assert p1_1.length == 0
assert p1_2.length == 0
# Test are_colinear type error
raises(TypeError, lambda: Point3D.are_collinear(p, x))
# Test are_coplanar
assert Point.are_coplanar()
assert Point.are_coplanar((1, 2, 0), (1, 2, 0), (1, 3, 0))
assert Point.are_coplanar((1, 2, 0), (1, 2, 3))
with warns(UserWarning, test_stacklevel=False):
raises(ValueError, lambda: Point2D.are_coplanar((1, 2), (1, 2, 3)))
assert Point3D.are_coplanar((1, 2, 0), (1, 2, 3))
assert Point.are_coplanar((0, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 1)) is False
planar2 = Point3D(1, -1, 1)
planar3 = Point3D(-1, 1, 1)
assert Point3D.are_coplanar(p, planar2, planar3) == True
assert Point3D.are_coplanar(p, planar2, planar3, p3) == False
assert Point.are_coplanar(p, planar2)
planar2 = Point3D(1, 1, 2)
planar3 = Point3D(1, 1, 3)
assert Point3D.are_coplanar(p, planar2, planar3) # line, not plane
plane = Plane((1, 2, 1), (2, 1, 0), (3, 1, 2))
assert Point.are_coplanar(*[plane.projection(((-1)**i, i)) for i in range(4)])
# all 2D points are coplanar
assert Point.are_coplanar(Point(x, y), Point(x, x + y), Point(y, x + 2)) is True
# Test Intersection
assert planar2.intersection(Line3D(p, planar3)) == [Point3D(1, 1, 2)]
# Test Scale
assert planar2.scale(1, 1, 1) == planar2
assert planar2.scale(2, 2, 2, planar3) == Point3D(1, 1, 1)
assert planar2.scale(1, 1, 1, p3) == planar2
# Test Transform
identity = Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
assert p.transform(identity) == p
trans = Matrix([[1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 1], [0, 0, 0, 1]])
assert p.transform(trans) == Point3D(2, 2, 2)
raises(ValueError, lambda: p.transform(p))
raises(ValueError, lambda: p.transform(Matrix([[1, 0], [0, 1]])))
# Test Equals
assert p.equals(x1) == False
# Test __sub__
p_4d = Point(0, 0, 0, 1)
with warns(UserWarning, test_stacklevel=False):
assert p - p_4d == Point(1, 1, 1, -1)
p_4d3d = Point(0, 0, 1, 0)
with warns(UserWarning, test_stacklevel=False):
assert p - p_4d3d == Point(1, 1, 0, 0)
def test_Point2D():
# Test Distance
p1 = Point2D(1, 5)
p2 = Point2D(4, 2.5)
p3 = (6, 3)
assert p1.distance(p2) == sqrt(61)/2
assert p2.distance(p3) == sqrt(17)/2
# Test coordinates
assert p1.x == 1
assert p1.y == 5
assert p2.x == 4
assert p2.y == S(5)/2
assert p1.coordinates == (1, 5)
assert p2.coordinates == (4, S(5)/2)
# test bounds
assert p1.bounds == (1, 5, 1, 5)
def test_issue_9214():
p1 = Point3D(4, -2, 6)
p2 = Point3D(1, 2, 3)
p3 = Point3D(7, 2, 3)
assert Point3D.are_collinear(p1, p2, p3) is False
def test_issue_11617():
p1 = Point3D(1,0,2)
p2 = Point2D(2,0)
with warns(UserWarning, test_stacklevel=False):
assert p1.distance(p2) == sqrt(5)
def test_transform():
p = Point(1, 1)
assert p.transform(rotate(pi/2)) == Point(-1, 1)
assert p.transform(scale(3, 2)) == Point(3, 2)
assert p.transform(translate(1, 2)) == Point(2, 3)
assert Point(1, 1).scale(2, 3, (4, 5)) == \
Point(-2, -7)
assert Point(1, 1).translate(4, 5) == \
Point(5, 6)
def test_concyclic_doctest_bug():
p1, p2 = Point(-1, 0), Point(1, 0)
p3, p4 = Point(0, 1), Point(-1, 2)
assert Point.is_concyclic(p1, p2, p3)
assert not Point.is_concyclic(p1, p2, p3, p4)
def test_arguments():
"""Functions accepting `Point` objects in `geometry`
should also accept tuples and lists and
automatically convert them to points."""
singles2d = ((1,2), [1,2], Point(1,2))
singles2d2 = ((1,3), [1,3], Point(1,3))
doubles2d = cartes(singles2d, singles2d2)
p2d = Point2D(1,2)
singles3d = ((1,2,3), [1,2,3], Point(1,2,3))
doubles3d = subsets(singles3d, 2)
p3d = Point3D(1,2,3)
singles4d = ((1,2,3,4), [1,2,3,4], Point(1,2,3,4))
doubles4d = subsets(singles4d, 2)
p4d = Point(1,2,3,4)
# test 2D
test_single = ['distance', 'is_scalar_multiple', 'taxicab_distance', 'midpoint', 'intersection', 'dot', 'equals', '__add__', '__sub__']
test_double = ['is_concyclic', 'is_collinear']
for p in singles2d:
Point2D(p)
for func in test_single:
for p in singles2d:
getattr(p2d, func)(p)
for func in test_double:
for p in doubles2d:
getattr(p2d, func)(*p)
# test 3D
test_double = ['is_collinear']
for p in singles3d:
Point3D(p)
for func in test_single:
for p in singles3d:
getattr(p3d, func)(p)
for func in test_double:
for p in doubles3d:
getattr(p3d, func)(*p)
# test 4D
test_double = ['is_collinear']
for p in singles4d:
Point(p)
for func in test_single:
for p in singles4d:
getattr(p4d, func)(p)
for func in test_double:
for p in doubles4d:
getattr(p4d, func)(*p)
# test evaluate=False for ops
x = Symbol('x')
a = Point(0, 1)
assert a + (0.1, x) == Point(0.1, 1 + x, evaluate=False)
a = Point(0, 1)
assert a/10.0 == Point(0, 0.1, evaluate=False)
a = Point(0, 1)
assert a*10.0 == Point(0.0, 10.0, evaluate=False)
# test evaluate=False when changing dimensions
u = Point(.1, .2, evaluate=False)
u4 = Point(u, dim=4, on_morph='ignore')
assert u4.args == (.1, .2, 0, 0)
assert all(i.is_Float for i in u4.args[:2])
# and even when *not* changing dimensions
assert all(i.is_Float for i in Point(u).args)
# never raise error if creating an origin
assert Point(dim=3, on_morph='error')
# raise error with unmatched dimension
raises(ValueError, lambda: Point(1, 1, dim=3, on_morph='error'))
# test unknown on_morph
raises(ValueError, lambda: Point(1, 1, dim=3, on_morph='unknown'))
# test invalid expressions
raises(TypeError, lambda: Point(Basic(), Basic()))
def test_unit():
assert Point(1, 1).unit == Point(sqrt(2)/2, sqrt(2)/2)
def test_dot():
raises(TypeError, lambda: Point(1, 2).dot(Line((0, 0), (1, 1))))
def test__normalize_dimension():
assert Point._normalize_dimension(Point(1, 2), Point(3, 4)) == [
Point(1, 2), Point(3, 4)]
assert Point._normalize_dimension(
Point(1, 2), Point(3, 4, 0), on_morph='ignore') == [
Point(1, 2, 0), Point(3, 4, 0)]
def test_issue_22684():
# Used to give an error
with evaluate(False):
Point(1, 2)
def test_direction_cosine():
p1 = Point3D(0, 0, 0)
p2 = Point3D(1, 1, 1)
assert p1.direction_cosine(Point3D(1, 0, 0)) == [1, 0, 0]
assert p1.direction_cosine(Point3D(0, 1, 0)) == [0, 1, 0]
assert p1.direction_cosine(Point3D(0, 0, pi)) == [0, 0, 1]
assert p1.direction_cosine(Point3D(5, 0, 0)) == [1, 0, 0]
assert p1.direction_cosine(Point3D(0, sqrt(3), 0)) == [0, 1, 0]
assert p1.direction_cosine(Point3D(0, 0, 5)) == [0, 0, 1]
assert p1.direction_cosine(Point3D(2.4, 2.4, 0)) == [sqrt(2)/2, sqrt(2)/2, 0]
assert p1.direction_cosine(Point3D(1, 1, 1)) == [sqrt(3) / 3, sqrt(3) / 3, sqrt(3) / 3]
assert p1.direction_cosine(Point3D(-12, 0 -15)) == [-4*sqrt(41)/41, -5*sqrt(41)/41, 0]
assert p2.direction_cosine(Point3D(0, 0, 0)) == [-sqrt(3) / 3, -sqrt(3) / 3, -sqrt(3) / 3]
assert p2.direction_cosine(Point3D(1, 1, 12)) == [0, 0, 1]
assert p2.direction_cosine(Point3D(12, 1, 12)) == [sqrt(2) / 2, 0, sqrt(2) / 2]

View File

@ -0,0 +1,676 @@
from sympy.core.numbers import (Float, Rational, oo, pi)
from sympy.core.singleton import S
from sympy.core.symbol import (Symbol, symbols)
from sympy.functions.elementary.complexes import Abs
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.trigonometric import (acos, cos, sin)
from sympy.functions.elementary.trigonometric import tan
from sympy.geometry import (Circle, Ellipse, GeometryError, Point, Point2D,
Polygon, Ray, RegularPolygon, Segment, Triangle,
are_similar, convex_hull, intersection, Line, Ray2D)
from sympy.testing.pytest import raises, slow, warns
from sympy.core.random import verify_numerically
from sympy.geometry.polygon import rad, deg
from sympy.integrals.integrals import integrate
from sympy.utilities.iterables import rotate_left
def feq(a, b):
"""Test if two floating point values are 'equal'."""
t_float = Float("1.0E-10")
return -t_float < a - b < t_float
@slow
def test_polygon():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
q = Symbol('q', real=True)
u = Symbol('u', real=True)
v = Symbol('v', real=True)
w = Symbol('w', real=True)
x1 = Symbol('x1', real=True)
half = S.Half
a, b, c = Point(0, 0), Point(2, 0), Point(3, 3)
t = Triangle(a, b, c)
assert Polygon(Point(0, 0)) == Point(0, 0)
assert Polygon(a, Point(1, 0), b, c) == t
assert Polygon(Point(1, 0), b, c, a) == t
assert Polygon(b, c, a, Point(1, 0)) == t
# 2 "remove folded" tests
assert Polygon(a, Point(3, 0), b, c) == t
assert Polygon(a, b, Point(3, -1), b, c) == t
# remove multiple collinear points
assert Polygon(Point(-4, 15), Point(-11, 15), Point(-15, 15),
Point(-15, 33/5), Point(-15, -87/10), Point(-15, -15),
Point(-42/5, -15), Point(-2, -15), Point(7, -15), Point(15, -15),
Point(15, -3), Point(15, 10), Point(15, 15)) == \
Polygon(Point(-15, -15), Point(15, -15), Point(15, 15), Point(-15, 15))
p1 = Polygon(
Point(0, 0), Point(3, -1),
Point(6, 0), Point(4, 5),
Point(2, 3), Point(0, 3))
p2 = Polygon(
Point(6, 0), Point(3, -1),
Point(0, 0), Point(0, 3),
Point(2, 3), Point(4, 5))
p3 = Polygon(
Point(0, 0), Point(3, 0),
Point(5, 2), Point(4, 4))
p4 = Polygon(
Point(0, 0), Point(4, 4),
Point(5, 2), Point(3, 0))
p5 = Polygon(
Point(0, 0), Point(4, 4),
Point(0, 4))
p6 = Polygon(
Point(-11, 1), Point(-9, 6.6),
Point(-4, -3), Point(-8.4, -8.7))
p7 = Polygon(
Point(x, y), Point(q, u),
Point(v, w))
p8 = Polygon(
Point(x, y), Point(v, w),
Point(q, u))
p9 = Polygon(
Point(0, 0), Point(4, 4),
Point(3, 0), Point(5, 2))
p10 = Polygon(
Point(0, 2), Point(2, 2),
Point(0, 0), Point(2, 0))
p11 = Polygon(Point(0, 0), 1, n=3)
p12 = Polygon(Point(0, 0), 1, 0, n=3)
p13 = Polygon(
Point(0, 0),Point(8, 8),
Point(23, 20),Point(0, 20))
p14 = Polygon(*rotate_left(p13.args, 1))
r = Ray(Point(-9, 6.6), Point(-9, 5.5))
#
# General polygon
#
assert p1 == p2
assert len(p1.args) == 6
assert len(p1.sides) == 6
assert p1.perimeter == 5 + 2*sqrt(10) + sqrt(29) + sqrt(8)
assert p1.area == 22
assert not p1.is_convex()
assert Polygon((-1, 1), (2, -1), (2, 1), (-1, -1), (3, 0)
).is_convex() is False
# ensure convex for both CW and CCW point specification
assert p3.is_convex()
assert p4.is_convex()
dict5 = p5.angles
assert dict5[Point(0, 0)] == pi / 4
assert dict5[Point(0, 4)] == pi / 2
assert p5.encloses_point(Point(x, y)) is None
assert p5.encloses_point(Point(1, 3))
assert p5.encloses_point(Point(0, 0)) is False
assert p5.encloses_point(Point(4, 0)) is False
assert p1.encloses(Circle(Point(2.5, 2.5), 5)) is False
assert p1.encloses(Ellipse(Point(2.5, 2), 5, 6)) is False
assert p5.plot_interval('x') == [x, 0, 1]
assert p5.distance(
Polygon(Point(10, 10), Point(14, 14), Point(10, 14))) == 6 * sqrt(2)
assert p5.distance(
Polygon(Point(1, 8), Point(5, 8), Point(8, 12), Point(1, 12))) == 4
with warns(UserWarning, \
match="Polygons may intersect producing erroneous output"):
Polygon(Point(0, 0), Point(1, 0), Point(1, 1)).distance(
Polygon(Point(0, 0), Point(0, 1), Point(1, 1)))
assert hash(p5) == hash(Polygon(Point(0, 0), Point(4, 4), Point(0, 4)))
assert hash(p1) == hash(p2)
assert hash(p7) == hash(p8)
assert hash(p3) != hash(p9)
assert p5 == Polygon(Point(4, 4), Point(0, 4), Point(0, 0))
assert Polygon(Point(4, 4), Point(0, 4), Point(0, 0)) in p5
assert p5 != Point(0, 4)
assert Point(0, 1) in p5
assert p5.arbitrary_point('t').subs(Symbol('t', real=True), 0) == \
Point(0, 0)
raises(ValueError, lambda: Polygon(
Point(x, 0), Point(0, y), Point(x, y)).arbitrary_point('x'))
assert p6.intersection(r) == [Point(-9, Rational(-84, 13)), Point(-9, Rational(33, 5))]
assert p10.area == 0
assert p11 == RegularPolygon(Point(0, 0), 1, 3, 0)
assert p11 == p12
assert p11.vertices[0] == Point(1, 0)
assert p11.args[0] == Point(0, 0)
p11.spin(pi/2)
assert p11.vertices[0] == Point(0, 1)
#
# Regular polygon
#
p1 = RegularPolygon(Point(0, 0), 10, 5)
p2 = RegularPolygon(Point(0, 0), 5, 5)
raises(GeometryError, lambda: RegularPolygon(Point(0, 0), Point(0,
1), Point(1, 1)))
raises(GeometryError, lambda: RegularPolygon(Point(0, 0), 1, 2))
raises(ValueError, lambda: RegularPolygon(Point(0, 0), 1, 2.5))
assert p1 != p2
assert p1.interior_angle == pi*Rational(3, 5)
assert p1.exterior_angle == pi*Rational(2, 5)
assert p2.apothem == 5*cos(pi/5)
assert p2.circumcenter == p1.circumcenter == Point(0, 0)
assert p1.circumradius == p1.radius == 10
assert p2.circumcircle == Circle(Point(0, 0), 5)
assert p2.incircle == Circle(Point(0, 0), p2.apothem)
assert p2.inradius == p2.apothem == (5 * (1 + sqrt(5)) / 4)
p2.spin(pi / 10)
dict1 = p2.angles
assert dict1[Point(0, 5)] == 3 * pi / 5
assert p1.is_convex()
assert p1.rotation == 0
assert p1.encloses_point(Point(0, 0))
assert p1.encloses_point(Point(11, 0)) is False
assert p2.encloses_point(Point(0, 4.9))
p1.spin(pi/3)
assert p1.rotation == pi/3
assert p1.vertices[0] == Point(5, 5*sqrt(3))
for var in p1.args:
if isinstance(var, Point):
assert var == Point(0, 0)
else:
assert var in (5, 10, pi / 3)
assert p1 != Point(0, 0)
assert p1 != p5
# while spin works in place (notice that rotation is 2pi/3 below)
# rotate returns a new object
p1_old = p1
assert p1.rotate(pi/3) == RegularPolygon(Point(0, 0), 10, 5, pi*Rational(2, 3))
assert p1 == p1_old
assert p1.area == (-250*sqrt(5) + 1250)/(4*tan(pi/5))
assert p1.length == 20*sqrt(-sqrt(5)/8 + Rational(5, 8))
assert p1.scale(2, 2) == \
RegularPolygon(p1.center, p1.radius*2, p1._n, p1.rotation)
assert RegularPolygon((0, 0), 1, 4).scale(2, 3) == \
Polygon(Point(2, 0), Point(0, 3), Point(-2, 0), Point(0, -3))
assert repr(p1) == str(p1)
#
# Angles
#
angles = p4.angles
assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483"))
assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544"))
assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388"))
assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449"))
angles = p3.angles
assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483"))
assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544"))
assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388"))
assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449"))
# https://github.com/sympy/sympy/issues/24885
interior_angles_sum = sum(p13.angles.values())
assert feq(interior_angles_sum, (len(p13.angles) - 2)*pi )
interior_angles_sum = sum(p14.angles.values())
assert feq(interior_angles_sum, (len(p14.angles) - 2)*pi )
#
# Triangle
#
p1 = Point(0, 0)
p2 = Point(5, 0)
p3 = Point(0, 5)
t1 = Triangle(p1, p2, p3)
t2 = Triangle(p1, p2, Point(Rational(5, 2), sqrt(Rational(75, 4))))
t3 = Triangle(p1, Point(x1, 0), Point(0, x1))
s1 = t1.sides
assert Triangle(p1, p2, p1) == Polygon(p1, p2, p1) == Segment(p1, p2)
raises(GeometryError, lambda: Triangle(Point(0, 0)))
# Basic stuff
assert Triangle(p1, p1, p1) == p1
assert Triangle(p2, p2*2, p2*3) == Segment(p2, p2*3)
assert t1.area == Rational(25, 2)
assert t1.is_right()
assert t2.is_right() is False
assert t3.is_right()
assert p1 in t1
assert t1.sides[0] in t1
assert Segment((0, 0), (1, 0)) in t1
assert Point(5, 5) not in t2
assert t1.is_convex()
assert feq(t1.angles[p1].evalf(), pi.evalf()/2)
assert t1.is_equilateral() is False
assert t2.is_equilateral()
assert t3.is_equilateral() is False
assert are_similar(t1, t2) is False
assert are_similar(t1, t3)
assert are_similar(t2, t3) is False
assert t1.is_similar(Point(0, 0)) is False
assert t1.is_similar(t2) is False
# Bisectors
bisectors = t1.bisectors()
assert bisectors[p1] == Segment(
p1, Point(Rational(5, 2), Rational(5, 2)))
assert t2.bisectors()[p2] == Segment(
Point(5, 0), Point(Rational(5, 4), 5*sqrt(3)/4))
p4 = Point(0, x1)
assert t3.bisectors()[p4] == Segment(p4, Point(x1*(sqrt(2) - 1), 0))
ic = (250 - 125*sqrt(2))/50
assert t1.incenter == Point(ic, ic)
# Inradius
assert t1.inradius == t1.incircle.radius == 5 - 5*sqrt(2)/2
assert t2.inradius == t2.incircle.radius == 5*sqrt(3)/6
assert t3.inradius == t3.incircle.radius == x1**2/((2 + sqrt(2))*Abs(x1))
# Exradius
assert t1.exradii[t1.sides[2]] == 5*sqrt(2)/2
# Excenters
assert t1.excenters[t1.sides[2]] == Point2D(25*sqrt(2), -5*sqrt(2)/2)
# Circumcircle
assert t1.circumcircle.center == Point(2.5, 2.5)
# Medians + Centroid
m = t1.medians
assert t1.centroid == Point(Rational(5, 3), Rational(5, 3))
assert m[p1] == Segment(p1, Point(Rational(5, 2), Rational(5, 2)))
assert t3.medians[p1] == Segment(p1, Point(x1/2, x1/2))
assert intersection(m[p1], m[p2], m[p3]) == [t1.centroid]
assert t1.medial == Triangle(Point(2.5, 0), Point(0, 2.5), Point(2.5, 2.5))
# Nine-point circle
assert t1.nine_point_circle == Circle(Point(2.5, 0),
Point(0, 2.5), Point(2.5, 2.5))
assert t1.nine_point_circle == Circle(Point(0, 0),
Point(0, 2.5), Point(2.5, 2.5))
# Perpendicular
altitudes = t1.altitudes
assert altitudes[p1] == Segment(p1, Point(Rational(5, 2), Rational(5, 2)))
assert altitudes[p2].equals(s1[0])
assert altitudes[p3] == s1[2]
assert t1.orthocenter == p1
t = S('''Triangle(
Point(100080156402737/5000000000000, 79782624633431/500000000000),
Point(39223884078253/2000000000000, 156345163124289/1000000000000),
Point(31241359188437/1250000000000, 338338270939941/1000000000000000))''')
assert t.orthocenter == S('''Point(-780660869050599840216997'''
'''79471538701955848721853/80368430960602242240789074233100000000000000,'''
'''20151573611150265741278060334545897615974257/16073686192120448448157'''
'''8148466200000000000)''')
# Ensure
assert len(intersection(*bisectors.values())) == 1
assert len(intersection(*altitudes.values())) == 1
assert len(intersection(*m.values())) == 1
# Distance
p1 = Polygon(
Point(0, 0), Point(1, 0),
Point(1, 1), Point(0, 1))
p2 = Polygon(
Point(0, Rational(5)/4), Point(1, Rational(5)/4),
Point(1, Rational(9)/4), Point(0, Rational(9)/4))
p3 = Polygon(
Point(1, 2), Point(2, 2),
Point(2, 1))
p4 = Polygon(
Point(1, 1), Point(Rational(6)/5, 1),
Point(1, Rational(6)/5))
pt1 = Point(half, half)
pt2 = Point(1, 1)
'''Polygon to Point'''
assert p1.distance(pt1) == half
assert p1.distance(pt2) == 0
assert p2.distance(pt1) == Rational(3)/4
assert p3.distance(pt2) == sqrt(2)/2
'''Polygon to Polygon'''
# p1.distance(p2) emits a warning
with warns(UserWarning, \
match="Polygons may intersect producing erroneous output"):
assert p1.distance(p2) == half/2
assert p1.distance(p3) == sqrt(2)/2
# p3.distance(p4) emits a warning
with warns(UserWarning, \
match="Polygons may intersect producing erroneous output"):
assert p3.distance(p4) == (sqrt(2)/2 - sqrt(Rational(2)/25)/2)
def test_convex_hull():
p = [Point(-5, -1), Point(-2, 1), Point(-2, -1), Point(-1, -3), \
Point(0, 0), Point(1, 1), Point(2, 2), Point(2, -1), Point(3, 1), \
Point(4, -1), Point(6, 2)]
ch = Polygon(p[0], p[3], p[9], p[10], p[6], p[1])
#test handling of duplicate points
p.append(p[3])
#more than 3 collinear points
another_p = [Point(-45, -85), Point(-45, 85), Point(-45, 26), \
Point(-45, -24)]
ch2 = Segment(another_p[0], another_p[1])
assert convex_hull(*another_p) == ch2
assert convex_hull(*p) == ch
assert convex_hull(p[0]) == p[0]
assert convex_hull(p[0], p[1]) == Segment(p[0], p[1])
# no unique points
assert convex_hull(*[p[-1]]*3) == p[-1]
# collection of items
assert convex_hull(*[Point(0, 0), \
Segment(Point(1, 0), Point(1, 1)), \
RegularPolygon(Point(2, 0), 2, 4)]) == \
Polygon(Point(0, 0), Point(2, -2), Point(4, 0), Point(2, 2))
def test_encloses():
# square with a dimpled left side
s = Polygon(Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1), \
Point(S.Half, S.Half))
# the following is True if the polygon isn't treated as closing on itself
assert s.encloses(Point(0, S.Half)) is False
assert s.encloses(Point(S.Half, S.Half)) is False # it's a vertex
assert s.encloses(Point(Rational(3, 4), S.Half)) is True
def test_triangle_kwargs():
assert Triangle(sss=(3, 4, 5)) == \
Triangle(Point(0, 0), Point(3, 0), Point(3, 4))
assert Triangle(asa=(30, 2, 30)) == \
Triangle(Point(0, 0), Point(2, 0), Point(1, sqrt(3)/3))
assert Triangle(sas=(1, 45, 2)) == \
Triangle(Point(0, 0), Point(2, 0), Point(sqrt(2)/2, sqrt(2)/2))
assert Triangle(sss=(1, 2, 5)) is None
assert deg(rad(180)) == 180
def test_transform():
pts = [Point(0, 0), Point(S.Half, Rational(1, 4)), Point(1, 1)]
pts_out = [Point(-4, -10), Point(-3, Rational(-37, 4)), Point(-2, -7)]
assert Triangle(*pts).scale(2, 3, (4, 5)) == Triangle(*pts_out)
assert RegularPolygon((0, 0), 1, 4).scale(2, 3, (4, 5)) == \
Polygon(Point(-2, -10), Point(-4, -7), Point(-6, -10), Point(-4, -13))
# Checks for symmetric scaling
assert RegularPolygon((0, 0), 1, 4).scale(2, 2) == \
RegularPolygon(Point2D(0, 0), 2, 4, 0)
def test_reflect():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
b = Symbol('b')
m = Symbol('m')
l = Line((0, b), slope=m)
p = Point(x, y)
r = p.reflect(l)
dp = l.perpendicular_segment(p).length
dr = l.perpendicular_segment(r).length
assert verify_numerically(dp, dr)
assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((3, 0), slope=oo)) \
== Triangle(Point(5, 0), Point(4, 0), Point(4, 2))
assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((0, 3), slope=oo)) \
== Triangle(Point(-1, 0), Point(-2, 0), Point(-2, 2))
assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((0, 3), slope=0)) \
== Triangle(Point(1, 6), Point(2, 6), Point(2, 4))
assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((3, 0), slope=0)) \
== Triangle(Point(1, 0), Point(2, 0), Point(2, -2))
def test_bisectors():
p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
p = Polygon(Point(0, 0), Point(2, 0), Point(1, 1), Point(0, 3))
q = Polygon(Point(1, 0), Point(2, 0), Point(3, 3), Point(-1, 5))
poly = Polygon(Point(3, 4), Point(0, 0), Point(8, 7), Point(-1, 1), Point(19, -19))
t = Triangle(p1, p2, p3)
assert t.bisectors()[p2] == Segment(Point(1, 0), Point(0, sqrt(2) - 1))
assert p.bisectors()[Point2D(0, 3)] == Ray2D(Point2D(0, 3), \
Point2D(sin(acos(2*sqrt(5)/5)/2), 3 - cos(acos(2*sqrt(5)/5)/2)))
assert q.bisectors()[Point2D(-1, 5)] == \
Ray2D(Point2D(-1, 5), Point2D(-1 + sqrt(29)*(5*sin(acos(9*sqrt(145)/145)/2) + \
2*cos(acos(9*sqrt(145)/145)/2))/29, sqrt(29)*(-5*cos(acos(9*sqrt(145)/145)/2) + \
2*sin(acos(9*sqrt(145)/145)/2))/29 + 5))
assert poly.bisectors()[Point2D(-1, 1)] == Ray2D(Point2D(-1, 1), \
Point2D(-1 + sin(acos(sqrt(26)/26)/2 + pi/4), 1 - sin(-acos(sqrt(26)/26)/2 + pi/4)))
def test_incenter():
assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).incenter \
== Point(1 - sqrt(2)/2, 1 - sqrt(2)/2)
def test_inradius():
assert Triangle(Point(0, 0), Point(4, 0), Point(0, 3)).inradius == 1
def test_incircle():
assert Triangle(Point(0, 0), Point(2, 0), Point(0, 2)).incircle \
== Circle(Point(2 - sqrt(2), 2 - sqrt(2)), 2 - sqrt(2))
def test_exradii():
t = Triangle(Point(0, 0), Point(6, 0), Point(0, 2))
assert t.exradii[t.sides[2]] == (-2 + sqrt(10))
def test_medians():
t = Triangle(Point(0, 0), Point(1, 0), Point(0, 1))
assert t.medians[Point(0, 0)] == Segment(Point(0, 0), Point(S.Half, S.Half))
def test_medial():
assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).medial \
== Triangle(Point(S.Half, 0), Point(S.Half, S.Half), Point(0, S.Half))
def test_nine_point_circle():
assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).nine_point_circle \
== Circle(Point2D(Rational(1, 4), Rational(1, 4)), sqrt(2)/4)
def test_eulerline():
assert Triangle(Point(0, 0), Point(1, 0), Point(0, 1)).eulerline \
== Line(Point2D(0, 0), Point2D(S.Half, S.Half))
assert Triangle(Point(0, 0), Point(10, 0), Point(5, 5*sqrt(3))).eulerline \
== Point2D(5, 5*sqrt(3)/3)
assert Triangle(Point(4, -6), Point(4, -1), Point(-3, 3)).eulerline \
== Line(Point2D(Rational(64, 7), 3), Point2D(Rational(-29, 14), Rational(-7, 2)))
def test_intersection():
poly1 = Triangle(Point(0, 0), Point(1, 0), Point(0, 1))
poly2 = Polygon(Point(0, 1), Point(-5, 0),
Point(0, -4), Point(0, Rational(1, 5)),
Point(S.Half, -0.1), Point(1, 0), Point(0, 1))
assert poly1.intersection(poly2) == [Point2D(Rational(1, 3), 0),
Segment(Point(0, Rational(1, 5)), Point(0, 0)),
Segment(Point(1, 0), Point(0, 1))]
assert poly2.intersection(poly1) == [Point(Rational(1, 3), 0),
Segment(Point(0, 0), Point(0, Rational(1, 5))),
Segment(Point(1, 0), Point(0, 1))]
assert poly1.intersection(Point(0, 0)) == [Point(0, 0)]
assert poly1.intersection(Point(-12, -43)) == []
assert poly2.intersection(Line((-12, 0), (12, 0))) == [Point(-5, 0),
Point(0, 0), Point(Rational(1, 3), 0), Point(1, 0)]
assert poly2.intersection(Line((-12, 12), (12, 12))) == []
assert poly2.intersection(Ray((-3, 4), (1, 0))) == [Segment(Point(1, 0),
Point(0, 1))]
assert poly2.intersection(Circle((0, -1), 1)) == [Point(0, -2),
Point(0, 0)]
assert poly1.intersection(poly1) == [Segment(Point(0, 0), Point(1, 0)),
Segment(Point(0, 1), Point(0, 0)), Segment(Point(1, 0), Point(0, 1))]
assert poly2.intersection(poly2) == [Segment(Point(-5, 0), Point(0, -4)),
Segment(Point(0, -4), Point(0, Rational(1, 5))),
Segment(Point(0, Rational(1, 5)), Point(S.Half, Rational(-1, 10))),
Segment(Point(0, 1), Point(-5, 0)),
Segment(Point(S.Half, Rational(-1, 10)), Point(1, 0)),
Segment(Point(1, 0), Point(0, 1))]
assert poly2.intersection(Triangle(Point(0, 1), Point(1, 0), Point(-1, 1))) \
== [Point(Rational(-5, 7), Rational(6, 7)), Segment(Point2D(0, 1), Point(1, 0))]
assert poly1.intersection(RegularPolygon((-12, -15), 3, 3)) == []
def test_parameter_value():
t = Symbol('t')
sq = Polygon((0, 0), (0, 1), (1, 1), (1, 0))
assert sq.parameter_value((0.5, 1), t) == {t: Rational(3, 8)}
q = Polygon((0, 0), (2, 1), (2, 4), (4, 0))
assert q.parameter_value((4, 0), t) == {t: -6 + 3*sqrt(5)} # ~= 0.708
raises(ValueError, lambda: sq.parameter_value((5, 6), t))
raises(ValueError, lambda: sq.parameter_value(Circle(Point(0, 0), 1), t))
def test_issue_12966():
poly = Polygon(Point(0, 0), Point(0, 10), Point(5, 10), Point(5, 5),
Point(10, 5), Point(10, 0))
t = Symbol('t')
pt = poly.arbitrary_point(t)
DELTA = 5/poly.perimeter
assert [pt.subs(t, DELTA*i) for i in range(int(1/DELTA))] == [
Point(0, 0), Point(0, 5), Point(0, 10), Point(5, 10),
Point(5, 5), Point(10, 5), Point(10, 0), Point(5, 0)]
def test_second_moment_of_area():
x, y = symbols('x, y')
# triangle
p1, p2, p3 = [(0, 0), (4, 0), (0, 2)]
p = (0, 0)
# equation of hypotenuse
eq_y = (1-x/4)*2
I_yy = integrate((x**2) * (integrate(1, (y, 0, eq_y))), (x, 0, 4))
I_xx = integrate(1 * (integrate(y**2, (y, 0, eq_y))), (x, 0, 4))
I_xy = integrate(x * (integrate(y, (y, 0, eq_y))), (x, 0, 4))
triangle = Polygon(p1, p2, p3)
assert (I_xx - triangle.second_moment_of_area(p)[0]) == 0
assert (I_yy - triangle.second_moment_of_area(p)[1]) == 0
assert (I_xy - triangle.second_moment_of_area(p)[2]) == 0
# rectangle
p1, p2, p3, p4=[(0, 0), (4, 0), (4, 2), (0, 2)]
I_yy = integrate((x**2) * integrate(1, (y, 0, 2)), (x, 0, 4))
I_xx = integrate(1 * integrate(y**2, (y, 0, 2)), (x, 0, 4))
I_xy = integrate(x * integrate(y, (y, 0, 2)), (x, 0, 4))
rectangle = Polygon(p1, p2, p3, p4)
assert (I_xx - rectangle.second_moment_of_area(p)[0]) == 0
assert (I_yy - rectangle.second_moment_of_area(p)[1]) == 0
assert (I_xy - rectangle.second_moment_of_area(p)[2]) == 0
r = RegularPolygon(Point(0, 0), 5, 3)
assert r.second_moment_of_area() == (1875*sqrt(3)/S(32), 1875*sqrt(3)/S(32), 0)
def test_first_moment():
a, b = symbols('a, b', positive=True)
# rectangle
p1 = Polygon((0, 0), (a, 0), (a, b), (0, b))
assert p1.first_moment_of_area() == (a*b**2/8, a**2*b/8)
assert p1.first_moment_of_area((a/3, b/4)) == (-3*a*b**2/32, -a**2*b/9)
p1 = Polygon((0, 0), (40, 0), (40, 30), (0, 30))
assert p1.first_moment_of_area() == (4500, 6000)
# triangle
p2 = Polygon((0, 0), (a, 0), (a/2, b))
assert p2.first_moment_of_area() == (4*a*b**2/81, a**2*b/24)
assert p2.first_moment_of_area((a/8, b/6)) == (-25*a*b**2/648, -5*a**2*b/768)
p2 = Polygon((0, 0), (12, 0), (12, 30))
assert p2.first_moment_of_area() == (S(1600)/3, -S(640)/3)
def test_section_modulus_and_polar_second_moment_of_area():
a, b = symbols('a, b', positive=True)
x, y = symbols('x, y')
rectangle = Polygon((0, b), (0, 0), (a, 0), (a, b))
assert rectangle.section_modulus(Point(x, y)) == (a*b**3/12/(-b/2 + y), a**3*b/12/(-a/2 + x))
assert rectangle.polar_second_moment_of_area() == a**3*b/12 + a*b**3/12
convex = RegularPolygon((0, 0), 1, 6)
assert convex.section_modulus() == (Rational(5, 8), sqrt(3)*Rational(5, 16))
assert convex.polar_second_moment_of_area() == 5*sqrt(3)/S(8)
concave = Polygon((0, 0), (1, 8), (3, 4), (4, 6), (7, 1))
assert concave.section_modulus() == (Rational(-6371, 429), Rational(-9778, 519))
assert concave.polar_second_moment_of_area() == Rational(-38669, 252)
def test_cut_section():
# concave polygon
p = Polygon((-1, -1), (1, Rational(5, 2)), (2, 1), (3, Rational(5, 2)), (4, 2), (5, 3), (-1, 3))
l = Line((0, 0), (Rational(9, 2), 3))
p1 = p.cut_section(l)[0]
p2 = p.cut_section(l)[1]
assert p1 == Polygon(
Point2D(Rational(-9, 13), Rational(-6, 13)), Point2D(1, Rational(5, 2)), Point2D(Rational(24, 13), Rational(16, 13)),
Point2D(Rational(12, 5), Rational(8, 5)), Point2D(3, Rational(5, 2)), Point2D(Rational(24, 7), Rational(16, 7)),
Point2D(Rational(9, 2), 3), Point2D(-1, 3), Point2D(-1, Rational(-2, 3)))
assert p2 == Polygon(Point2D(-1, -1), Point2D(Rational(-9, 13), Rational(-6, 13)), Point2D(Rational(24, 13), Rational(16, 13)),
Point2D(2, 1), Point2D(Rational(12, 5), Rational(8, 5)), Point2D(Rational(24, 7), Rational(16, 7)), Point2D(4, 2), Point2D(5, 3),
Point2D(Rational(9, 2), 3), Point2D(-1, Rational(-2, 3)))
# convex polygon
p = RegularPolygon(Point2D(0, 0), 6, 6)
s = p.cut_section(Line((0, 0), slope=1))
assert s[0] == Polygon(Point2D(-3*sqrt(3) + 9, -3*sqrt(3) + 9), Point2D(3, 3*sqrt(3)),
Point2D(-3, 3*sqrt(3)), Point2D(-6, 0), Point2D(-9 + 3*sqrt(3), -9 + 3*sqrt(3)))
assert s[1] == Polygon(Point2D(6, 0), Point2D(-3*sqrt(3) + 9, -3*sqrt(3) + 9),
Point2D(-9 + 3*sqrt(3), -9 + 3*sqrt(3)), Point2D(-3, -3*sqrt(3)), Point2D(3, -3*sqrt(3)))
# case where line does not intersects but coincides with the edge of polygon
a, b = 20, 10
t1, t2, t3, t4 = [(0, b), (0, 0), (a, 0), (a, b)]
p = Polygon(t1, t2, t3, t4)
p1, p2 = p.cut_section(Line((0, b), slope=0))
assert p1 == None
assert p2 == Polygon(Point2D(0, 10), Point2D(0, 0), Point2D(20, 0), Point2D(20, 10))
p3, p4 = p.cut_section(Line((0, 0), slope=0))
assert p3 == Polygon(Point2D(0, 10), Point2D(0, 0), Point2D(20, 0), Point2D(20, 10))
assert p4 == None
# case where the line does not intersect with a polygon at all
raises(ValueError, lambda: p.cut_section(Line((0, a), slope=0)))
def test_type_of_triangle():
# Isoceles triangle
p1 = Polygon(Point(0, 0), Point(5, 0), Point(2, 4))
assert p1.is_isosceles() == True
assert p1.is_scalene() == False
assert p1.is_equilateral() == False
# Scalene triangle
p2 = Polygon (Point(0, 0), Point(0, 2), Point(4, 0))
assert p2.is_isosceles() == False
assert p2.is_scalene() == True
assert p2.is_equilateral() == False
# Equilateral triagle
p3 = Polygon(Point(0, 0), Point(6, 0), Point(3, sqrt(27)))
assert p3.is_isosceles() == True
assert p3.is_scalene() == False
assert p3.is_equilateral() == True
def test_do_poly_distance():
# Non-intersecting polygons
square1 = Polygon (Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0))
triangle1 = Polygon(Point(1, 2), Point(2, 2), Point(2, 1))
assert square1._do_poly_distance(triangle1) == sqrt(2)/2
# Polygons which sides intersect
square2 = Polygon(Point(1, 0), Point(2, 0), Point(2, 1), Point(1, 1))
with warns(UserWarning, \
match="Polygons may intersect producing erroneous output", test_stacklevel=False):
assert square1._do_poly_distance(square2) == 0
# Polygons which bodies intersect
triangle2 = Polygon(Point(0, -1), Point(2, -1), Point(S.Half, S.Half))
with warns(UserWarning, \
match="Polygons may intersect producing erroneous output", test_stacklevel=False):
assert triangle2._do_poly_distance(square1) == 0

View File

@ -0,0 +1,170 @@
import pytest
from sympy.core.numbers import Float
from sympy.core.function import (Derivative, Function)
from sympy.core.singleton import S
from sympy.core.symbol import Symbol
from sympy.functions import exp, cos, sin, tan, cosh, sinh
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.geometry import Point, Point2D, Line, Polygon, Segment, convex_hull,\
intersection, centroid, Point3D, Line3D, Ray, Ellipse
from sympy.geometry.util import idiff, closest_points, farthest_points, _ordered_points, are_coplanar
from sympy.solvers.solvers import solve
from sympy.testing.pytest import raises
def test_idiff():
x = Symbol('x', real=True)
y = Symbol('y', real=True)
t = Symbol('t', real=True)
f = Function('f')
g = Function('g')
# the use of idiff in ellipse also provides coverage
circ = x**2 + y**2 - 4
ans = -3*x*(x**2/y**2 + 1)/y**3
assert ans == idiff(circ, y, x, 3), idiff(circ, y, x, 3)
assert ans == idiff(circ, [y], x, 3)
assert idiff(circ, y, x, 3) == ans
explicit = 12*x/sqrt(-x**2 + 4)**5
assert ans.subs(y, solve(circ, y)[0]).equals(explicit)
assert True in [sol.diff(x, 3).equals(explicit) for sol in solve(circ, y)]
assert idiff(x + t + y, [y, t], x) == -Derivative(t, x) - 1
assert idiff(f(x) * exp(f(x)) - x * exp(x), f(x), x) == (x + 1)*exp(x)*exp(-f(x))/(f(x) + 1)
assert idiff(f(x) - y * exp(x), [f(x), y], x) == (y + Derivative(y, x))*exp(x)
assert idiff(f(x) - y * exp(x), [y, f(x)], x) == -y + Derivative(f(x), x)*exp(-x)
assert idiff(f(x) - g(x), [f(x), g(x)], x) == Derivative(g(x), x)
# this should be fast
fxy = y - (-10*(-sin(x) + 1/x)**2 + tan(x)**2 + 2*cosh(x/10))
assert idiff(fxy, y, x) == -20*sin(x)*cos(x) + 2*tan(x)**3 + \
2*tan(x) + sinh(x/10)/5 + 20*cos(x)/x - 20*sin(x)/x**2 + 20/x**3
def test_intersection():
assert intersection(Point(0, 0)) == []
raises(TypeError, lambda: intersection(Point(0, 0), 3))
assert intersection(
Segment((0, 0), (2, 0)),
Segment((-1, 0), (1, 0)),
Line((0, 0), (0, 1)), pairwise=True) == [
Point(0, 0), Segment((0, 0), (1, 0))]
assert intersection(
Line((0, 0), (0, 1)),
Segment((0, 0), (2, 0)),
Segment((-1, 0), (1, 0)), pairwise=True) == [
Point(0, 0), Segment((0, 0), (1, 0))]
assert intersection(
Line((0, 0), (0, 1)),
Segment((0, 0), (2, 0)),
Segment((-1, 0), (1, 0)),
Line((0, 0), slope=1), pairwise=True) == [
Point(0, 0), Segment((0, 0), (1, 0))]
R = 4.0
c = intersection(
Ray(Point2D(0.001, -1),
Point2D(0.0008, -1.7)),
Ellipse(center=Point2D(0, 0), hradius=R, vradius=2.0), pairwise=True)[0].coordinates
assert c == pytest.approx(
Point2D(0.000714285723396502, -1.99999996811224, evaluate=False).coordinates)
# check this is responds to a lower precision parameter
R = Float(4, 5)
c2 = intersection(
Ray(Point2D(0.001, -1),
Point2D(0.0008, -1.7)),
Ellipse(center=Point2D(0, 0), hradius=R, vradius=2.0), pairwise=True)[0].coordinates
assert c2 == pytest.approx(
Point2D(0.000714285723396502, -1.99999996811224, evaluate=False).coordinates)
assert c[0]._prec == 53
assert c2[0]._prec == 20
def test_convex_hull():
raises(TypeError, lambda: convex_hull(Point(0, 0), 3))
points = [(1, -1), (1, -2), (3, -1), (-5, -2), (15, -4)]
assert convex_hull(*points, **{"polygon": False}) == (
[Point2D(-5, -2), Point2D(1, -1), Point2D(3, -1), Point2D(15, -4)],
[Point2D(-5, -2), Point2D(15, -4)])
def test_centroid():
p = Polygon((0, 0), (10, 0), (10, 10))
q = p.translate(0, 20)
assert centroid(p, q) == Point(20, 40)/3
p = Segment((0, 0), (2, 0))
q = Segment((0, 0), (2, 2))
assert centroid(p, q) == Point(1, -sqrt(2) + 2)
assert centroid(Point(0, 0), Point(2, 0)) == Point(2, 0)/2
assert centroid(Point(0, 0), Point(0, 0), Point(2, 0)) == Point(2, 0)/3
def test_farthest_points_closest_points():
from sympy.core.random import randint
from sympy.utilities.iterables import subsets
for how in (min, max):
if how == min:
func = closest_points
else:
func = farthest_points
raises(ValueError, lambda: func(Point2D(0, 0), Point2D(0, 0)))
# 3rd pt dx is close and pt is closer to 1st pt
p1 = [Point2D(0, 0), Point2D(3, 0), Point2D(1, 1)]
# 3rd pt dx is close and pt is closer to 2nd pt
p2 = [Point2D(0, 0), Point2D(3, 0), Point2D(2, 1)]
# 3rd pt dx is close and but pt is not closer
p3 = [Point2D(0, 0), Point2D(3, 0), Point2D(1, 10)]
# 3rd pt dx is not closer and it's closer to 2nd pt
p4 = [Point2D(0, 0), Point2D(3, 0), Point2D(4, 0)]
# 3rd pt dx is not closer and it's closer to 1st pt
p5 = [Point2D(0, 0), Point2D(3, 0), Point2D(-1, 0)]
# duplicate point doesn't affect outcome
dup = [Point2D(0, 0), Point2D(3, 0), Point2D(3, 0), Point2D(-1, 0)]
# symbolic
x = Symbol('x', positive=True)
s = [Point2D(a) for a in ((x, 1), (x + 3, 2), (x + 2, 2))]
for points in (p1, p2, p3, p4, p5, dup, s):
d = how(i.distance(j) for i, j in subsets(set(points), 2))
ans = a, b = list(func(*points))[0]
assert a.distance(b) == d
assert ans == _ordered_points(ans)
# if the following ever fails, the above tests were not sufficient
# and the logical error in the routine should be fixed
points = set()
while len(points) != 7:
points.add(Point2D(randint(1, 100), randint(1, 100)))
points = list(points)
d = how(i.distance(j) for i, j in subsets(points, 2))
ans = a, b = list(func(*points))[0]
assert a.distance(b) == d
assert ans == _ordered_points(ans)
# equidistant points
a, b, c = (
Point2D(0, 0), Point2D(1, 0), Point2D(S.Half, sqrt(3)/2))
ans = {_ordered_points((i, j))
for i, j in subsets((a, b, c), 2)}
assert closest_points(b, c, a) == ans
assert farthest_points(b, c, a) == ans
# unique to farthest
points = [(1, 1), (1, 2), (3, 1), (-5, 2), (15, 4)]
assert farthest_points(*points) == {
(Point2D(-5, 2), Point2D(15, 4))}
points = [(1, -1), (1, -2), (3, -1), (-5, -2), (15, -4)]
assert farthest_points(*points) == {
(Point2D(-5, -2), Point2D(15, -4))}
assert farthest_points((1, 1), (0, 0)) == {
(Point2D(0, 0), Point2D(1, 1))}
raises(ValueError, lambda: farthest_points((1, 1)))
def test_are_coplanar():
a = Line3D(Point3D(5, 0, 0), Point3D(1, -1, 1))
b = Line3D(Point3D(0, -2, 0), Point3D(3, 1, 1))
c = Line3D(Point3D(0, -1, 0), Point3D(5, -1, 9))
d = Line(Point2D(0, 3), Point2D(1, 5))
assert are_coplanar(a, b, c) == False
assert are_coplanar(a, d) == False

View File

@ -0,0 +1,733 @@
"""Utility functions for geometrical entities.
Contains
========
intersection
convex_hull
closest_points
farthest_points
are_coplanar
are_similar
"""
from collections import deque
from math import sqrt as _sqrt
from sympy import nsimplify
from .entity import GeometryEntity
from .exceptions import GeometryError
from .point import Point, Point2D, Point3D
from sympy.core.containers import OrderedSet
from sympy.core.exprtools import factor_terms
from sympy.core.function import Function, expand_mul
from sympy.core.numbers import Float
from sympy.core.sorting import ordered
from sympy.core.symbol import Symbol
from sympy.core.singleton import S
from sympy.polys.polytools import cancel
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.utilities.iterables import is_sequence
from mpmath.libmp.libmpf import prec_to_dps
def find(x, equation):
"""
Checks whether a Symbol matching ``x`` is present in ``equation``
or not. If present, the matching symbol is returned, else a
ValueError is raised. If ``x`` is a string the matching symbol
will have the same name; if ``x`` is a Symbol then it will be
returned if found.
Examples
========
>>> from sympy.geometry.util import find
>>> from sympy import Dummy
>>> from sympy.abc import x
>>> find('x', x)
x
>>> find('x', Dummy('x'))
_x
The dummy symbol is returned since it has a matching name:
>>> _.name == 'x'
True
>>> find(x, Dummy('x'))
Traceback (most recent call last):
...
ValueError: could not find x
"""
free = equation.free_symbols
xs = [i for i in free if (i.name if isinstance(x, str) else i) == x]
if not xs:
raise ValueError('could not find %s' % x)
if len(xs) != 1:
raise ValueError('ambiguous %s' % x)
return xs[0]
def _ordered_points(p):
"""Return the tuple of points sorted numerically according to args"""
return tuple(sorted(p, key=lambda x: x.args))
def are_coplanar(*e):
""" Returns True if the given entities are coplanar otherwise False
Parameters
==========
e: entities to be checked for being coplanar
Returns
=======
Boolean
Examples
========
>>> from sympy import Point3D, Line3D
>>> from sympy.geometry.util import are_coplanar
>>> a = Line3D(Point3D(5, 0, 0), Point3D(1, -1, 1))
>>> b = Line3D(Point3D(0, -2, 0), Point3D(3, 1, 1))
>>> c = Line3D(Point3D(0, -1, 0), Point3D(5, -1, 9))
>>> are_coplanar(a, b, c)
False
"""
from .line import LinearEntity3D
from .plane import Plane
# XXX update tests for coverage
e = set(e)
# first work with a Plane if present
for i in list(e):
if isinstance(i, Plane):
e.remove(i)
return all(p.is_coplanar(i) for p in e)
if all(isinstance(i, Point3D) for i in e):
if len(e) < 3:
return False
# remove pts that are collinear with 2 pts
a, b = e.pop(), e.pop()
for i in list(e):
if Point3D.are_collinear(a, b, i):
e.remove(i)
if not e:
return False
else:
# define a plane
p = Plane(a, b, e.pop())
for i in e:
if i not in p:
return False
return True
else:
pt3d = []
for i in e:
if isinstance(i, Point3D):
pt3d.append(i)
elif isinstance(i, LinearEntity3D):
pt3d.extend(i.args)
elif isinstance(i, GeometryEntity): # XXX we should have a GeometryEntity3D class so we can tell the difference between 2D and 3D -- here we just want to deal with 2D objects; if new 3D objects are encountered that we didn't handle above, an error should be raised
# all 2D objects have some Point that defines them; so convert those points to 3D pts by making z=0
for p in i.args:
if isinstance(p, Point):
pt3d.append(Point3D(*(p.args + (0,))))
return are_coplanar(*pt3d)
def are_similar(e1, e2):
"""Are two geometrical entities similar.
Can one geometrical entity be uniformly scaled to the other?
Parameters
==========
e1 : GeometryEntity
e2 : GeometryEntity
Returns
=======
are_similar : boolean
Raises
======
GeometryError
When `e1` and `e2` cannot be compared.
Notes
=====
If the two objects are equal then they are similar.
See Also
========
sympy.geometry.entity.GeometryEntity.is_similar
Examples
========
>>> from sympy import Point, Circle, Triangle, are_similar
>>> c1, c2 = Circle(Point(0, 0), 4), Circle(Point(1, 4), 3)
>>> t1 = Triangle(Point(0, 0), Point(1, 0), Point(0, 1))
>>> t2 = Triangle(Point(0, 0), Point(2, 0), Point(0, 2))
>>> t3 = Triangle(Point(0, 0), Point(3, 0), Point(0, 1))
>>> are_similar(t1, t2)
True
>>> are_similar(t1, t3)
False
"""
if e1 == e2:
return True
is_similar1 = getattr(e1, 'is_similar', None)
if is_similar1:
return is_similar1(e2)
is_similar2 = getattr(e2, 'is_similar', None)
if is_similar2:
return is_similar2(e1)
n1 = e1.__class__.__name__
n2 = e2.__class__.__name__
raise GeometryError(
"Cannot test similarity between %s and %s" % (n1, n2))
def centroid(*args):
"""Find the centroid (center of mass) of the collection containing only Points,
Segments or Polygons. The centroid is the weighted average of the individual centroid
where the weights are the lengths (of segments) or areas (of polygons).
Overlapping regions will add to the weight of that region.
If there are no objects (or a mixture of objects) then None is returned.
See Also
========
sympy.geometry.point.Point, sympy.geometry.line.Segment,
sympy.geometry.polygon.Polygon
Examples
========
>>> from sympy import Point, Segment, Polygon
>>> from sympy.geometry.util import centroid
>>> p = Polygon((0, 0), (10, 0), (10, 10))
>>> q = p.translate(0, 20)
>>> p.centroid, q.centroid
(Point2D(20/3, 10/3), Point2D(20/3, 70/3))
>>> centroid(p, q)
Point2D(20/3, 40/3)
>>> p, q = Segment((0, 0), (2, 0)), Segment((0, 0), (2, 2))
>>> centroid(p, q)
Point2D(1, 2 - sqrt(2))
>>> centroid(Point(0, 0), Point(2, 0))
Point2D(1, 0)
Stacking 3 polygons on top of each other effectively triples the
weight of that polygon:
>>> p = Polygon((0, 0), (1, 0), (1, 1), (0, 1))
>>> q = Polygon((1, 0), (3, 0), (3, 1), (1, 1))
>>> centroid(p, q)
Point2D(3/2, 1/2)
>>> centroid(p, p, p, q) # centroid x-coord shifts left
Point2D(11/10, 1/2)
Stacking the squares vertically above and below p has the same
effect:
>>> centroid(p, p.translate(0, 1), p.translate(0, -1), q)
Point2D(11/10, 1/2)
"""
from .line import Segment
from .polygon import Polygon
if args:
if all(isinstance(g, Point) for g in args):
c = Point(0, 0)
for g in args:
c += g
den = len(args)
elif all(isinstance(g, Segment) for g in args):
c = Point(0, 0)
L = 0
for g in args:
l = g.length
c += g.midpoint*l
L += l
den = L
elif all(isinstance(g, Polygon) for g in args):
c = Point(0, 0)
A = 0
for g in args:
a = g.area
c += g.centroid*a
A += a
den = A
c /= den
return c.func(*[i.simplify() for i in c.args])
def closest_points(*args):
"""Return the subset of points from a set of points that were
the closest to each other in the 2D plane.
Parameters
==========
args
A collection of Points on 2D plane.
Notes
=====
This can only be performed on a set of points whose coordinates can
be ordered on the number line. If there are no ties then a single
pair of Points will be in the set.
Examples
========
>>> from sympy import closest_points, Triangle
>>> Triangle(sss=(3, 4, 5)).args
(Point2D(0, 0), Point2D(3, 0), Point2D(3, 4))
>>> closest_points(*_)
{(Point2D(0, 0), Point2D(3, 0))}
References
==========
.. [1] https://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairPS.html
.. [2] Sweep line algorithm
https://en.wikipedia.org/wiki/Sweep_line_algorithm
"""
p = [Point2D(i) for i in set(args)]
if len(p) < 2:
raise ValueError('At least 2 distinct points must be given.')
try:
p.sort(key=lambda x: x.args)
except TypeError:
raise ValueError("The points could not be sorted.")
if not all(i.is_Rational for j in p for i in j.args):
def hypot(x, y):
arg = x*x + y*y
if arg.is_Rational:
return _sqrt(arg)
return sqrt(arg)
else:
from math import hypot
rv = [(0, 1)]
best_dist = hypot(p[1].x - p[0].x, p[1].y - p[0].y)
i = 2
left = 0
box = deque([0, 1])
while i < len(p):
while left < i and p[i][0] - p[left][0] > best_dist:
box.popleft()
left += 1
for j in box:
d = hypot(p[i].x - p[j].x, p[i].y - p[j].y)
if d < best_dist:
rv = [(j, i)]
elif d == best_dist:
rv.append((j, i))
else:
continue
best_dist = d
box.append(i)
i += 1
return {tuple([p[i] for i in pair]) for pair in rv}
def convex_hull(*args, polygon=True):
"""The convex hull surrounding the Points contained in the list of entities.
Parameters
==========
args : a collection of Points, Segments and/or Polygons
Optional parameters
===================
polygon : Boolean. If True, returns a Polygon, if false a tuple, see below.
Default is True.
Returns
=======
convex_hull : Polygon if ``polygon`` is True else as a tuple `(U, L)` where
``L`` and ``U`` are the lower and upper hulls, respectively.
Notes
=====
This can only be performed on a set of points whose coordinates can
be ordered on the number line.
See Also
========
sympy.geometry.point.Point, sympy.geometry.polygon.Polygon
Examples
========
>>> from sympy import convex_hull
>>> points = [(1, 1), (1, 2), (3, 1), (-5, 2), (15, 4)]
>>> convex_hull(*points)
Polygon(Point2D(-5, 2), Point2D(1, 1), Point2D(3, 1), Point2D(15, 4))
>>> convex_hull(*points, **dict(polygon=False))
([Point2D(-5, 2), Point2D(15, 4)],
[Point2D(-5, 2), Point2D(1, 1), Point2D(3, 1), Point2D(15, 4)])
References
==========
.. [1] https://en.wikipedia.org/wiki/Graham_scan
.. [2] Andrew's Monotone Chain Algorithm
(A.M. Andrew,
"Another Efficient Algorithm for Convex Hulls in Two Dimensions", 1979)
https://web.archive.org/web/20210511015444/http://geomalgorithms.com/a10-_hull-1.html
"""
from .line import Segment
from .polygon import Polygon
p = OrderedSet()
for e in args:
if not isinstance(e, GeometryEntity):
try:
e = Point(e)
except NotImplementedError:
raise ValueError('%s is not a GeometryEntity and cannot be made into Point' % str(e))
if isinstance(e, Point):
p.add(e)
elif isinstance(e, Segment):
p.update(e.points)
elif isinstance(e, Polygon):
p.update(e.vertices)
else:
raise NotImplementedError(
'Convex hull for %s not implemented.' % type(e))
# make sure all our points are of the same dimension
if any(len(x) != 2 for x in p):
raise ValueError('Can only compute the convex hull in two dimensions')
p = list(p)
if len(p) == 1:
return p[0] if polygon else (p[0], None)
elif len(p) == 2:
s = Segment(p[0], p[1])
return s if polygon else (s, None)
def _orientation(p, q, r):
'''Return positive if p-q-r are clockwise, neg if ccw, zero if
collinear.'''
return (q.y - p.y)*(r.x - p.x) - (q.x - p.x)*(r.y - p.y)
# scan to find upper and lower convex hulls of a set of 2d points.
U = []
L = []
try:
p.sort(key=lambda x: x.args)
except TypeError:
raise ValueError("The points could not be sorted.")
for p_i in p:
while len(U) > 1 and _orientation(U[-2], U[-1], p_i) <= 0:
U.pop()
while len(L) > 1 and _orientation(L[-2], L[-1], p_i) >= 0:
L.pop()
U.append(p_i)
L.append(p_i)
U.reverse()
convexHull = tuple(L + U[1:-1])
if len(convexHull) == 2:
s = Segment(convexHull[0], convexHull[1])
return s if polygon else (s, None)
if polygon:
return Polygon(*convexHull)
else:
U.reverse()
return (U, L)
def farthest_points(*args):
"""Return the subset of points from a set of points that were
the furthest apart from each other in the 2D plane.
Parameters
==========
args
A collection of Points on 2D plane.
Notes
=====
This can only be performed on a set of points whose coordinates can
be ordered on the number line. If there are no ties then a single
pair of Points will be in the set.
Examples
========
>>> from sympy.geometry import farthest_points, Triangle
>>> Triangle(sss=(3, 4, 5)).args
(Point2D(0, 0), Point2D(3, 0), Point2D(3, 4))
>>> farthest_points(*_)
{(Point2D(0, 0), Point2D(3, 4))}
References
==========
.. [1] https://code.activestate.com/recipes/117225-convex-hull-and-diameter-of-2d-point-sets/
.. [2] Rotating Callipers Technique
https://en.wikipedia.org/wiki/Rotating_calipers
"""
def rotatingCalipers(Points):
U, L = convex_hull(*Points, **{"polygon": False})
if L is None:
if isinstance(U, Point):
raise ValueError('At least two distinct points must be given.')
yield U.args
else:
i = 0
j = len(L) - 1
while i < len(U) - 1 or j > 0:
yield U[i], L[j]
# if all the way through one side of hull, advance the other side
if i == len(U) - 1:
j -= 1
elif j == 0:
i += 1
# still points left on both lists, compare slopes of next hull edges
# being careful to avoid divide-by-zero in slope calculation
elif (U[i+1].y - U[i].y) * (L[j].x - L[j-1].x) > \
(L[j].y - L[j-1].y) * (U[i+1].x - U[i].x):
i += 1
else:
j -= 1
p = [Point2D(i) for i in set(args)]
if not all(i.is_Rational for j in p for i in j.args):
def hypot(x, y):
arg = x*x + y*y
if arg.is_Rational:
return _sqrt(arg)
return sqrt(arg)
else:
from math import hypot
rv = []
diam = 0
for pair in rotatingCalipers(args):
h, q = _ordered_points(pair)
d = hypot(h.x - q.x, h.y - q.y)
if d > diam:
rv = [(h, q)]
elif d == diam:
rv.append((h, q))
else:
continue
diam = d
return set(rv)
def idiff(eq, y, x, n=1):
"""Return ``dy/dx`` assuming that ``eq == 0``.
Parameters
==========
y : the dependent variable or a list of dependent variables (with y first)
x : the variable that the derivative is being taken with respect to
n : the order of the derivative (default is 1)
Examples
========
>>> from sympy.abc import x, y, a
>>> from sympy.geometry.util import idiff
>>> circ = x**2 + y**2 - 4
>>> idiff(circ, y, x)
-x/y
>>> idiff(circ, y, x, 2).simplify()
(-x**2 - y**2)/y**3
Here, ``a`` is assumed to be independent of ``x``:
>>> idiff(x + a + y, y, x)
-1
Now the x-dependence of ``a`` is made explicit by listing ``a`` after
``y`` in a list.
>>> idiff(x + a + y, [y, a], x)
-Derivative(a, x) - 1
See Also
========
sympy.core.function.Derivative: represents unevaluated derivatives
sympy.core.function.diff: explicitly differentiates wrt symbols
"""
if is_sequence(y):
dep = set(y)
y = y[0]
elif isinstance(y, Symbol):
dep = {y}
elif isinstance(y, Function):
pass
else:
raise ValueError("expecting x-dependent symbol(s) or function(s) but got: %s" % y)
f = {s: Function(s.name)(x) for s in eq.free_symbols
if s != x and s in dep}
if isinstance(y, Symbol):
dydx = Function(y.name)(x).diff(x)
else:
dydx = y.diff(x)
eq = eq.subs(f)
derivs = {}
for i in range(n):
# equation will be linear in dydx, a*dydx + b, so dydx = -b/a
deq = eq.diff(x)
b = deq.xreplace({dydx: S.Zero})
a = (deq - b).xreplace({dydx: S.One})
yp = factor_terms(expand_mul(cancel((-b/a).subs(derivs)), deep=False))
if i == n - 1:
return yp.subs([(v, k) for k, v in f.items()])
derivs[dydx] = yp
eq = dydx - yp
dydx = dydx.diff(x)
def intersection(*entities, pairwise=False, **kwargs):
"""The intersection of a collection of GeometryEntity instances.
Parameters
==========
entities : sequence of GeometryEntity
pairwise (keyword argument) : Can be either True or False
Returns
=======
intersection : list of GeometryEntity
Raises
======
NotImplementedError
When unable to calculate intersection.
Notes
=====
The intersection of any geometrical entity with itself should return
a list with one item: the entity in question.
An intersection requires two or more entities. If only a single
entity is given then the function will return an empty list.
It is possible for `intersection` to miss intersections that one
knows exists because the required quantities were not fully
simplified internally.
Reals should be converted to Rationals, e.g. Rational(str(real_num))
or else failures due to floating point issues may result.
Case 1: When the keyword argument 'pairwise' is False (default value):
In this case, the function returns a list of intersections common to
all entities.
Case 2: When the keyword argument 'pairwise' is True:
In this case, the functions returns a list intersections that occur
between any pair of entities.
See Also
========
sympy.geometry.entity.GeometryEntity.intersection
Examples
========
>>> from sympy import Ray, Circle, intersection
>>> c = Circle((0, 1), 1)
>>> intersection(c, c.center)
[]
>>> right = Ray((0, 0), (1, 0))
>>> up = Ray((0, 0), (0, 1))
>>> intersection(c, right, up)
[Point2D(0, 0)]
>>> intersection(c, right, up, pairwise=True)
[Point2D(0, 0), Point2D(0, 2)]
>>> left = Ray((1, 0), (0, 0))
>>> intersection(right, left)
[Segment2D(Point2D(0, 0), Point2D(1, 0))]
"""
if len(entities) <= 1:
return []
entities = list(entities)
prec = None
for i, e in enumerate(entities):
if not isinstance(e, GeometryEntity):
# entities may be an immutable tuple
e = Point(e)
# convert to exact Rationals
d = {}
for f in e.atoms(Float):
prec = f._prec if prec is None else min(f._prec, prec)
d.setdefault(f, nsimplify(f, rational=True))
entities[i] = e.xreplace(d)
if not pairwise:
# find the intersection common to all objects
res = entities[0].intersection(entities[1])
for entity in entities[2:]:
newres = []
for x in res:
newres.extend(x.intersection(entity))
res = newres
else:
# find all pairwise intersections
ans = []
for j in range(len(entities)):
for k in range(j + 1, len(entities)):
ans.extend(intersection(entities[j], entities[k]))
res = list(ordered(set(ans)))
# convert back to Floats
if prec is not None:
p = prec_to_dps(prec)
res = [i.n(p) for i in res]
return res