Buckets:
MisterAI/LocalAI_Demo_backends / cpu-diffusers.upgrade-tmp /venv /lib /python3.10 /site-packages /sympy /geometry /line.py
| """Line-like geometrical entities. | |
| Contains | |
| ======== | |
| LinearEntity | |
| Line | |
| Ray | |
| Segment | |
| LinearEntity2D | |
| Line2D | |
| Ray2D | |
| Segment2D | |
| LinearEntity3D | |
| Line3D | |
| Ray3D | |
| Segment3D | |
| """ | |
| from sympy.core.containers import Tuple | |
| from sympy.core.evalf import N | |
| from sympy.core.expr import Expr | |
| from sympy.core.numbers import Rational, oo, Float | |
| from sympy.core.relational import Eq | |
| from sympy.core.singleton import S | |
| from sympy.core.sorting import ordered | |
| from sympy.core.symbol import _symbol, Dummy, uniquely_named_symbol | |
| from sympy.core.sympify import sympify | |
| from sympy.functions.elementary.piecewise import Piecewise | |
| from sympy.functions.elementary.trigonometric import (_pi_coeff, acos, tan, atan2) | |
| from .entity import GeometryEntity, GeometrySet | |
| from .exceptions import GeometryError | |
| from .point import Point, Point3D | |
| from .util import find, intersection | |
| from sympy.logic.boolalg import And | |
| from sympy.matrices import Matrix | |
| from sympy.sets.sets import Intersection | |
| from sympy.simplify.simplify import simplify | |
| from sympy.solvers.solvers import solve | |
| from sympy.solvers.solveset import linear_coeffs | |
| from sympy.utilities.misc import Undecidable, filldedent | |
| import random | |
| t, u = [Dummy('line_dummy') for i in range(2)] | |
| class LinearEntity(GeometrySet): | |
| """A base class for all linear entities (Line, Ray and Segment) | |
| in n-dimensional Euclidean space. | |
| Attributes | |
| ========== | |
| ambient_dimension | |
| direction | |
| length | |
| p1 | |
| p2 | |
| points | |
| Notes | |
| ===== | |
| This is an abstract class and is not meant to be instantiated. | |
| See Also | |
| ======== | |
| sympy.geometry.entity.GeometryEntity | |
| """ | |
| def __new__(cls, p1, p2=None, **kwargs): | |
| p1, p2 = Point._normalize_dimension(p1, p2) | |
| if p1 == p2: | |
| # sometimes we return a single point if we are not given two unique | |
| # points. This is done in the specific subclass | |
| raise ValueError( | |
| "%s.__new__ requires two unique Points." % cls.__name__) | |
| if len(p1) != len(p2): | |
| raise ValueError( | |
| "%s.__new__ requires two Points of equal dimension." % cls.__name__) | |
| return GeometryEntity.__new__(cls, p1, p2, **kwargs) | |
| def __contains__(self, other): | |
| """Return a definitive answer or else raise an error if it cannot | |
| be determined that other is on the boundaries of self.""" | |
| result = self.contains(other) | |
| if result is not None: | |
| return result | |
| else: | |
| raise Undecidable( | |
| "Cannot decide whether '%s' contains '%s'" % (self, other)) | |
| def _span_test(self, other): | |
| """Test whether the point `other` lies in the positive span of `self`. | |
| A point x is 'in front' of a point y if x.dot(y) >= 0. Return | |
| -1 if `other` is behind `self.p1`, 0 if `other` is `self.p1` and | |
| and 1 if `other` is in front of `self.p1`.""" | |
| if self.p1 == other: | |
| return 0 | |
| rel_pos = other - self.p1 | |
| d = self.direction | |
| if d.dot(rel_pos) > 0: | |
| return 1 | |
| return -1 | |
| def ambient_dimension(self): | |
| """A property method that returns the dimension of LinearEntity | |
| object. | |
| Parameters | |
| ========== | |
| p1 : LinearEntity | |
| Returns | |
| ======= | |
| dimension : integer | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(1, 1) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.ambient_dimension | |
| 2 | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0, 0), Point(1, 1, 1) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.ambient_dimension | |
| 3 | |
| """ | |
| return len(self.p1) | |
| def angle_between(l1, l2): | |
| """Return the non-reflex angle formed by rays emanating from | |
| the origin with directions the same as the direction vectors | |
| of the linear entities. | |
| Parameters | |
| ========== | |
| l1 : LinearEntity | |
| l2 : LinearEntity | |
| Returns | |
| ======= | |
| angle : angle in radians | |
| Notes | |
| ===== | |
| From the dot product of vectors v1 and v2 it is known that: | |
| ``dot(v1, v2) = |v1|*|v2|*cos(A)`` | |
| where A is the angle formed between the two vectors. We can | |
| get the directional vectors of the two lines and readily | |
| find the angle between the two using the above formula. | |
| See Also | |
| ======== | |
| is_perpendicular, Ray2D.closing_angle | |
| Examples | |
| ======== | |
| >>> from sympy import Line | |
| >>> e = Line((0, 0), (1, 0)) | |
| >>> ne = Line((0, 0), (1, 1)) | |
| >>> sw = Line((1, 1), (0, 0)) | |
| >>> ne.angle_between(e) | |
| pi/4 | |
| >>> sw.angle_between(e) | |
| 3*pi/4 | |
| To obtain the non-obtuse angle at the intersection of lines, use | |
| the ``smallest_angle_between`` method: | |
| >>> sw.smallest_angle_between(e) | |
| pi/4 | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(-1, 2, 0) | |
| >>> l1, l2 = Line3D(p1, p2), Line3D(p2, p3) | |
| >>> l1.angle_between(l2) | |
| acos(-sqrt(2)/3) | |
| >>> l1.smallest_angle_between(l2) | |
| acos(sqrt(2)/3) | |
| """ | |
| if not isinstance(l1, LinearEntity) and not isinstance(l2, LinearEntity): | |
| raise TypeError('Must pass only LinearEntity objects') | |
| v1, v2 = l1.direction, l2.direction | |
| return acos(v1.dot(v2)/(abs(v1)*abs(v2))) | |
| def smallest_angle_between(l1, l2): | |
| """Return the smallest angle formed at the intersection of the | |
| lines containing the linear entities. | |
| Parameters | |
| ========== | |
| l1 : LinearEntity | |
| l2 : LinearEntity | |
| Returns | |
| ======= | |
| angle : angle in radians | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2, p3 = Point(0, 0), Point(0, 4), Point(2, -2) | |
| >>> l1, l2 = Line(p1, p2), Line(p1, p3) | |
| >>> l1.smallest_angle_between(l2) | |
| pi/4 | |
| See Also | |
| ======== | |
| angle_between, is_perpendicular, Ray2D.closing_angle | |
| """ | |
| if not isinstance(l1, LinearEntity) and not isinstance(l2, LinearEntity): | |
| raise TypeError('Must pass only LinearEntity objects') | |
| v1, v2 = l1.direction, l2.direction | |
| return acos(abs(v1.dot(v2))/(abs(v1)*abs(v2))) | |
| def arbitrary_point(self, parameter='t'): | |
| """A parameterized point on the Line. | |
| Parameters | |
| ========== | |
| parameter : str, optional | |
| The name of the parameter which will be used for the parametric | |
| point. The default value is 't'. When this parameter is 0, the | |
| first point used to define the line will be returned, and when | |
| it is 1 the second point will be returned. | |
| Returns | |
| ======= | |
| point : Point | |
| Raises | |
| ====== | |
| ValueError | |
| When ``parameter`` already appears in the Line's definition. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(1, 0), Point(5, 3) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.arbitrary_point() | |
| Point2D(4*t + 1, 3*t) | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2 = Point3D(1, 0, 0), Point3D(5, 3, 1) | |
| >>> l1 = Line3D(p1, p2) | |
| >>> l1.arbitrary_point() | |
| Point3D(4*t + 1, 3*t, t) | |
| """ | |
| t = _symbol(parameter, real=True) | |
| if t.name in (f.name for f in self.free_symbols): | |
| raise ValueError(filldedent(''' | |
| Symbol %s already appears in object | |
| and cannot be used as a parameter. | |
| ''' % t.name)) | |
| # multiply on the right so the variable gets | |
| # combined with the coordinates of the point | |
| return self.p1 + (self.p2 - self.p1)*t | |
| def are_concurrent(*lines): | |
| """Is a sequence of linear entities concurrent? | |
| Two or more linear entities are concurrent if they all | |
| intersect at a single point. | |
| Parameters | |
| ========== | |
| lines | |
| A sequence of linear entities. | |
| Returns | |
| ======= | |
| True : if the set of linear entities intersect in one point | |
| False : otherwise. | |
| See Also | |
| ======== | |
| sympy.geometry.util.intersection | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(3, 5) | |
| >>> p3, p4 = Point(-2, -2), Point(0, 2) | |
| >>> l1, l2, l3 = Line(p1, p2), Line(p1, p3), Line(p1, p4) | |
| >>> Line.are_concurrent(l1, l2, l3) | |
| True | |
| >>> l4 = Line(p2, p3) | |
| >>> Line.are_concurrent(l2, l3, l4) | |
| False | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2 = Point3D(0, 0, 0), Point3D(3, 5, 2) | |
| >>> p3, p4 = Point3D(-2, -2, -2), Point3D(0, 2, 1) | |
| >>> l1, l2, l3 = Line3D(p1, p2), Line3D(p1, p3), Line3D(p1, p4) | |
| >>> Line3D.are_concurrent(l1, l2, l3) | |
| True | |
| >>> l4 = Line3D(p2, p3) | |
| >>> Line3D.are_concurrent(l2, l3, l4) | |
| False | |
| """ | |
| common_points = Intersection(*lines) | |
| if common_points.is_FiniteSet and len(common_points) == 1: | |
| return True | |
| return False | |
| def contains(self, other): | |
| """Subclasses should implement this method and should return | |
| True if other is on the boundaries of self; | |
| False if not on the boundaries of self; | |
| None if a determination cannot be made.""" | |
| raise NotImplementedError() | |
| def direction(self): | |
| """The direction vector of the LinearEntity. | |
| Returns | |
| ======= | |
| p : a Point; the ray from the origin to this point is the | |
| direction of `self` | |
| Examples | |
| ======== | |
| >>> from sympy import Line | |
| >>> a, b = (1, 1), (1, 3) | |
| >>> Line(a, b).direction | |
| Point2D(0, 2) | |
| >>> Line(b, a).direction | |
| Point2D(0, -2) | |
| This can be reported so the distance from the origin is 1: | |
| >>> Line(b, a).direction.unit | |
| Point2D(0, -1) | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point.unit | |
| """ | |
| return self.p2 - self.p1 | |
| def intersection(self, other): | |
| """The intersection with another geometrical entity. | |
| Parameters | |
| ========== | |
| o : Point or LinearEntity | |
| Returns | |
| ======= | |
| intersection : list of geometrical entities | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line, Segment | |
| >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(7, 7) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.intersection(p3) | |
| [Point2D(7, 7)] | |
| >>> p4, p5 = Point(5, 0), Point(0, 3) | |
| >>> l2 = Line(p4, p5) | |
| >>> l1.intersection(l2) | |
| [Point2D(15/8, 15/8)] | |
| >>> p6, p7 = Point(0, 5), Point(2, 6) | |
| >>> s1 = Segment(p6, p7) | |
| >>> l1.intersection(s1) | |
| [] | |
| >>> from sympy import Point3D, Line3D, Segment3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(7, 7, 7) | |
| >>> l1 = Line3D(p1, p2) | |
| >>> l1.intersection(p3) | |
| [Point3D(7, 7, 7)] | |
| >>> l1 = Line3D(Point3D(4,19,12), Point3D(5,25,17)) | |
| >>> l2 = Line3D(Point3D(-3, -15, -19), direction_ratio=[2,8,8]) | |
| >>> l1.intersection(l2) | |
| [Point3D(1, 1, -3)] | |
| >>> p6, p7 = Point3D(0, 5, 2), Point3D(2, 6, 3) | |
| >>> s1 = Segment3D(p6, p7) | |
| >>> l1.intersection(s1) | |
| [] | |
| """ | |
| def intersect_parallel_rays(ray1, ray2): | |
| if ray1.direction.dot(ray2.direction) > 0: | |
| # rays point in the same direction | |
| # so return the one that is "in front" | |
| return [ray2] if ray1._span_test(ray2.p1) >= 0 else [ray1] | |
| else: | |
| # rays point in opposite directions | |
| st = ray1._span_test(ray2.p1) | |
| if st < 0: | |
| return [] | |
| elif st == 0: | |
| return [ray2.p1] | |
| return [Segment(ray1.p1, ray2.p1)] | |
| def intersect_parallel_ray_and_segment(ray, seg): | |
| st1, st2 = ray._span_test(seg.p1), ray._span_test(seg.p2) | |
| if st1 < 0 and st2 < 0: | |
| return [] | |
| elif st1 >= 0 and st2 >= 0: | |
| return [seg] | |
| elif st1 >= 0: # st2 < 0: | |
| return [Segment(ray.p1, seg.p1)] | |
| else: # st1 < 0 and st2 >= 0: | |
| return [Segment(ray.p1, seg.p2)] | |
| def intersect_parallel_segments(seg1, seg2): | |
| if seg1.contains(seg2): | |
| return [seg2] | |
| if seg2.contains(seg1): | |
| return [seg1] | |
| # direct the segments so they're oriented the same way | |
| if seg1.direction.dot(seg2.direction) < 0: | |
| seg2 = Segment(seg2.p2, seg2.p1) | |
| # order the segments so seg1 is "behind" seg2 | |
| if seg1._span_test(seg2.p1) < 0: | |
| seg1, seg2 = seg2, seg1 | |
| if seg2._span_test(seg1.p2) < 0: | |
| return [] | |
| return [Segment(seg2.p1, seg1.p2)] | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| if other.is_Point: | |
| if self.contains(other): | |
| return [other] | |
| else: | |
| return [] | |
| elif isinstance(other, LinearEntity): | |
| # break into cases based on whether | |
| # the lines are parallel, non-parallel intersecting, or skew | |
| pts = Point._normalize_dimension(self.p1, self.p2, other.p1, other.p2) | |
| rank = Point.affine_rank(*pts) | |
| if rank == 1: | |
| # we're collinear | |
| if isinstance(self, Line): | |
| return [other] | |
| if isinstance(other, Line): | |
| return [self] | |
| if isinstance(self, Ray) and isinstance(other, Ray): | |
| return intersect_parallel_rays(self, other) | |
| if isinstance(self, Ray) and isinstance(other, Segment): | |
| return intersect_parallel_ray_and_segment(self, other) | |
| if isinstance(self, Segment) and isinstance(other, Ray): | |
| return intersect_parallel_ray_and_segment(other, self) | |
| if isinstance(self, Segment) and isinstance(other, Segment): | |
| return intersect_parallel_segments(self, other) | |
| elif rank == 2: | |
| # we're in the same plane | |
| l1 = Line(*pts[:2]) | |
| l2 = Line(*pts[2:]) | |
| # check to see if we're parallel. If we are, we can't | |
| # be intersecting, since the collinear case was already | |
| # handled | |
| if l1.direction.is_scalar_multiple(l2.direction): | |
| return [] | |
| # find the intersection as if everything were lines | |
| # by solving the equation t*d + p1 == s*d' + p1' | |
| m = Matrix([l1.direction, -l2.direction]).transpose() | |
| v = Matrix([l2.p1 - l1.p1]).transpose() | |
| # we cannot use m.solve(v) because that only works for square matrices | |
| m_rref, pivots = m.col_insert(2, v).rref(simplify=True) | |
| # rank == 2 ensures we have 2 pivots, but let's check anyway | |
| if len(pivots) != 2: | |
| raise GeometryError("Failed when solving Mx=b when M={} and b={}".format(m, v)) | |
| coeff = m_rref[0, 2] | |
| line_intersection = l1.direction*coeff + self.p1 | |
| # if both are lines, skip a containment check | |
| if isinstance(self, Line) and isinstance(other, Line): | |
| return [line_intersection] | |
| if ((isinstance(self, Line) or | |
| self.contains(line_intersection)) and | |
| other.contains(line_intersection)): | |
| return [line_intersection] | |
| if not self.atoms(Float) and not other.atoms(Float): | |
| # if it can fail when there are no Floats then | |
| # maybe the following parametric check should be | |
| # done | |
| return [] | |
| # floats may fail exact containment so check that the | |
| # arbitrary points, when equal, both give a | |
| # non-negative parameter when the arbitrary point | |
| # coordinates are equated | |
| tu = solve(self.arbitrary_point(t) - other.arbitrary_point(u), | |
| t, u, dict=True)[0] | |
| def ok(p, l): | |
| if isinstance(l, Line): | |
| # p > -oo | |
| return True | |
| if isinstance(l, Ray): | |
| # p >= 0 | |
| return p.is_nonnegative | |
| if isinstance(l, Segment): | |
| # 0 <= p <= 1 | |
| return p.is_nonnegative and (1 - p).is_nonnegative | |
| raise ValueError("unexpected line type") | |
| if ok(tu[t], self) and ok(tu[u], other): | |
| return [line_intersection] | |
| return [] | |
| else: | |
| # we're skew | |
| return [] | |
| return other.intersection(self) | |
| def is_parallel(l1, l2): | |
| """Are two linear entities parallel? | |
| Parameters | |
| ========== | |
| l1 : LinearEntity | |
| l2 : LinearEntity | |
| Returns | |
| ======= | |
| True : if l1 and l2 are parallel, | |
| False : otherwise. | |
| See Also | |
| ======== | |
| coefficients | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(1, 1) | |
| >>> p3, p4 = Point(3, 4), Point(6, 7) | |
| >>> l1, l2 = Line(p1, p2), Line(p3, p4) | |
| >>> Line.is_parallel(l1, l2) | |
| True | |
| >>> p5 = Point(6, 6) | |
| >>> l3 = Line(p3, p5) | |
| >>> Line.is_parallel(l1, l3) | |
| False | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2 = Point3D(0, 0, 0), Point3D(3, 4, 5) | |
| >>> p3, p4 = Point3D(2, 1, 1), Point3D(8, 9, 11) | |
| >>> l1, l2 = Line3D(p1, p2), Line3D(p3, p4) | |
| >>> Line3D.is_parallel(l1, l2) | |
| True | |
| >>> p5 = Point3D(6, 6, 6) | |
| >>> l3 = Line3D(p3, p5) | |
| >>> Line3D.is_parallel(l1, l3) | |
| False | |
| """ | |
| if not isinstance(l1, LinearEntity) and not isinstance(l2, LinearEntity): | |
| raise TypeError('Must pass only LinearEntity objects') | |
| return l1.direction.is_scalar_multiple(l2.direction) | |
| def is_perpendicular(l1, l2): | |
| """Are two linear entities perpendicular? | |
| Parameters | |
| ========== | |
| l1 : LinearEntity | |
| l2 : LinearEntity | |
| Returns | |
| ======= | |
| True : if l1 and l2 are perpendicular, | |
| False : otherwise. | |
| See Also | |
| ======== | |
| coefficients | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(-1, 1) | |
| >>> l1, l2 = Line(p1, p2), Line(p1, p3) | |
| >>> l1.is_perpendicular(l2) | |
| True | |
| >>> p4 = Point(5, 3) | |
| >>> l3 = Line(p1, p4) | |
| >>> l1.is_perpendicular(l3) | |
| False | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(-1, 2, 0) | |
| >>> l1, l2 = Line3D(p1, p2), Line3D(p2, p3) | |
| >>> l1.is_perpendicular(l2) | |
| False | |
| >>> p4 = Point3D(5, 3, 7) | |
| >>> l3 = Line3D(p1, p4) | |
| >>> l1.is_perpendicular(l3) | |
| False | |
| """ | |
| if not isinstance(l1, LinearEntity) and not isinstance(l2, LinearEntity): | |
| raise TypeError('Must pass only LinearEntity objects') | |
| return S.Zero.equals(l1.direction.dot(l2.direction)) | |
| def is_similar(self, other): | |
| """ | |
| Return True if self and other are contained in the same line. | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2, p3 = Point(0, 1), Point(3, 4), Point(2, 3) | |
| >>> l1 = Line(p1, p2) | |
| >>> l2 = Line(p1, p3) | |
| >>> l1.is_similar(l2) | |
| True | |
| """ | |
| l = Line(self.p1, self.p2) | |
| return l.contains(other) | |
| def length(self): | |
| """ | |
| The length of the line. | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(3, 5) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.length | |
| oo | |
| """ | |
| return S.Infinity | |
| def p1(self): | |
| """The first defining point of a linear entity. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(5, 3) | |
| >>> l = Line(p1, p2) | |
| >>> l.p1 | |
| Point2D(0, 0) | |
| """ | |
| return self.args[0] | |
| def p2(self): | |
| """The second defining point of a linear entity. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(5, 3) | |
| >>> l = Line(p1, p2) | |
| >>> l.p2 | |
| Point2D(5, 3) | |
| """ | |
| return self.args[1] | |
| def parallel_line(self, p): | |
| """Create a new Line parallel to this linear entity which passes | |
| through the point `p`. | |
| Parameters | |
| ========== | |
| p : Point | |
| Returns | |
| ======= | |
| line : Line | |
| See Also | |
| ======== | |
| is_parallel | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2, p3 = Point(0, 0), Point(2, 3), Point(-2, 2) | |
| >>> l1 = Line(p1, p2) | |
| >>> l2 = l1.parallel_line(p3) | |
| >>> p3 in l2 | |
| True | |
| >>> l1.is_parallel(l2) | |
| True | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(2, 3, 4), Point3D(-2, 2, 0) | |
| >>> l1 = Line3D(p1, p2) | |
| >>> l2 = l1.parallel_line(p3) | |
| >>> p3 in l2 | |
| True | |
| >>> l1.is_parallel(l2) | |
| True | |
| """ | |
| p = Point(p, dim=self.ambient_dimension) | |
| return Line(p, p + self.direction) | |
| def perpendicular_line(self, p): | |
| """Create a new Line perpendicular to this linear entity which passes | |
| through the point `p`. | |
| Parameters | |
| ========== | |
| p : Point | |
| Returns | |
| ======= | |
| line : Line | |
| See Also | |
| ======== | |
| sympy.geometry.line.LinearEntity.is_perpendicular, perpendicular_segment | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(2, 3, 4), Point3D(-2, 2, 0) | |
| >>> L = Line3D(p1, p2) | |
| >>> P = L.perpendicular_line(p3); P | |
| Line3D(Point3D(-2, 2, 0), Point3D(4/29, 6/29, 8/29)) | |
| >>> L.is_perpendicular(P) | |
| True | |
| In 3D the, the first point used to define the line is the point | |
| through which the perpendicular was required to pass; the | |
| second point is (arbitrarily) contained in the given line: | |
| >>> P.p2 in L | |
| True | |
| """ | |
| p = Point(p, dim=self.ambient_dimension) | |
| if p in self: | |
| p = p + self.direction.orthogonal_direction | |
| return Line(p, self.projection(p)) | |
| def perpendicular_segment(self, p): | |
| """Create a perpendicular line segment from `p` to this line. | |
| The endpoints of the segment are ``p`` and the closest point in | |
| the line containing self. (If self is not a line, the point might | |
| not be in self.) | |
| Parameters | |
| ========== | |
| p : Point | |
| Returns | |
| ======= | |
| segment : Segment | |
| Notes | |
| ===== | |
| Returns `p` itself if `p` is on this linear entity. | |
| See Also | |
| ======== | |
| perpendicular_line | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, 2) | |
| >>> l1 = Line(p1, p2) | |
| >>> s1 = l1.perpendicular_segment(p3) | |
| >>> l1.is_perpendicular(s1) | |
| True | |
| >>> p3 in s1 | |
| True | |
| >>> l1.perpendicular_segment(Point(4, 0)) | |
| Segment2D(Point2D(4, 0), Point2D(2, 2)) | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(0, 2, 0) | |
| >>> l1 = Line3D(p1, p2) | |
| >>> s1 = l1.perpendicular_segment(p3) | |
| >>> l1.is_perpendicular(s1) | |
| True | |
| >>> p3 in s1 | |
| True | |
| >>> l1.perpendicular_segment(Point3D(4, 0, 0)) | |
| Segment3D(Point3D(4, 0, 0), Point3D(4/3, 4/3, 4/3)) | |
| """ | |
| p = Point(p, dim=self.ambient_dimension) | |
| if p in self: | |
| return p | |
| l = self.perpendicular_line(p) | |
| # The intersection should be unique, so unpack the singleton | |
| p2, = Intersection(Line(self.p1, self.p2), l) | |
| return Segment(p, p2) | |
| def points(self): | |
| """The two points used to define this linear entity. | |
| Returns | |
| ======= | |
| points : tuple of Points | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(5, 11) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.points | |
| (Point2D(0, 0), Point2D(5, 11)) | |
| """ | |
| return (self.p1, self.p2) | |
| def projection(self, other): | |
| """Project a point, line, ray, or segment onto this linear entity. | |
| Parameters | |
| ========== | |
| other : Point or LinearEntity (Line, Ray, Segment) | |
| Returns | |
| ======= | |
| projection : Point or LinearEntity (Line, Ray, Segment) | |
| The return type matches the type of the parameter ``other``. | |
| Raises | |
| ====== | |
| GeometryError | |
| When method is unable to perform projection. | |
| Notes | |
| ===== | |
| A projection involves taking the two points that define | |
| the linear entity and projecting those points onto a | |
| Line and then reforming the linear entity using these | |
| projections. | |
| A point P is projected onto a line L by finding the point | |
| on L that is closest to P. This point is the intersection | |
| of L and the line perpendicular to L that passes through P. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point, perpendicular_line | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line, Segment, Rational | |
| >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(Rational(1, 2), 0) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.projection(p3) | |
| Point2D(1/4, 1/4) | |
| >>> p4, p5 = Point(10, 0), Point(12, 1) | |
| >>> s1 = Segment(p4, p5) | |
| >>> l1.projection(s1) | |
| Segment2D(Point2D(5, 5), Point2D(13/2, 13/2)) | |
| >>> p1, p2, p3 = Point(0, 0, 1), Point(1, 1, 2), Point(2, 0, 1) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.projection(p3) | |
| Point3D(2/3, 2/3, 5/3) | |
| >>> p4, p5 = Point(10, 0, 1), Point(12, 1, 3) | |
| >>> s1 = Segment(p4, p5) | |
| >>> l1.projection(s1) | |
| Segment3D(Point3D(10/3, 10/3, 13/3), Point3D(5, 5, 6)) | |
| """ | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| def proj_point(p): | |
| return Point.project(p - self.p1, self.direction) + self.p1 | |
| if isinstance(other, Point): | |
| return proj_point(other) | |
| elif isinstance(other, LinearEntity): | |
| p1, p2 = proj_point(other.p1), proj_point(other.p2) | |
| # test to see if we're degenerate | |
| if p1 == p2: | |
| return p1 | |
| projected = other.__class__(p1, p2) | |
| projected = Intersection(self, projected) | |
| if projected.is_empty: | |
| return projected | |
| # if we happen to have intersected in only a point, return that | |
| if projected.is_FiniteSet and len(projected) == 1: | |
| # projected is a set of size 1, so unpack it in `a` | |
| a, = projected | |
| return a | |
| # order args so projection is in the same direction as self | |
| if self.direction.dot(projected.direction) < 0: | |
| p1, p2 = projected.args | |
| projected = projected.func(p2, p1) | |
| return projected | |
| raise GeometryError( | |
| "Do not know how to project %s onto %s" % (other, self)) | |
| def random_point(self, seed=None): | |
| """A random point on a LinearEntity. | |
| Returns | |
| ======= | |
| point : Point | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line, Ray, Segment | |
| >>> p1, p2 = Point(0, 0), Point(5, 3) | |
| >>> line = Line(p1, p2) | |
| >>> r = line.random_point(seed=42) # seed value is optional | |
| >>> r.n(3) | |
| Point2D(-0.72, -0.432) | |
| >>> r in line | |
| True | |
| >>> Ray(p1, p2).random_point(seed=42).n(3) | |
| Point2D(0.72, 0.432) | |
| >>> Segment(p1, p2).random_point(seed=42).n(3) | |
| Point2D(3.2, 1.92) | |
| """ | |
| if seed is not None: | |
| rng = random.Random(seed) | |
| else: | |
| rng = random | |
| pt = self.arbitrary_point(t) | |
| if isinstance(self, Ray): | |
| v = abs(rng.gauss(0, 1)) | |
| elif isinstance(self, Segment): | |
| v = rng.random() | |
| elif isinstance(self, Line): | |
| v = rng.gauss(0, 1) | |
| else: | |
| raise NotImplementedError('unhandled line type') | |
| return pt.subs(t, Rational(v)) | |
| def bisectors(self, other): | |
| """Returns the perpendicular lines which pass through the intersections | |
| of self and other that are in the same plane. | |
| Parameters | |
| ========== | |
| line : Line3D | |
| Returns | |
| ======= | |
| list: two Line instances | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Line3D | |
| >>> r1 = Line3D(Point3D(0, 0, 0), Point3D(1, 0, 0)) | |
| >>> r2 = Line3D(Point3D(0, 0, 0), Point3D(0, 1, 0)) | |
| >>> r1.bisectors(r2) | |
| [Line3D(Point3D(0, 0, 0), Point3D(1, 1, 0)), Line3D(Point3D(0, 0, 0), Point3D(1, -1, 0))] | |
| """ | |
| if not isinstance(other, LinearEntity): | |
| raise GeometryError("Expecting LinearEntity, not %s" % other) | |
| l1, l2 = self, other | |
| # make sure dimensions match or else a warning will rise from | |
| # intersection calculation | |
| if l1.p1.ambient_dimension != l2.p1.ambient_dimension: | |
| if isinstance(l1, Line2D): | |
| l1, l2 = l2, l1 | |
| _, p1 = Point._normalize_dimension(l1.p1, l2.p1, on_morph='ignore') | |
| _, p2 = Point._normalize_dimension(l1.p2, l2.p2, on_morph='ignore') | |
| l2 = Line(p1, p2) | |
| point = intersection(l1, l2) | |
| # Three cases: Lines may intersect in a point, may be equal or may not intersect. | |
| if not point: | |
| raise GeometryError("The lines do not intersect") | |
| else: | |
| pt = point[0] | |
| if isinstance(pt, Line): | |
| # Intersection is a line because both lines are coincident | |
| return [self] | |
| d1 = l1.direction.unit | |
| d2 = l2.direction.unit | |
| bis1 = Line(pt, pt + d1 + d2) | |
| bis2 = Line(pt, pt + d1 - d2) | |
| return [bis1, bis2] | |
| class Line(LinearEntity): | |
| """An infinite line in space. | |
| A 2D line is declared with two distinct points, point and slope, or | |
| an equation. A 3D line may be defined with a point and a direction ratio. | |
| Parameters | |
| ========== | |
| p1 : Point | |
| p2 : Point | |
| slope : SymPy expression | |
| direction_ratio : list | |
| equation : equation of a line | |
| Notes | |
| ===== | |
| `Line` will automatically subclass to `Line2D` or `Line3D` based | |
| on the dimension of `p1`. The `slope` argument is only relevant | |
| for `Line2D` and the `direction_ratio` argument is only relevant | |
| for `Line3D`. | |
| The order of the points will define the direction of the line | |
| which is used when calculating the angle between lines. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| sympy.geometry.line.Line2D | |
| sympy.geometry.line.Line3D | |
| Examples | |
| ======== | |
| >>> from sympy import Line, Segment, Point, Eq | |
| >>> from sympy.abc import x, y, a, b | |
| >>> L = Line(Point(2,3), Point(3,5)) | |
| >>> L | |
| Line2D(Point2D(2, 3), Point2D(3, 5)) | |
| >>> L.points | |
| (Point2D(2, 3), Point2D(3, 5)) | |
| >>> L.equation() | |
| -2*x + y + 1 | |
| >>> L.coefficients | |
| (-2, 1, 1) | |
| Instantiate with keyword ``slope``: | |
| >>> Line(Point(0, 0), slope=0) | |
| Line2D(Point2D(0, 0), Point2D(1, 0)) | |
| Instantiate with another linear object | |
| >>> s = Segment((0, 0), (0, 1)) | |
| >>> Line(s).equation() | |
| x | |
| The line corresponding to an equation in the for `ax + by + c = 0`, | |
| can be entered: | |
| >>> Line(3*x + y + 18) | |
| Line2D(Point2D(0, -18), Point2D(1, -21)) | |
| If `x` or `y` has a different name, then they can be specified, too, | |
| as a string (to match the name) or symbol: | |
| >>> Line(Eq(3*a + b, -18), x='a', y=b) | |
| Line2D(Point2D(0, -18), Point2D(1, -21)) | |
| """ | |
| def __new__(cls, *args, **kwargs): | |
| if len(args) == 1 and isinstance(args[0], (Expr, Eq)): | |
| missing = uniquely_named_symbol('?', args) | |
| if not kwargs: | |
| x = 'x' | |
| y = 'y' | |
| else: | |
| x = kwargs.pop('x', missing) | |
| y = kwargs.pop('y', missing) | |
| if kwargs: | |
| raise ValueError('expecting only x and y as keywords') | |
| equation = args[0] | |
| if isinstance(equation, Eq): | |
| equation = equation.lhs - equation.rhs | |
| def find_or_missing(x): | |
| try: | |
| return find(x, equation) | |
| except ValueError: | |
| return missing | |
| x = find_or_missing(x) | |
| y = find_or_missing(y) | |
| a, b, c = linear_coeffs(equation, x, y) | |
| if b: | |
| return Line((0, -c/b), slope=-a/b) | |
| if a: | |
| return Line((-c/a, 0), slope=oo) | |
| raise ValueError('not found in equation: %s' % (set('xy') - {x, y})) | |
| else: | |
| if len(args) > 0: | |
| p1 = args[0] | |
| if len(args) > 1: | |
| p2 = args[1] | |
| else: | |
| p2 = None | |
| if isinstance(p1, LinearEntity): | |
| if p2: | |
| raise ValueError('If p1 is a LinearEntity, p2 must be None.') | |
| dim = len(p1.p1) | |
| else: | |
| p1 = Point(p1) | |
| dim = len(p1) | |
| if p2 is not None or isinstance(p2, Point) and p2.ambient_dimension != dim: | |
| p2 = Point(p2) | |
| if dim == 2: | |
| return Line2D(p1, p2, **kwargs) | |
| elif dim == 3: | |
| return Line3D(p1, p2, **kwargs) | |
| return LinearEntity.__new__(cls, p1, p2, **kwargs) | |
| def contains(self, other): | |
| """ | |
| Return True if `other` is on this Line, or False otherwise. | |
| Examples | |
| ======== | |
| >>> from sympy import Line,Point | |
| >>> p1, p2 = Point(0, 1), Point(3, 4) | |
| >>> l = Line(p1, p2) | |
| >>> l.contains(p1) | |
| True | |
| >>> l.contains((0, 1)) | |
| True | |
| >>> l.contains((0, 0)) | |
| False | |
| >>> a = (0, 0, 0) | |
| >>> b = (1, 1, 1) | |
| >>> c = (2, 2, 2) | |
| >>> l1 = Line(a, b) | |
| >>> l2 = Line(b, a) | |
| >>> l1 == l2 | |
| False | |
| >>> l1 in l2 | |
| True | |
| """ | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| if isinstance(other, Point): | |
| return Point.is_collinear(other, self.p1, self.p2) | |
| if isinstance(other, LinearEntity): | |
| return Point.is_collinear(self.p1, self.p2, other.p1, other.p2) | |
| return False | |
| def distance(self, other): | |
| """ | |
| Finds the shortest distance between a line and a point. | |
| Raises | |
| ====== | |
| NotImplementedError is raised if `other` is not a Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(1, 1) | |
| >>> s = Line(p1, p2) | |
| >>> s.distance(Point(-1, 1)) | |
| sqrt(2) | |
| >>> s.distance((-1, 2)) | |
| 3*sqrt(2)/2 | |
| >>> p1, p2 = Point(0, 0, 0), Point(1, 1, 1) | |
| >>> s = Line(p1, p2) | |
| >>> s.distance(Point(-1, 1, 1)) | |
| 2*sqrt(6)/3 | |
| >>> s.distance((-1, 1, 1)) | |
| 2*sqrt(6)/3 | |
| """ | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| if self.contains(other): | |
| return S.Zero | |
| return self.perpendicular_segment(other).length | |
| def equals(self, other): | |
| """Returns True if self and other are the same mathematical entities""" | |
| if not isinstance(other, Line): | |
| return False | |
| return Point.is_collinear(self.p1, other.p1, self.p2, other.p2) | |
| def plot_interval(self, parameter='t'): | |
| """The plot interval for the default geometric plot of line. Gives | |
| values that will produce a line that is +/- 5 units long (where a | |
| unit is the distance between the two points that define the line). | |
| Parameters | |
| ========== | |
| parameter : str, optional | |
| Default value is 't'. | |
| Returns | |
| ======= | |
| plot_interval : list (plot interval) | |
| [parameter, lower_bound, upper_bound] | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(5, 3) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.plot_interval() | |
| [t, -5, 5] | |
| """ | |
| t = _symbol(parameter, real=True) | |
| return [t, -5, 5] | |
| class Ray(LinearEntity): | |
| """A Ray is a semi-line in the space with a source point and a direction. | |
| Parameters | |
| ========== | |
| p1 : Point | |
| The source of the Ray | |
| p2 : Point or radian value | |
| This point determines the direction in which the Ray propagates. | |
| If given as an angle it is interpreted in radians with the positive | |
| direction being ccw. | |
| Attributes | |
| ========== | |
| source | |
| See Also | |
| ======== | |
| sympy.geometry.line.Ray2D | |
| sympy.geometry.line.Ray3D | |
| sympy.geometry.point.Point | |
| sympy.geometry.line.Line | |
| Notes | |
| ===== | |
| `Ray` will automatically subclass to `Ray2D` or `Ray3D` based on the | |
| dimension of `p1`. | |
| Examples | |
| ======== | |
| >>> from sympy import Ray, Point, pi | |
| >>> r = Ray(Point(2, 3), Point(3, 5)) | |
| >>> r | |
| Ray2D(Point2D(2, 3), Point2D(3, 5)) | |
| >>> r.points | |
| (Point2D(2, 3), Point2D(3, 5)) | |
| >>> r.source | |
| Point2D(2, 3) | |
| >>> r.xdirection | |
| oo | |
| >>> r.ydirection | |
| oo | |
| >>> r.slope | |
| 2 | |
| >>> Ray(Point(0, 0), angle=pi/4).slope | |
| 1 | |
| """ | |
| def __new__(cls, p1, p2=None, **kwargs): | |
| p1 = Point(p1) | |
| if p2 is not None: | |
| p1, p2 = Point._normalize_dimension(p1, Point(p2)) | |
| dim = len(p1) | |
| if dim == 2: | |
| return Ray2D(p1, p2, **kwargs) | |
| elif dim == 3: | |
| return Ray3D(p1, p2, **kwargs) | |
| return LinearEntity.__new__(cls, p1, p2, **kwargs) | |
| def _svg(self, scale_factor=1., fill_color="#66cc99"): | |
| """Returns SVG path element for the LinearEntity. | |
| 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". | |
| """ | |
| verts = (N(self.p1), N(self.p2)) | |
| coords = ["{},{}".format(p.x, p.y) for p in verts] | |
| path = "M {} L {}".format(coords[0], " L ".join(coords[1:])) | |
| return ( | |
| '<path fill-rule="evenodd" fill="{2}" stroke="#555555" ' | |
| 'stroke-width="{0}" opacity="0.6" d="{1}" ' | |
| 'marker-start="url(#markerCircle)" marker-end="url(#markerArrow)"/>' | |
| ).format(2.*scale_factor, path, fill_color) | |
| def contains(self, other): | |
| """ | |
| Is other GeometryEntity contained in this Ray? | |
| Examples | |
| ======== | |
| >>> from sympy import Ray,Point,Segment | |
| >>> p1, p2 = Point(0, 0), Point(4, 4) | |
| >>> r = Ray(p1, p2) | |
| >>> r.contains(p1) | |
| True | |
| >>> r.contains((1, 1)) | |
| True | |
| >>> r.contains((1, 3)) | |
| False | |
| >>> s = Segment((1, 1), (2, 2)) | |
| >>> r.contains(s) | |
| True | |
| >>> s = Segment((1, 2), (2, 5)) | |
| >>> r.contains(s) | |
| False | |
| >>> r1 = Ray((2, 2), (3, 3)) | |
| >>> r.contains(r1) | |
| True | |
| >>> r1 = Ray((2, 2), (3, 5)) | |
| >>> r.contains(r1) | |
| False | |
| """ | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| if isinstance(other, Point): | |
| if Point.is_collinear(self.p1, self.p2, other): | |
| # if we're in the direction of the ray, our | |
| # direction vector dot the ray's direction vector | |
| # should be non-negative | |
| return bool((self.p2 - self.p1).dot(other - self.p1) >= S.Zero) | |
| return False | |
| elif isinstance(other, Ray): | |
| if Point.is_collinear(self.p1, self.p2, other.p1, other.p2): | |
| return bool((self.p2 - self.p1).dot(other.p2 - other.p1) > S.Zero) | |
| return False | |
| elif isinstance(other, Segment): | |
| return other.p1 in self and other.p2 in self | |
| # No other known entity can be contained in a Ray | |
| return False | |
| def distance(self, other): | |
| """ | |
| Finds the shortest distance between the ray and a point. | |
| Raises | |
| ====== | |
| NotImplementedError is raised if `other` is not a Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Ray | |
| >>> p1, p2 = Point(0, 0), Point(1, 1) | |
| >>> s = Ray(p1, p2) | |
| >>> s.distance(Point(-1, -1)) | |
| sqrt(2) | |
| >>> s.distance((-1, 2)) | |
| 3*sqrt(2)/2 | |
| >>> p1, p2 = Point(0, 0, 0), Point(1, 1, 2) | |
| >>> s = Ray(p1, p2) | |
| >>> s | |
| Ray3D(Point3D(0, 0, 0), Point3D(1, 1, 2)) | |
| >>> s.distance(Point(-1, -1, 2)) | |
| 4*sqrt(3)/3 | |
| >>> s.distance((-1, -1, 2)) | |
| 4*sqrt(3)/3 | |
| """ | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| if self.contains(other): | |
| return S.Zero | |
| proj = Line(self.p1, self.p2).projection(other) | |
| if self.contains(proj): | |
| return abs(other - proj) | |
| else: | |
| return abs(other - self.source) | |
| def equals(self, other): | |
| """Returns True if self and other are the same mathematical entities""" | |
| if not isinstance(other, Ray): | |
| return False | |
| return self.source == other.source and other.p2 in self | |
| def plot_interval(self, parameter='t'): | |
| """The plot interval for the default geometric plot of the Ray. Gives | |
| values that will produce a ray that is 10 units long (where a unit is | |
| the distance between the two points that define the ray). | |
| Parameters | |
| ========== | |
| parameter : str, optional | |
| Default value is 't'. | |
| Returns | |
| ======= | |
| plot_interval : list | |
| [parameter, lower_bound, upper_bound] | |
| Examples | |
| ======== | |
| >>> from sympy import Ray, pi | |
| >>> r = Ray((0, 0), angle=pi/4) | |
| >>> r.plot_interval() | |
| [t, 0, 10] | |
| """ | |
| t = _symbol(parameter, real=True) | |
| return [t, 0, 10] | |
| def source(self): | |
| """The point from which the ray emanates. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Ray | |
| >>> p1, p2 = Point(0, 0), Point(4, 1) | |
| >>> r1 = Ray(p1, p2) | |
| >>> r1.source | |
| Point2D(0, 0) | |
| >>> p1, p2 = Point(0, 0, 0), Point(4, 1, 5) | |
| >>> r1 = Ray(p2, p1) | |
| >>> r1.source | |
| Point3D(4, 1, 5) | |
| """ | |
| return self.p1 | |
| class Segment(LinearEntity): | |
| """A line segment in space. | |
| Parameters | |
| ========== | |
| p1 : Point | |
| p2 : Point | |
| Attributes | |
| ========== | |
| length : number or SymPy expression | |
| midpoint : Point | |
| See Also | |
| ======== | |
| sympy.geometry.line.Segment2D | |
| sympy.geometry.line.Segment3D | |
| sympy.geometry.point.Point | |
| sympy.geometry.line.Line | |
| Notes | |
| ===== | |
| If 2D or 3D points are used to define `Segment`, it will | |
| be automatically subclassed to `Segment2D` or `Segment3D`. | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> Segment((1, 0), (1, 1)) # tuples are interpreted as pts | |
| Segment2D(Point2D(1, 0), Point2D(1, 1)) | |
| >>> s = Segment(Point(4, 3), Point(1, 1)) | |
| >>> s.points | |
| (Point2D(4, 3), Point2D(1, 1)) | |
| >>> s.slope | |
| 2/3 | |
| >>> s.length | |
| sqrt(13) | |
| >>> s.midpoint | |
| Point2D(5/2, 2) | |
| >>> Segment((1, 0, 0), (1, 1, 1)) # tuples are interpreted as pts | |
| Segment3D(Point3D(1, 0, 0), Point3D(1, 1, 1)) | |
| >>> s = Segment(Point(4, 3, 9), Point(1, 1, 7)); s | |
| Segment3D(Point3D(4, 3, 9), Point3D(1, 1, 7)) | |
| >>> s.points | |
| (Point3D(4, 3, 9), Point3D(1, 1, 7)) | |
| >>> s.length | |
| sqrt(17) | |
| >>> s.midpoint | |
| Point3D(5/2, 2, 8) | |
| """ | |
| def __new__(cls, p1, p2, **kwargs): | |
| p1, p2 = Point._normalize_dimension(Point(p1), Point(p2)) | |
| dim = len(p1) | |
| if dim == 2: | |
| return Segment2D(p1, p2, **kwargs) | |
| elif dim == 3: | |
| return Segment3D(p1, p2, **kwargs) | |
| return LinearEntity.__new__(cls, p1, p2, **kwargs) | |
| def contains(self, other): | |
| """ | |
| Is the other GeometryEntity contained within this Segment? | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> p1, p2 = Point(0, 1), Point(3, 4) | |
| >>> s = Segment(p1, p2) | |
| >>> s2 = Segment(p2, p1) | |
| >>> s.contains(s2) | |
| True | |
| >>> from sympy import Point3D, Segment3D | |
| >>> p1, p2 = Point3D(0, 1, 1), Point3D(3, 4, 5) | |
| >>> s = Segment3D(p1, p2) | |
| >>> s2 = Segment3D(p2, p1) | |
| >>> s.contains(s2) | |
| True | |
| >>> s.contains((p1 + p2)/2) | |
| True | |
| """ | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| if isinstance(other, Point): | |
| if Point.is_collinear(other, self.p1, self.p2): | |
| if isinstance(self, Segment2D): | |
| # if it is collinear and is in the bounding box of the | |
| # segment then it must be on the segment | |
| vert = (1/self.slope).equals(0) | |
| if vert is False: | |
| isin = (self.p1.x - other.x)*(self.p2.x - other.x) <= 0 | |
| if isin in (True, False): | |
| return isin | |
| if vert is True: | |
| isin = (self.p1.y - other.y)*(self.p2.y - other.y) <= 0 | |
| if isin in (True, False): | |
| return isin | |
| # use the triangle inequality | |
| d1, d2 = other - self.p1, other - self.p2 | |
| d = self.p2 - self.p1 | |
| # without the call to simplify, SymPy cannot tell that an expression | |
| # like (a+b)*(a/2+b/2) is always non-negative. If it cannot be | |
| # determined, raise an Undecidable error | |
| try: | |
| # the triangle inequality says that |d1|+|d2| >= |d| and is strict | |
| # only if other lies in the line segment | |
| return bool(simplify(Eq(abs(d1) + abs(d2) - abs(d), 0))) | |
| except TypeError: | |
| raise Undecidable("Cannot determine if {} is in {}".format(other, self)) | |
| if isinstance(other, Segment): | |
| return other.p1 in self and other.p2 in self | |
| return False | |
| def equals(self, other): | |
| """Returns True if self and other are the same mathematical entities""" | |
| return isinstance(other, self.func) and list( | |
| ordered(self.args)) == list(ordered(other.args)) | |
| def distance(self, other): | |
| """ | |
| Finds the shortest distance between a line segment and a point. | |
| Raises | |
| ====== | |
| NotImplementedError is raised if `other` is not a Point | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> p1, p2 = Point(0, 1), Point(3, 4) | |
| >>> s = Segment(p1, p2) | |
| >>> s.distance(Point(10, 15)) | |
| sqrt(170) | |
| >>> s.distance((0, 12)) | |
| sqrt(73) | |
| >>> from sympy import Point3D, Segment3D | |
| >>> p1, p2 = Point3D(0, 0, 3), Point3D(1, 1, 4) | |
| >>> s = Segment3D(p1, p2) | |
| >>> s.distance(Point3D(10, 15, 12)) | |
| sqrt(341) | |
| >>> s.distance((10, 15, 12)) | |
| sqrt(341) | |
| """ | |
| if not isinstance(other, GeometryEntity): | |
| other = Point(other, dim=self.ambient_dimension) | |
| if isinstance(other, Point): | |
| vp1 = other - self.p1 | |
| vp2 = other - self.p2 | |
| dot_prod_sign_1 = self.direction.dot(vp1) >= 0 | |
| dot_prod_sign_2 = self.direction.dot(vp2) <= 0 | |
| if dot_prod_sign_1 and dot_prod_sign_2: | |
| return Line(self.p1, self.p2).distance(other) | |
| if dot_prod_sign_1 and not dot_prod_sign_2: | |
| return abs(vp2) | |
| if not dot_prod_sign_1 and dot_prod_sign_2: | |
| return abs(vp1) | |
| raise NotImplementedError() | |
| def length(self): | |
| """The length of the line segment. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point.distance | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> p1, p2 = Point(0, 0), Point(4, 3) | |
| >>> s1 = Segment(p1, p2) | |
| >>> s1.length | |
| 5 | |
| >>> from sympy import Point3D, Segment3D | |
| >>> p1, p2 = Point3D(0, 0, 0), Point3D(4, 3, 3) | |
| >>> s1 = Segment3D(p1, p2) | |
| >>> s1.length | |
| sqrt(34) | |
| """ | |
| return Point.distance(self.p1, self.p2) | |
| def midpoint(self): | |
| """The midpoint of the line segment. | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point.midpoint | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> p1, p2 = Point(0, 0), Point(4, 3) | |
| >>> s1 = Segment(p1, p2) | |
| >>> s1.midpoint | |
| Point2D(2, 3/2) | |
| >>> from sympy import Point3D, Segment3D | |
| >>> p1, p2 = Point3D(0, 0, 0), Point3D(4, 3, 3) | |
| >>> s1 = Segment3D(p1, p2) | |
| >>> s1.midpoint | |
| Point3D(2, 3/2, 3/2) | |
| """ | |
| return Point.midpoint(self.p1, self.p2) | |
| def perpendicular_bisector(self, p=None): | |
| """The perpendicular bisector of this segment. | |
| If no point is specified or the point specified is not on the | |
| bisector then the bisector is returned as a Line. Otherwise a | |
| Segment is returned that joins the point specified and the | |
| intersection of the bisector and the segment. | |
| Parameters | |
| ========== | |
| p : Point | |
| Returns | |
| ======= | |
| bisector : Line or Segment | |
| See Also | |
| ======== | |
| LinearEntity.perpendicular_segment | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> p1, p2, p3 = Point(0, 0), Point(6, 6), Point(5, 1) | |
| >>> s1 = Segment(p1, p2) | |
| >>> s1.perpendicular_bisector() | |
| Line2D(Point2D(3, 3), Point2D(-3, 9)) | |
| >>> s1.perpendicular_bisector(p3) | |
| Segment2D(Point2D(5, 1), Point2D(3, 3)) | |
| """ | |
| l = self.perpendicular_line(self.midpoint) | |
| if p is not None: | |
| p2 = Point(p, dim=self.ambient_dimension) | |
| if p2 in l: | |
| return Segment(p2, self.midpoint) | |
| return l | |
| def plot_interval(self, parameter='t'): | |
| """The plot interval for the default geometric plot of the Segment gives | |
| values that will produce the full segment in a plot. | |
| Parameters | |
| ========== | |
| parameter : str, optional | |
| Default value is 't'. | |
| Returns | |
| ======= | |
| plot_interval : list | |
| [parameter, lower_bound, upper_bound] | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> p1, p2 = Point(0, 0), Point(5, 3) | |
| >>> s1 = Segment(p1, p2) | |
| >>> s1.plot_interval() | |
| [t, 0, 1] | |
| """ | |
| t = _symbol(parameter, real=True) | |
| return [t, 0, 1] | |
| class LinearEntity2D(LinearEntity): | |
| """A base class for all linear entities (line, ray and segment) | |
| in a 2-dimensional Euclidean space. | |
| Attributes | |
| ========== | |
| p1 | |
| p2 | |
| coefficients | |
| slope | |
| points | |
| Notes | |
| ===== | |
| This is an abstract class and is not meant to be instantiated. | |
| See Also | |
| ======== | |
| sympy.geometry.entity.GeometryEntity | |
| """ | |
| def bounds(self): | |
| """Return a tuple (xmin, ymin, xmax, ymax) representing the bounding | |
| rectangle for the geometric figure. | |
| """ | |
| verts = self.points | |
| xs = [p.x for p in verts] | |
| ys = [p.y for p in verts] | |
| return (min(xs), min(ys), max(xs), max(ys)) | |
| def perpendicular_line(self, p): | |
| """Create a new Line perpendicular to this linear entity which passes | |
| through the point `p`. | |
| Parameters | |
| ========== | |
| p : Point | |
| Returns | |
| ======= | |
| line : Line | |
| See Also | |
| ======== | |
| sympy.geometry.line.LinearEntity.is_perpendicular, perpendicular_segment | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2, p3 = Point(0, 0), Point(2, 3), Point(-2, 2) | |
| >>> L = Line(p1, p2) | |
| >>> P = L.perpendicular_line(p3); P | |
| Line2D(Point2D(-2, 2), Point2D(-5, 4)) | |
| >>> L.is_perpendicular(P) | |
| True | |
| In 2D, the first point of the perpendicular line is the | |
| point through which was required to pass; the second | |
| point is arbitrarily chosen. To get a line that explicitly | |
| uses a point in the line, create a line from the perpendicular | |
| segment from the line to the point: | |
| >>> Line(L.perpendicular_segment(p3)) | |
| Line2D(Point2D(-2, 2), Point2D(4/13, 6/13)) | |
| """ | |
| p = Point(p, dim=self.ambient_dimension) | |
| # any two lines in R^2 intersect, so blindly making | |
| # a line through p in an orthogonal direction will work | |
| # and is faster than finding the projection point as in 3D | |
| return Line(p, p + self.direction.orthogonal_direction) | |
| def slope(self): | |
| """The slope of this linear entity, or infinity if vertical. | |
| Returns | |
| ======= | |
| slope : number or SymPy expression | |
| See Also | |
| ======== | |
| coefficients | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(0, 0), Point(3, 5) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.slope | |
| 5/3 | |
| >>> p3 = Point(0, 4) | |
| >>> l2 = Line(p1, p3) | |
| >>> l2.slope | |
| oo | |
| """ | |
| d1, d2 = (self.p1 - self.p2).args | |
| if d1 == 0: | |
| return S.Infinity | |
| return simplify(d2/d1) | |
| class Line2D(LinearEntity2D, Line): | |
| """An infinite line in space 2D. | |
| A line is declared with two distinct points or a point and slope | |
| as defined using keyword `slope`. | |
| Parameters | |
| ========== | |
| p1 : Point | |
| pt : Point | |
| slope : SymPy expression | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point | |
| Examples | |
| ======== | |
| >>> from sympy import Line, Segment, Point | |
| >>> L = Line(Point(2,3), Point(3,5)) | |
| >>> L | |
| Line2D(Point2D(2, 3), Point2D(3, 5)) | |
| >>> L.points | |
| (Point2D(2, 3), Point2D(3, 5)) | |
| >>> L.equation() | |
| -2*x + y + 1 | |
| >>> L.coefficients | |
| (-2, 1, 1) | |
| Instantiate with keyword ``slope``: | |
| >>> Line(Point(0, 0), slope=0) | |
| Line2D(Point2D(0, 0), Point2D(1, 0)) | |
| Instantiate with another linear object | |
| >>> s = Segment((0, 0), (0, 1)) | |
| >>> Line(s).equation() | |
| x | |
| """ | |
| def __new__(cls, p1, pt=None, slope=None, **kwargs): | |
| if isinstance(p1, LinearEntity): | |
| if pt is not None: | |
| raise ValueError('When p1 is a LinearEntity, pt should be None') | |
| p1, pt = Point._normalize_dimension(*p1.args, dim=2) | |
| else: | |
| p1 = Point(p1, dim=2) | |
| if pt is not None and slope is None: | |
| try: | |
| p2 = Point(pt, dim=2) | |
| except (NotImplementedError, TypeError, ValueError): | |
| raise ValueError(filldedent(''' | |
| The 2nd argument was not a valid Point. | |
| If it was a slope, enter it with keyword "slope". | |
| ''')) | |
| elif slope is not None and pt is None: | |
| slope = sympify(slope) | |
| if slope.is_finite is False: | |
| # when infinite slope, don't change x | |
| dx = 0 | |
| dy = 1 | |
| else: | |
| # go over 1 up slope | |
| dx = 1 | |
| dy = slope | |
| # XXX avoiding simplification by adding to coords directly | |
| p2 = Point(p1.x + dx, p1.y + dy, evaluate=False) | |
| else: | |
| raise ValueError('A 2nd Point or keyword "slope" must be used.') | |
| return LinearEntity2D.__new__(cls, p1, p2, **kwargs) | |
| def _svg(self, scale_factor=1., fill_color="#66cc99"): | |
| """Returns SVG path element for the LinearEntity. | |
| 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". | |
| """ | |
| verts = (N(self.p1), N(self.p2)) | |
| coords = ["{},{}".format(p.x, p.y) for p in verts] | |
| path = "M {} L {}".format(coords[0], " L ".join(coords[1:])) | |
| return ( | |
| '<path fill-rule="evenodd" fill="{2}" stroke="#555555" ' | |
| 'stroke-width="{0}" opacity="0.6" d="{1}" ' | |
| 'marker-start="url(#markerReverseArrow)" marker-end="url(#markerArrow)"/>' | |
| ).format(2.*scale_factor, path, fill_color) | |
| def coefficients(self): | |
| """The coefficients (`a`, `b`, `c`) for `ax + by + c = 0`. | |
| See Also | |
| ======== | |
| sympy.geometry.line.Line2D.equation | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> from sympy.abc import x, y | |
| >>> p1, p2 = Point(0, 0), Point(5, 3) | |
| >>> l = Line(p1, p2) | |
| >>> l.coefficients | |
| (-3, 5, 0) | |
| >>> p3 = Point(x, y) | |
| >>> l2 = Line(p1, p3) | |
| >>> l2.coefficients | |
| (-y, x, 0) | |
| """ | |
| p1, p2 = self.points | |
| if p1.x == p2.x: | |
| return (S.One, S.Zero, -p1.x) | |
| elif p1.y == p2.y: | |
| return (S.Zero, S.One, -p1.y) | |
| return tuple([simplify(i) for i in | |
| (self.p1.y - self.p2.y, | |
| self.p2.x - self.p1.x, | |
| self.p1.x*self.p2.y - self.p1.y*self.p2.x)]) | |
| def equation(self, x='x', y='y'): | |
| """The equation of the line: ax + by + c. | |
| Parameters | |
| ========== | |
| x : str, optional | |
| The name to use for the x-axis, default value is 'x'. | |
| y : str, optional | |
| The name to use for the y-axis, default value is 'y'. | |
| Returns | |
| ======= | |
| equation : SymPy expression | |
| See Also | |
| ======== | |
| sympy.geometry.line.Line2D.coefficients | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Line | |
| >>> p1, p2 = Point(1, 0), Point(5, 3) | |
| >>> l1 = Line(p1, p2) | |
| >>> l1.equation() | |
| -3*x + 4*y + 3 | |
| """ | |
| x = _symbol(x, real=True) | |
| y = _symbol(y, real=True) | |
| p1, p2 = self.points | |
| if p1.x == p2.x: | |
| return x - p1.x | |
| elif p1.y == p2.y: | |
| return y - p1.y | |
| a, b, c = self.coefficients | |
| return a*x + b*y + c | |
| class Ray2D(LinearEntity2D, Ray): | |
| """ | |
| A Ray is a semi-line in the space with a source point and a direction. | |
| Parameters | |
| ========== | |
| p1 : Point | |
| The source of the Ray | |
| p2 : Point or radian value | |
| This point determines the direction in which the Ray propagates. | |
| If given as an angle it is interpreted in radians with the positive | |
| direction being ccw. | |
| Attributes | |
| ========== | |
| source | |
| xdirection | |
| ydirection | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point, Line | |
| Examples | |
| ======== | |
| >>> from sympy import Point, pi, Ray | |
| >>> r = Ray(Point(2, 3), Point(3, 5)) | |
| >>> r | |
| Ray2D(Point2D(2, 3), Point2D(3, 5)) | |
| >>> r.points | |
| (Point2D(2, 3), Point2D(3, 5)) | |
| >>> r.source | |
| Point2D(2, 3) | |
| >>> r.xdirection | |
| oo | |
| >>> r.ydirection | |
| oo | |
| >>> r.slope | |
| 2 | |
| >>> Ray(Point(0, 0), angle=pi/4).slope | |
| 1 | |
| """ | |
| def __new__(cls, p1, pt=None, angle=None, **kwargs): | |
| p1 = Point(p1, dim=2) | |
| if pt is not None and angle is None: | |
| try: | |
| p2 = Point(pt, dim=2) | |
| except (NotImplementedError, TypeError, ValueError): | |
| raise ValueError(filldedent(''' | |
| The 2nd argument was not a valid Point; if | |
| it was meant to be an angle it should be | |
| given with keyword "angle".''')) | |
| if p1 == p2: | |
| raise ValueError('A Ray requires two distinct points.') | |
| elif angle is not None and pt is None: | |
| # we need to know if the angle is an odd multiple of pi/2 | |
| angle = sympify(angle) | |
| c = _pi_coeff(angle) | |
| p2 = None | |
| if c is not None: | |
| if c.is_Rational: | |
| if c.q == 2: | |
| if c.p == 1: | |
| p2 = p1 + Point(0, 1) | |
| elif c.p == 3: | |
| p2 = p1 + Point(0, -1) | |
| elif c.q == 1: | |
| if c.p == 0: | |
| p2 = p1 + Point(1, 0) | |
| elif c.p == 1: | |
| p2 = p1 + Point(-1, 0) | |
| if p2 is None: | |
| c *= S.Pi | |
| else: | |
| c = angle % (2*S.Pi) | |
| if not p2: | |
| m = 2*c/S.Pi | |
| left = And(1 < m, m < 3) # is it in quadrant 2 or 3? | |
| x = Piecewise((-1, left), (Piecewise((0, Eq(m % 1, 0)), (1, True)), True)) | |
| y = Piecewise((-tan(c), left), (Piecewise((1, Eq(m, 1)), (-1, Eq(m, 3)), (tan(c), True)), True)) | |
| p2 = p1 + Point(x, y) | |
| else: | |
| raise ValueError('A 2nd point or keyword "angle" must be used.') | |
| return LinearEntity2D.__new__(cls, p1, p2, **kwargs) | |
| def xdirection(self): | |
| """The x direction of the ray. | |
| Positive infinity if the ray points in the positive x direction, | |
| negative infinity if the ray points in the negative x direction, | |
| or 0 if the ray is vertical. | |
| See Also | |
| ======== | |
| ydirection | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Ray | |
| >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, -1) | |
| >>> r1, r2 = Ray(p1, p2), Ray(p1, p3) | |
| >>> r1.xdirection | |
| oo | |
| >>> r2.xdirection | |
| 0 | |
| """ | |
| if self.p1.x < self.p2.x: | |
| return S.Infinity | |
| elif self.p1.x == self.p2.x: | |
| return S.Zero | |
| else: | |
| return S.NegativeInfinity | |
| def ydirection(self): | |
| """The y direction of the ray. | |
| Positive infinity if the ray points in the positive y direction, | |
| negative infinity if the ray points in the negative y direction, | |
| or 0 if the ray is horizontal. | |
| See Also | |
| ======== | |
| xdirection | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Ray | |
| >>> p1, p2, p3 = Point(0, 0), Point(-1, -1), Point(-1, 0) | |
| >>> r1, r2 = Ray(p1, p2), Ray(p1, p3) | |
| >>> r1.ydirection | |
| -oo | |
| >>> r2.ydirection | |
| 0 | |
| """ | |
| if self.p1.y < self.p2.y: | |
| return S.Infinity | |
| elif self.p1.y == self.p2.y: | |
| return S.Zero | |
| else: | |
| return S.NegativeInfinity | |
| def closing_angle(r1, r2): | |
| """Return the angle by which r2 must be rotated so it faces the same | |
| direction as r1. | |
| Parameters | |
| ========== | |
| r1 : Ray2D | |
| r2 : Ray2D | |
| Returns | |
| ======= | |
| angle : angle in radians (ccw angle is positive) | |
| See Also | |
| ======== | |
| LinearEntity.angle_between | |
| Examples | |
| ======== | |
| >>> from sympy import Ray, pi | |
| >>> r1 = Ray((0, 0), (1, 0)) | |
| >>> r2 = r1.rotate(-pi/2) | |
| >>> angle = r1.closing_angle(r2); angle | |
| pi/2 | |
| >>> r2.rotate(angle).direction.unit == r1.direction.unit | |
| True | |
| >>> r2.closing_angle(r1) | |
| -pi/2 | |
| """ | |
| if not all(isinstance(r, Ray2D) for r in (r1, r2)): | |
| # although the direction property is defined for | |
| # all linear entities, only the Ray is truly a | |
| # directed object | |
| raise TypeError('Both arguments must be Ray2D objects.') | |
| a1 = atan2(*list(reversed(r1.direction.args))) | |
| a2 = atan2(*list(reversed(r2.direction.args))) | |
| if a1*a2 < 0: | |
| a1 = 2*S.Pi + a1 if a1 < 0 else a1 | |
| a2 = 2*S.Pi + a2 if a2 < 0 else a2 | |
| return a1 - a2 | |
| class Segment2D(LinearEntity2D, Segment): | |
| """A line segment in 2D space. | |
| Parameters | |
| ========== | |
| p1 : Point | |
| p2 : Point | |
| Attributes | |
| ========== | |
| length : number or SymPy expression | |
| midpoint : Point | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point, Line | |
| Examples | |
| ======== | |
| >>> from sympy import Point, Segment | |
| >>> Segment((1, 0), (1, 1)) # tuples are interpreted as pts | |
| Segment2D(Point2D(1, 0), Point2D(1, 1)) | |
| >>> s = Segment(Point(4, 3), Point(1, 1)); s | |
| Segment2D(Point2D(4, 3), Point2D(1, 1)) | |
| >>> s.points | |
| (Point2D(4, 3), Point2D(1, 1)) | |
| >>> s.slope | |
| 2/3 | |
| >>> s.length | |
| sqrt(13) | |
| >>> s.midpoint | |
| Point2D(5/2, 2) | |
| """ | |
| def __new__(cls, p1, p2, **kwargs): | |
| p1 = Point(p1, dim=2) | |
| p2 = Point(p2, dim=2) | |
| if p1 == p2: | |
| return p1 | |
| return LinearEntity2D.__new__(cls, p1, p2, **kwargs) | |
| def _svg(self, scale_factor=1., fill_color="#66cc99"): | |
| """Returns SVG path element for the LinearEntity. | |
| 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". | |
| """ | |
| verts = (N(self.p1), N(self.p2)) | |
| coords = ["{},{}".format(p.x, p.y) for p in verts] | |
| path = "M {} L {}".format(coords[0], " L ".join(coords[1:])) | |
| return ( | |
| '<path fill-rule="evenodd" fill="{2}" stroke="#555555" ' | |
| 'stroke-width="{0}" opacity="0.6" d="{1}" />' | |
| ).format(2.*scale_factor, path, fill_color) | |
| class LinearEntity3D(LinearEntity): | |
| """An base class for all linear entities (line, ray and segment) | |
| in a 3-dimensional Euclidean space. | |
| Attributes | |
| ========== | |
| p1 | |
| p2 | |
| direction_ratio | |
| direction_cosine | |
| points | |
| Notes | |
| ===== | |
| This is a base class and is not meant to be instantiated. | |
| """ | |
| def __new__(cls, p1, p2, **kwargs): | |
| p1 = Point3D(p1, dim=3) | |
| p2 = Point3D(p2, dim=3) | |
| if p1 == p2: | |
| # if it makes sense to return a Point, handle in subclass | |
| raise ValueError( | |
| "%s.__new__ requires two unique Points." % cls.__name__) | |
| return GeometryEntity.__new__(cls, p1, p2, **kwargs) | |
| ambient_dimension = 3 | |
| def direction_ratio(self): | |
| """The direction ratio of a given line in 3D. | |
| See Also | |
| ======== | |
| sympy.geometry.line.Line3D.equation | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2 = Point3D(0, 0, 0), Point3D(5, 3, 1) | |
| >>> l = Line3D(p1, p2) | |
| >>> l.direction_ratio | |
| [5, 3, 1] | |
| """ | |
| p1, p2 = self.points | |
| return p1.direction_ratio(p2) | |
| def direction_cosine(self): | |
| """The normalized direction ratio of a given line in 3D. | |
| See Also | |
| ======== | |
| sympy.geometry.line.Line3D.equation | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Line3D | |
| >>> p1, p2 = Point3D(0, 0, 0), Point3D(5, 3, 1) | |
| >>> l = Line3D(p1, p2) | |
| >>> l.direction_cosine | |
| [sqrt(35)/7, 3*sqrt(35)/35, sqrt(35)/35] | |
| >>> sum(i**2 for i in _) | |
| 1 | |
| """ | |
| p1, p2 = self.points | |
| return p1.direction_cosine(p2) | |
| class Line3D(LinearEntity3D, Line): | |
| """An infinite 3D line in space. | |
| A line is declared with two distinct points or a point and direction_ratio | |
| as defined using keyword `direction_ratio`. | |
| Parameters | |
| ========== | |
| p1 : Point3D | |
| pt : Point3D | |
| direction_ratio : list | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point3D | |
| sympy.geometry.line.Line | |
| sympy.geometry.line.Line2D | |
| Examples | |
| ======== | |
| >>> from sympy import Line3D, Point3D | |
| >>> L = Line3D(Point3D(2, 3, 4), Point3D(3, 5, 1)) | |
| >>> L | |
| Line3D(Point3D(2, 3, 4), Point3D(3, 5, 1)) | |
| >>> L.points | |
| (Point3D(2, 3, 4), Point3D(3, 5, 1)) | |
| """ | |
| def __new__(cls, p1, pt=None, direction_ratio=(), **kwargs): | |
| if isinstance(p1, LinearEntity3D): | |
| if pt is not None: | |
| raise ValueError('if p1 is a LinearEntity, pt must be None.') | |
| p1, pt = p1.args | |
| else: | |
| p1 = Point(p1, dim=3) | |
| if pt is not None and len(direction_ratio) == 0: | |
| pt = Point(pt, dim=3) | |
| elif len(direction_ratio) == 3 and pt is None: | |
| pt = Point3D(p1.x + direction_ratio[0], p1.y + direction_ratio[1], | |
| p1.z + direction_ratio[2]) | |
| else: | |
| raise ValueError('A 2nd Point or keyword "direction_ratio" must ' | |
| 'be used.') | |
| return LinearEntity3D.__new__(cls, p1, pt, **kwargs) | |
| def equation(self, x='x', y='y', z='z'): | |
| """Return the equations that define the line in 3D. | |
| Parameters | |
| ========== | |
| x : str, optional | |
| The name to use for the x-axis, default value is 'x'. | |
| y : str, optional | |
| The name to use for the y-axis, default value is 'y'. | |
| z : str, optional | |
| The name to use for the z-axis, default value is 'z'. | |
| Returns | |
| ======= | |
| equation : Tuple of simultaneous equations | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Line3D, solve | |
| >>> from sympy.abc import x, y, z | |
| >>> p1, p2 = Point3D(1, 0, 0), Point3D(5, 3, 0) | |
| >>> l1 = Line3D(p1, p2) | |
| >>> eq = l1.equation(x, y, z); eq | |
| (-3*x + 4*y + 3, z) | |
| >>> solve(eq.subs(z, 0), (x, y, z)) | |
| {x: 4*y/3 + 1} | |
| """ | |
| x, y, z, k = [_symbol(i, real=True) for i in (x, y, z, 'k')] | |
| p1, p2 = self.points | |
| d1, d2, d3 = p1.direction_ratio(p2) | |
| x1, y1, z1 = p1 | |
| eqs = [-d1*k + x - x1, -d2*k + y - y1, -d3*k + z - z1] | |
| # eliminate k from equations by solving first eq with k for k | |
| for i, e in enumerate(eqs): | |
| if e.has(k): | |
| kk = solve(e, k)[0] | |
| eqs.pop(i) | |
| break | |
| return Tuple(*[i.subs(k, kk).as_numer_denom()[0] for i in eqs]) | |
| def distance(self, other): | |
| """ | |
| Finds the shortest distance between a line and another object. | |
| Parameters | |
| ========== | |
| Point3D, Line3D, Plane, tuple, list | |
| Returns | |
| ======= | |
| distance | |
| Notes | |
| ===== | |
| This method accepts only 3D entities as it's parameter | |
| Tuples and lists are converted to Point3D and therefore must be of | |
| length 3, 2 or 1. | |
| NotImplementedError is raised if `other` is not an instance of one | |
| of the specified classes: Point3D, Line3D, or Plane. | |
| Examples | |
| ======== | |
| >>> from sympy.geometry import Line3D | |
| >>> l1 = Line3D((0, 0, 0), (0, 0, 1)) | |
| >>> l2 = Line3D((0, 1, 0), (1, 1, 1)) | |
| >>> l1.distance(l2) | |
| 1 | |
| The computed distance may be symbolic, too: | |
| >>> from sympy.abc import x, y | |
| >>> l1 = Line3D((0, 0, 0), (0, 0, 1)) | |
| >>> l2 = Line3D((0, x, 0), (y, x, 1)) | |
| >>> l1.distance(l2) | |
| Abs(x*y)/Abs(sqrt(y**2)) | |
| """ | |
| from .plane import Plane # Avoid circular import | |
| if isinstance(other, (tuple, list)): | |
| try: | |
| other = Point3D(other) | |
| except ValueError: | |
| pass | |
| if isinstance(other, Point3D): | |
| return super().distance(other) | |
| if isinstance(other, Line3D): | |
| if self == other: | |
| return S.Zero | |
| if self.is_parallel(other): | |
| return super().distance(other.p1) | |
| # Skew lines | |
| self_direction = Matrix(self.direction_ratio) | |
| other_direction = Matrix(other.direction_ratio) | |
| normal = self_direction.cross(other_direction) | |
| plane_through_self = Plane(p1=self.p1, normal_vector=normal) | |
| return other.p1.distance(plane_through_self) | |
| if isinstance(other, Plane): | |
| return other.distance(self) | |
| msg = f"{other} has type {type(other)}, which is unsupported" | |
| raise NotImplementedError(msg) | |
| class Ray3D(LinearEntity3D, Ray): | |
| """ | |
| A Ray is a semi-line in the space with a source point and a direction. | |
| Parameters | |
| ========== | |
| p1 : Point3D | |
| The source of the Ray | |
| p2 : Point or a direction vector | |
| direction_ratio: Determines the direction in which the Ray propagates. | |
| Attributes | |
| ========== | |
| source | |
| xdirection | |
| ydirection | |
| zdirection | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point3D, Line3D | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Ray3D | |
| >>> r = Ray3D(Point3D(2, 3, 4), Point3D(3, 5, 0)) | |
| >>> r | |
| Ray3D(Point3D(2, 3, 4), Point3D(3, 5, 0)) | |
| >>> r.points | |
| (Point3D(2, 3, 4), Point3D(3, 5, 0)) | |
| >>> r.source | |
| Point3D(2, 3, 4) | |
| >>> r.xdirection | |
| oo | |
| >>> r.ydirection | |
| oo | |
| >>> r.direction_ratio | |
| [1, 2, -4] | |
| """ | |
| def __new__(cls, p1, pt=None, direction_ratio=(), **kwargs): | |
| if isinstance(p1, LinearEntity3D): | |
| if pt is not None: | |
| raise ValueError('If p1 is a LinearEntity, pt must be None') | |
| p1, pt = p1.args | |
| else: | |
| p1 = Point(p1, dim=3) | |
| if pt is not None and len(direction_ratio) == 0: | |
| pt = Point(pt, dim=3) | |
| elif len(direction_ratio) == 3 and pt is None: | |
| pt = Point3D(p1.x + direction_ratio[0], p1.y + direction_ratio[1], | |
| p1.z + direction_ratio[2]) | |
| else: | |
| raise ValueError(filldedent(''' | |
| A 2nd Point or keyword "direction_ratio" must be used. | |
| ''')) | |
| return LinearEntity3D.__new__(cls, p1, pt, **kwargs) | |
| def xdirection(self): | |
| """The x direction of the ray. | |
| Positive infinity if the ray points in the positive x direction, | |
| negative infinity if the ray points in the negative x direction, | |
| or 0 if the ray is vertical. | |
| See Also | |
| ======== | |
| ydirection | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Ray3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(0, -1, 0) | |
| >>> r1, r2 = Ray3D(p1, p2), Ray3D(p1, p3) | |
| >>> r1.xdirection | |
| oo | |
| >>> r2.xdirection | |
| 0 | |
| """ | |
| if self.p1.x < self.p2.x: | |
| return S.Infinity | |
| elif self.p1.x == self.p2.x: | |
| return S.Zero | |
| else: | |
| return S.NegativeInfinity | |
| def ydirection(self): | |
| """The y direction of the ray. | |
| Positive infinity if the ray points in the positive y direction, | |
| negative infinity if the ray points in the negative y direction, | |
| or 0 if the ray is horizontal. | |
| See Also | |
| ======== | |
| xdirection | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Ray3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(-1, -1, -1), Point3D(-1, 0, 0) | |
| >>> r1, r2 = Ray3D(p1, p2), Ray3D(p1, p3) | |
| >>> r1.ydirection | |
| -oo | |
| >>> r2.ydirection | |
| 0 | |
| """ | |
| if self.p1.y < self.p2.y: | |
| return S.Infinity | |
| elif self.p1.y == self.p2.y: | |
| return S.Zero | |
| else: | |
| return S.NegativeInfinity | |
| def zdirection(self): | |
| """The z direction of the ray. | |
| Positive infinity if the ray points in the positive z direction, | |
| negative infinity if the ray points in the negative z direction, | |
| or 0 if the ray is horizontal. | |
| See Also | |
| ======== | |
| xdirection | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Ray3D | |
| >>> p1, p2, p3 = Point3D(0, 0, 0), Point3D(-1, -1, -1), Point3D(-1, 0, 0) | |
| >>> r1, r2 = Ray3D(p1, p2), Ray3D(p1, p3) | |
| >>> r1.ydirection | |
| -oo | |
| >>> r2.ydirection | |
| 0 | |
| >>> r2.zdirection | |
| 0 | |
| """ | |
| if self.p1.z < self.p2.z: | |
| return S.Infinity | |
| elif self.p1.z == self.p2.z: | |
| return S.Zero | |
| else: | |
| return S.NegativeInfinity | |
| class Segment3D(LinearEntity3D, Segment): | |
| """A line segment in a 3D space. | |
| Parameters | |
| ========== | |
| p1 : Point3D | |
| p2 : Point3D | |
| Attributes | |
| ========== | |
| length : number or SymPy expression | |
| midpoint : Point3D | |
| See Also | |
| ======== | |
| sympy.geometry.point.Point3D, Line3D | |
| Examples | |
| ======== | |
| >>> from sympy import Point3D, Segment3D | |
| >>> Segment3D((1, 0, 0), (1, 1, 1)) # tuples are interpreted as pts | |
| Segment3D(Point3D(1, 0, 0), Point3D(1, 1, 1)) | |
| >>> s = Segment3D(Point3D(4, 3, 9), Point3D(1, 1, 7)); s | |
| Segment3D(Point3D(4, 3, 9), Point3D(1, 1, 7)) | |
| >>> s.points | |
| (Point3D(4, 3, 9), Point3D(1, 1, 7)) | |
| >>> s.length | |
| sqrt(17) | |
| >>> s.midpoint | |
| Point3D(5/2, 2, 8) | |
| """ | |
| def __new__(cls, p1, p2, **kwargs): | |
| p1 = Point(p1, dim=3) | |
| p2 = Point(p2, dim=3) | |
| if p1 == p2: | |
| return p1 | |
| return LinearEntity3D.__new__(cls, p1, p2, **kwargs) | |
Xet Storage Details
- Size:
- 80.4 kB
- Xet hash:
- a86038ae9c3d207c313c64dbb29e731242de8e33a8e6973f07d7ecbe0e967721
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.