Repository: sympy/sympy
Pull request: Point: redefine distance() in Point to work for Line and other GeometryEntity
point : modified `distance` to work for any Linear Entity . 

<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234". See
https://github.com/blog/1506-closing-issues-via-pull-requests .-->
Fixes #15531 


#### Brief description of what is fixed or changed
`distance()` is made symmetric . 
For objects of type `Point` , `tuple` or `list` , distance is calculated using distance formula directly by converting into Point if object is not already a Point . A `ValueError` is raised if any other non `GeometryEntity` is given as argument. If` GeometryEntity` has attribute distance then distance is calculated using 
`other.distance(self)`
Otherwise `Attribute Error` is raised. 

#### Other comments


#### Release Notes

<!-- Write the release notes for this release below. See
https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more information
on how to write release notes. The bot will check your release notes
automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->
* geometry
   * `distance` when called from object of Point type can return distance from line . 
<!-- END RELEASE NOTES -->


Problem statement:
Should Point.distance(Line) return distance?
In Geometry module, `Line.distance(Point)` can be used to compute distance, but `Point.distance(Line)` cannot. Should this be made symmetric? 
```
>>> L = Line((1, 1), (2, 2))
>>> P = Point(1, 0)
>>> L.distance(P)
sqrt(2)/2
>>> P.distance(L)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/k3/sympy/sympy/geometry/point.py", line 416, in distance
    s, p = Point._normalize_dimension(self, Point(p))
  File "/home/k3/sympy/sympy/geometry/point.py", line 129, in __new__
    .format(func_name(coords))))
TypeError: 
Expecting sequence of coordinates, not `Line2D`
```

Hints from issue discussion:
It would be natural that `distance` be symmetric. That is not even hard to implement.
I think it is right for `distance` to be symmetric . I would like to give this a try  .

Commit message:     Point : made distance symmetric

    distance function first checks whether argument passed( other) is a Geomety entity.
    If other is not a GeometryEntity , it is converted into a Point if it is a tuple or a list
    otherwise ValueError is raised .
    Further Ifit is a  point then distance is directly calculated using distance formula.
    If it is a any other GeometryEntity such that other.distance is defined it is returned
    otherwise AttributeError is raised.

    Test case is added.

Patch to review:
diff --git a/sympy/geometry/point.py b/sympy/geometry/point.py
index 5c8636e688b1..9a6596682a2f 100644
--- a/sympy/geometry/point.py
+++ b/sympy/geometry/point.py
@@ -380,19 +380,23 @@ def are_coplanar(cls, *points):
         points = list(uniq(points))
         return Point.affine_rank(*points) <= 2
 
-    def distance(self, p):
-        """The Euclidean distance from self to point p.
+    def distance(self, other):
+        """The Euclidean distance between self and other .
 
         Parameters
         ==========
 
-        p : Point
+        p : LinearEntity or Point
 
         Returns
         =======
 
         distance : number or symbolic expression.
 
+        Raises
+        ======
+        AttributeError : if other.distance() is not defined
+
         See Also
         ========
 
@@ -402,19 +406,28 @@ def distance(self, p):
         Examples
         ========
 
-        >>> from sympy.geometry import Point
+        >>> from sympy.geometry import Point, Line
         >>> p1, p2 = Point(1, 1), Point(4, 5)
+        >>> l = Line((3, 1), (2, 2))
         >>> p1.distance(p2)
         5
-
+        >>> p1.distance(l)
+        sqrt(2)
         >>> from sympy.abc import x, y
         >>> p3 = Point(x, y)
         >>> p3.distance(Point(0, 0))
         sqrt(x**2 + y**2)
 
         """
-        s, p = Point._normalize_dimension(self, Point(p))
-        return sqrt(Add(*((a - b)**2 for a, b in zip(s, p))))
+        if not isinstance(other , GeometryEntity) :
+            other = Point(other, dim=self.ambient_dimension)
+        if isinstance(other , Point) :
+            s, p = Point._normalize_dimension(self, Point(other))
+            return sqrt(Add(*((a - b)**2 for a, b in zip(s, p))))
+        try :
+            return other.distance(self)
+        except AttributeError :
+            raise AttributeError("distance between Point and %s is not defined"%type(other))
 
     def dot(self, p):
         """Return dot product of self with another Point."""
diff --git a/sympy/geometry/tests/test_point.py b/sympy/geometry/tests/test_point.py
index c50f66643038..ede35daec9c2 100644
--- a/sympy/geometry/tests/test_point.py
+++ b/sympy/geometry/tests/test_point.py
@@ -33,6 +33,7 @@ def test_point():
     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
@@ -55,6 +56,10 @@ def test_point():
     assert Point.distance(p1, p1) == 0
     assert Point.distance(p3, p2) == sqrt(p2.x**2 + p2.y**2)
 
+    # 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
@@ -72,7 +77,7 @@ def test_point():
     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
-    line = Line(Point(1,0), slope = 1)
+
     raises(TypeError, lambda: Point.is_collinear(line))
     raises(TypeError, lambda: p1_1.is_collinear(line))
 


Code review comments:
diff_hunk: @@ -402,19 +406,28 @@ def distance(self, p):
         Examples
         ========
 
-        >>> from sympy.geometry import Point
+        >>> from sympy.geometry import Point, Line
         >>> p1, p2 = Point(1, 1), Point(4, 5)
+        >>> l = Line((3, 1), (2, 2))
         >>> p1.distance(p2)
         5
-
+        >>> p1.distance(l)
+        sqrt(2)
         >>> from sympy.abc import x, y
         >>> p3 = Point(x, y)
         >>> p3.distance(Point(0, 0))
         sqrt(x**2 + y**2)
 
         """
-        s, p = Point._normalize_dimension(self, Point(p))
-        return sqrt(Add(*((a - b)**2 for a, b in zip(s, p))))
+        if not isinstance(other , GeometryEntity) :
+            other = Point(other, dim=self.ambient_dimension)
path: sympy/geometry/point.py
text: @user1:
Maybe a TypeError should be raised. Or else docstring should be extended.

diff_hunk: @@ -380,19 +380,23 @@ def are_coplanar(cls, *points):
         points = list(uniq(points))
         return Point.affine_rank(*points) <= 2
 
-    def distance(self, p):
-        """The Euclidean distance from self to point p.
+    def distance(self, other):
+        """The Euclidean distance between self and other .
 
         Parameters
         ==========
 
-        p : Point
+        p : LinearEntity or Point
path: sympy/geometry/point.py
text: @user1:
There could probably be GeometricEntity. Distance from non-linear geometric objects like a circle might be later implemented.

@author:
Actually `p` is reference to the point object after line
`s, p = Point._normalize_dimension(self, Point(other))`
It should be `point` only.
`p` does not refer to `other` which can either be a GeometricEntity or LinearEntity.