Repository: sympy/sympy

Issue:
make Line3D.equation like Line2D.equation
Line3D.equation conflates the notion of arbitrary point and equation set defining a 3D line. In 2D a single  equation relating x and y is returned; in 3D I would expect the same except now *2* equations are needed to relate the 3 variables.

Currently, (X, Y, Z, k) is returned by Line3D.equation which is not an "equation" which can be solved to find the coordinates of some coordinates given others.  One must solve `(X-k, Y-k, Z-k)`. Or, being careful to solve for k, solve `(Y - X, Z - X)`.

The following is a suggested modification of that routine:

```diff
diff --git a/sympy/geometry/line.py b/sympy/geometry/line.py
index 6af9079..dae05e7 100644
--- a/sympy/geometry/line.py
+++ b/sympy/geometry/line.py
@@ -26,6 +26,7 @@
 from sympy.logic.boolalg import And
 from sympy.simplify.simplify import simplify
 from sympy.geometry.exceptions import GeometryError
+from sympy.core.containers import Tuple
 from sympy.core.decorators import deprecated
 from sympy.sets import Intersection
 from sympy.matrices import Matrix
@@ -2353,8 +2354,8 @@ def __new__(cls, p1, pt=None, direction_ratio=[], **kwargs):
 
         return LinearEntity3D.__new__(cls, p1, pt, **kwargs)
 
-    def equation(self, x='x', y='y', z='z', k='k'):
-        """The equation of the line in 3D
+    def equation(self, x='x', y='y', z='z'):
+        """Return the equations that define the line in 3D.
 
         Parameters
         ==========
@@ -2364,28 +2365,40 @@ def equation(self, x='x', y='y', z='z', k='k'):
         y : str, optional
             The name to use for the y-axis, default value is 'y'.
         z : str, optional
-            The name to use for the x-axis, default value is 'z'.
+            The name to use for the z-axis, default value is 'z'.
 
         Returns
         =======
 
-        equation : tuple
+        equation : Tuple of simultaneous equations
 
         Examples
         ========
 
-        >>> from sympy import Point3D, Line3D
+        >>> 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)
-        >>> l1.equation()
-        (x/4 - 1/4, y/3, zoo*z, k)
+        >>> 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)]
+        from sympy import solve
+        x, y, z, k = [_symbol(i, real=True) for i in (x, y, z, 'k')]
         p1, p2 = self.points
-        a = p1.direction_ratio(p2)
-        return (((x - p1.x)/a[0]), ((y - p1.y)/a[1]),
-                ((z - p1.z)/a[2]), k)
+        d1, d2, d3 = p1.direction_ratio(p2)
+        x1, y1, z1 = p1
+        v = (x, y, z)
+        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(eqs[i], k)[0]
+                eqs.pop(i)
+                break
+        return Tuple(*[i.subs(k, kk).as_numer_denom()[0] for i in eqs])
 
 
 class Ray3D(LinearEntity3D, Ray):
diff --git a/sympy/geometry/tests/test_line.py b/sympy/geometry/tests/test_line.py
index 2475838..9447a57 100644
--- a/sympy/geometry/tests/test_line.py
+++ b/sympy/geometry/tests/test_line.py
@@ -409,9 +409,22 @@ def test_equation():
     assert Line(Point(2, 0), Point(2, 1)).equation() == x - 2
     assert Line(p2, Point(2, 1)).equation() == y - 1
 
-    assert Line3D(Point3D(0, 0, 0), Point3D(1, 1, 1)).equation() == (x, y, z, k)
-    assert Line3D(Point3D(x1, x1, x1), Point3D(y1, y1, y1)).equation() == \
-           ((x - x1) / (-x1 + y1), (-x1 + y) / (-x1 + y1), (-x1 + z) / (-x1 + y1), k)
+    assert Line3D(Point(x1, x1, x1), Point(y1, y1, y1)
+        ).equation() == (-x + y, -x + z)
+    assert Line3D(Point(1, 2, 3), Point(2, 3, 4)
+        ).equation() == (-x + y - 1, -x + z - 2)
+    assert Line3D(Point(1, 2, 3), Point(1, 3, 4)
+        ).equation() == (x - 1, -y + z - 1)
+    assert Line3D(Point(1, 2, 3), Point(2, 2, 4)
+        ).equation() == (y - 2, -x + z - 2)
+    assert Line3D(Point(1, 2, 3), Point(2, 3, 3)
+        ).equation() == (-x + y - 1, z - 3)
+    assert Line3D(Point(1, 2, 3), Point(1, 2, 4)
+        ).equation() == (x - 1, y - 2)
+    assert Line3D(Point(1, 2, 3), Point(1, 3, 3)
+        ).equation() == (x - 1, z - 3)
+    assert Line3D(Point(1, 2, 3), Point(2, 2, 3)
+        ).equation() == (y - 2, z - 3)
 
 
 def test_intersection_2d():
```

Discussion:
vishalg2235: @smichr I have added depreciation. Please review.
vishalg2235: @gxyd please review. Thanks
smichr: Do you know how to squash commits together? The last 3 could be merged with the first commit. That way we get rid of the misspelled "deprecation" instances. It's a good skill to have, too, to keep things tidied up. No need to open a new PR; just use `git push -f vishalg2235 line3D`.
vishalg2235: ok! so in that case instead of 4 commits I could merge my edits in one commit ?
Thanks I will use it in future.
@smichr please review. Is there anything else which  should do ?
ylemkimon: Have restarted failing Travis tests. As @smichr said, you can use `-f` (force) flag to rewrite existing commits in the PR. Also, please open a deprecation issue, explaining it is deprecated behavior and will be removed in the future release.
vishalg2235: @ylemkimon still "Some checks haven't completed yet" is showing. 
I don't know what is rebase on sympy/master and how to do it? can you please explain.
smichr: > I don't know what is rebase on sympy/master

If you set things up as suggested in the [workflow](https://github.com/sympy/sympy/wiki/Development-workflow) document, you should be able to do something like this:

```
git checkout line3D
git branch backup
git checkout master
git pull
git checkout line3D
git rebase master
git reset head~5
git commit -am "Line3d.equation like Line2D.equation"
git push -f  vishalg2235 line3D
```
If all goes well, then "git branch -D backup"
vishalg2235: @smichr I had done everything as above but on command `git push -f  vishalg2235 line3D` it give following error : 
```
fatal: 'vishalg2235' does not appear to be a git repository
fatal: Could not read from remote repository.
```
So I have used `git push -f  origin line3D` 
then it return `Everything up-to-date`
So is rebase done ?
vishalg2235: I had made a mistake.
I used `git commit -am "Line3D.equation like Line2D.equation"` here 3D is capital instead of 3d. What should I do now ?
vishalg2235: after comand `git rebase master` : 
```
First, rewinding head to replay your work on top of it...
Applying: Add `katal`, `gray` to derived units
Using index info to reconstruct a base tree...
M	sympy/physics/units/definitions.py
Falling back to patching base and 3-way merge...
Auto-merging sympy/physics/units/definitions.py
CONFLICT (content): Merge conflict in sympy/physics/units/definitions.py
error: Failed to merge in the changes.
Patch failed at 0001 Add `katal`, `gray` to derived units
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
```

and my remotes are : 
```
Abdullahjavednesar	https://github.com/Abdullahjavednesar/sympy.git (fetch)
Abdullahjavednesar	https://github.com/Abdullahjavednesar/sympy.git (push)
github	git@github.com:vishalg2235/sympy.git (fetch)
github	git@github.com:vishalg2235/sympy.git (push)
origin	https://github.com/vishalg2235/sympy.git (fetch)
origin	https://github.com/vishalg2235/sympy.git (push)
upstream	https://github.com/sympy/sympy.git (fetch)
upstream	https://github.com/sympy/sympy.git (push)
```
vishalg2235: @smichr @ylemkimon @gxyd please help!!
smichr: git's pretty forgiving...plus you have the backup that you already pushed here. You can always checkout vi sha that is shown in `git reflog` to go back to any state where you made a commit. But you (should also) have a backup. Assuming you do, I'll show you the commands I used (after downloading your repo):


```
$ git checkout backup
Switched to branch 'backup'

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (backup)
$ git co master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (master)
$ git pull
Already up-to-date.

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (master)
$ git checkout backup
Switched to branch 'backup'

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (backup)
$ git branch -D line3D
Deleted branch line3D (was 1382de1).

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (backup)
$ git checkout -b line3D
Switched to a new branch 'line3D'

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (line3D)
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Line3d.equation like Line2D.equation
Applying: space removed and k=None
Applying: Depreciation added
Using index info to reconstruct a base tree...
M       sympy/geometry/line.py
Falling back to patching base and 3-way merge...
Auto-merging sympy/geometry/line.py
Applying: deprcated_since_version=1.2
Applying: issue added

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (line3D)
$ git reset head~5
Unstaged changes after reset:
M       sympy/geometry/line.py
M       sympy/geometry/tests/test_line.py

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (line3D)
$ git commit -am 'Line3d.equation like Line2D.equation'
[line3D 372a4d8] Line3d.equation like Line2D.equation
 2 files changed, 50 insertions(+), 15 deletions(-)

CHRIS-LT+chriss@chris-lt MINGW32 ~/sympy (line3D)
$ git push -f smichr line3D
Counting objects: 234, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (218/218), done.
Writing objects: 100% (234/234), 37.72 KiB | 0 bytes/s, done.
Total 234 (delta 169), reused 19 (delta 14)
remote: Resolving deltas: 100% (169/169), completed with 40 local objects.
To git@github.com:smichr/sympy.git
 + 054da85...372a4d8 line3D -> line3D (forced update)
```

If you have questions, feel free to ask.
vishalg2235: @smichr in my case after "git rebase master" command  it gives: 
```
First, rewinding head to replay your work on top of it...
Applying: Add `katal`, `gray` to derived units
Using index info to reconstruct a base tree...
M	sympy/physics/units/definitions.py
Falling back to patching base and 3-way merge...
Auto-merging sympy/physics/units/definitions.py
CONFLICT (content): Merge conflict in sympy/physics/units/definitions.py
error: Failed to merge in the changes.
Patch failed at 0001 Add `katal`, `gray` to derived units
The copy of the patch that failed is found in: .git/rebase-apply/patch
```
Why is this happening . I have not did anything to 'sympy/physics/units/definitions.py' ?
vishalg2235: After command `git reset head~5` it gives : 
```
fatal: ambiguous argument 'head~5': unknown revision or path not in the working tree.
```

Patch:
diff --git a/sympy/geometry/line.py b/sympy/geometry/line.py
index ab2cc8a9e5..ebc8b8073c 100644
--- a/sympy/geometry/line.py
+++ b/sympy/geometry/line.py
@@ -18,6 +18,8 @@
 """
 from __future__ import division, print_function
 
+import warnings
+
 from sympy.core import S, sympify
 from sympy.core.relational import Eq
 from sympy.core.symbol import _symbol
@@ -26,6 +28,7 @@
 from sympy.logic.boolalg import And
 from sympy.simplify.simplify import simplify
 from sympy.geometry.exceptions import GeometryError
+from sympy.core.containers import Tuple
 from sympy.core.decorators import deprecated
 from sympy.sets import Intersection
 from sympy.matrices import Matrix
@@ -34,6 +37,8 @@
 from .point import Point, Point3D
 from sympy.utilities.misc import Undecidable, filldedent
 
+from sympy.utilities.exceptions import SymPyDeprecationWarning
+
 
 class LinearEntity(GeometrySet):
     """A base class for all linear entities (Line, Ray and Segment)
@@ -2353,8 +2358,8 @@ def __new__(cls, p1, pt=None, direction_ratio=[], **kwargs):
 
         return LinearEntity3D.__new__(cls, p1, pt, **kwargs)
 
-    def equation(self, x='x', y='y', z='z', k='k'):
-        """The equation of the line in 3D
+    def equation(self, x='x', y='y', z='z', k=None):
+        """Return the equations that define the line in 3D.
 
         Parameters
         ==========
@@ -2364,28 +2369,45 @@ def equation(self, x='x', y='y', z='z', k='k'):
         y : str, optional
             The name to use for the y-axis, default value is 'y'.
         z : str, optional
-            The name to use for the x-axis, default value is 'z'.
+            The name to use for the z-axis, default value is 'z'.
 
         Returns
         =======
 
-        equation : tuple
+        equation : Tuple of simultaneous equations
 
         Examples
         ========
 
-        >>> from sympy import Point3D, Line3D
+        >>> 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)
-        >>> l1.equation()
-        (x/4 - 1/4, y/3, zoo*z, k)
+        >>> 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)]
+        if k is not None:
+            SymPyDeprecationWarning(
+                            feature="equation() no longer needs 'k'",
+                            issue=13742,
+                            deprecated_since_version="1.2").warn()
+        from sympy import solve
+        x, y, z, k = [_symbol(i, real=True) for i in (x, y, z, 'k')]
         p1, p2 = self.points
-        a = p1.direction_ratio(p2)
-        return (((x - p1.x)/a[0]), ((y - p1.y)/a[1]),
-                ((z - p1.z)/a[2]), k)
+        d1, d2, d3 = p1.direction_ratio(p2)
+        x1, y1, z1 = p1
+        v = (x, y, z)
+        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(eqs[i], k)[0]
+                eqs.pop(i)
+                break
+        return Tuple(*[i.subs(k, kk).as_numer_denom()[0] for i in eqs])
 
 
 class Ray3D(LinearEntity3D, Ray):


Test patch:
diff --git a/sympy/geometry/tests/test_line.py b/sympy/geometry/tests/test_line.py
index ac17dd717a..57dfc9bf8c 100644
--- a/sympy/geometry/tests/test_line.py
+++ b/sympy/geometry/tests/test_line.py
@@ -33,7 +33,7 @@ def warn_with_traceback(message, category, filename, lineno, file=None, line=Non
 
 
 warnings.showwarning = warn_with_traceback
-warnings.simplefilter('always', UserWarning)  # make sure to show warnings every time they occur
+warnings.simplefilter('always', UserWarning)  # make sure to show warnings every time they occurr
 
 
 def feq(a, b):
@@ -409,9 +409,22 @@ def test_equation():
     assert Line(Point(2, 0), Point(2, 1)).equation() == x - 2
     assert Line(p2, Point(2, 1)).equation() == y - 1
 
-    assert Line3D(Point3D(0, 0, 0), Point3D(1, 1, 1)).equation() == (x, y, z, k)
-    assert Line3D(Point3D(x1, x1, x1), Point3D(y1, y1, y1)).equation() == \
-           ((x - x1) / (-x1 + y1), (-x1 + y) / (-x1 + y1), (-x1 + z) / (-x1 + y1), k)
+    assert Line3D(Point(x1, x1, x1), Point(y1, y1, y1)
+        ).equation() == (-x + y, -x + z)
+    assert Line3D(Point(1, 2, 3), Point(2, 3, 4)
+        ).equation() == (-x + y - 1, -x + z - 2)
+    assert Line3D(Point(1, 2, 3), Point(1, 3, 4)
+        ).equation() == (x - 1, -y + z - 1)
+    assert Line3D(Point(1, 2, 3), Point(2, 2, 4)
+        ).equation() == (y - 2, -x + z - 2)
+    assert Line3D(Point(1, 2, 3), Point(2, 3, 3)
+        ).equation() == (-x + y - 1, z - 3)
+    assert Line3D(Point(1, 2, 3), Point(1, 2, 4)
+        ).equation() == (x - 1, y - 2)
+    assert Line3D(Point(1, 2, 3), Point(1, 3, 3)
+        ).equation() == (x - 1, z - 3)
+    assert Line3D(Point(1, 2, 3), Point(2, 2, 3)
+        ).equation() == (y - 2, z - 3)
 
 
 def test_intersection_2d():