add read me
This commit is contained in:
45
venv/lib/python3.12/site-packages/sympy/geometry/__init__.py
Normal file
45
venv/lib/python3.12/site-packages/sympy/geometry/__init__.py
Normal 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',
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
424
venv/lib/python3.12/site-packages/sympy/geometry/curve.py
Normal file
424
venv/lib/python3.12/site-packages/sympy/geometry/curve.py
Normal 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)
|
||||
1768
venv/lib/python3.12/site-packages/sympy/geometry/ellipse.py
Normal file
1768
venv/lib/python3.12/site-packages/sympy/geometry/ellipse.py
Normal file
File diff suppressed because it is too large
Load Diff
641
venv/lib/python3.12/site-packages/sympy/geometry/entity.py
Normal file
641
venv/lib/python3.12/site-packages/sympy/geometry/entity.py
Normal 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
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Geometry Errors."""
|
||||
|
||||
class GeometryError(ValueError):
|
||||
"""An exception raised by classes in the geometry module."""
|
||||
pass
|
||||
2877
venv/lib/python3.12/site-packages/sympy/geometry/line.py
Normal file
2877
venv/lib/python3.12/site-packages/sympy/geometry/line.py
Normal file
File diff suppressed because it is too large
Load Diff
422
venv/lib/python3.12/site-packages/sympy/geometry/parabola.py
Normal file
422
venv/lib/python3.12/site-packages/sympy/geometry/parabola.py
Normal 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.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
|
||||
878
venv/lib/python3.12/site-packages/sympy/geometry/plane.py
Normal file
878
venv/lib/python3.12/site-packages/sympy/geometry/plane.py
Normal file
@@ -0,0 +1,878 @@
|
||||
"""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
|
||||
return sum(i*j for i, j in zip(a, b)) == 0
|
||||
if isinstance(l, Plane):
|
||||
a = Matrix(l.normal_vector)
|
||||
b = Matrix(self.normal_vector)
|
||||
return bool(a.cross(b).is_zero_matrix)
|
||||
|
||||
|
||||
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
|
||||
1378
venv/lib/python3.12/site-packages/sympy/geometry/point.py
Normal file
1378
venv/lib/python3.12/site-packages/sympy/geometry/point.py
Normal file
File diff suppressed because it is too large
Load Diff
2891
venv/lib/python3.12/site-packages/sympy/geometry/polygon.py
Normal file
2891
venv/lib/python3.12/site-packages/sympy/geometry/polygon.py
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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)
|
||||
@@ -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"/>'
|
||||
@@ -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(TypeError, 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))
|
||||
@@ -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)))
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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))
|
||||
@@ -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, 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]
|
||||
@@ -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 triangle
|
||||
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
|
||||
@@ -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
|
||||
731
venv/lib/python3.12/site-packages/sympy/geometry/util.py
Normal file
731
venv/lib/python3.12/site-packages/sympy/geometry/util.py
Normal file
@@ -0,0 +1,731 @@
|
||||
"""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)
|
||||
left = 0
|
||||
box = deque([0, 1])
|
||||
for i in range(2, 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)
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user