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