Repository: einsteinpy/einsteinpy

Issue:
Simulation of orbits - Animations
Simulating orbits with coordinate time running and correspondingly plot radial and angular coordinates and showing plots in animation format


Discussion:
shreyasbapat: Yess! Well, this is how the plotting ecosystem should grow! :) 
Now I would like to see some controllable animation!
shreyasbapat: And BTW All lights green! I will review thoroughly in a moment
Varunvaruns9: Updated the tests and added a new feature to control the speed of plotting. @shreyasbapat 
ritzvik: Really great work @Varunvaruns9 .
Varunvaruns9: Thanks @ritzvik 😀
shreyasbapat: My god! What happened to codecov?
shreyasbapat: I see : https://status.codecov.io/ 
We can't merge this before we get coverage report :/
Varunvaruns9: Please merge #180 before this. I would have to resolve some conflicts before this is mergeable.
Varunvaruns9: Resolved conflicts.
Varunvaruns9: @shreyasbapat Done the changes. Thanks for the review :)
Varunvaruns9: @shreyasbapat Fixed.
shreyasbapat: One rebase and it's in
![Screenshot from 2019-04-12 02-50-27](https://user-images.githubusercontent.com/24257914/55993874-cda98200-5ccd-11e9-8cd3-b8d9cf7b1db6.png)


Patch:
diff --git a/src/einsteinpy/plotting/geodesics_scatter.py b/src/einsteinpy/plotting/geodesics_scatter.py
index a022401..8bd2d9a 100644
--- a/src/einsteinpy/plotting/geodesics_scatter.py
+++ b/src/einsteinpy/plotting/geodesics_scatter.py
@@ -1,6 +1,7 @@
 import astropy.units as u
 import matplotlib.pyplot as plt
 import numpy as np
+from matplotlib.animation import FuncAnimation
 
 from einsteinpy.metric import Schwarzschild
 
@@ -20,6 +21,21 @@ class ScatterGeodesicPlotter:
         plt.scatter(0, 0, color="black")
 
     def plot(self, pos_vec, vel_vec, end_lambda=10, step_size=1e-3):
+        """
+
+        Parameters
+        ----------
+        pos_vec : list
+            list of r, theta & phi components along with ~astropy.units.
+        vel_vec : list
+            list of velocities of r, theta & phi components along with ~astropy.units.
+        end_lambda : float, optional
+            Lambda where iteartions will stop.
+        step_size : float, optional
+            Step size for the ODE.
+
+        """
+
         swc = Schwarzschild.from_spherical(pos_vec, vel_vec, self.time, self.mass)
 
         vals = swc.calculate_trajectory(
@@ -40,6 +56,59 @@ class ScatterGeodesicPlotter:
         if not self._attractor_present:
             self._plot_attractor()
 
+    def animate(self, pos_vec, vel_vec, end_lambda=10, step_size=1e-3, interval=50):
+        """
+        Function to generate animated plots of geodesics.
+
+        Parameters
+        ----------
+        pos_vec : list
+            list of r, theta & phi components along with ~astropy.units.
+        vel_vec : list
+            list of velocities of r, theta & phi components along with ~astropy.units.
+        end_lambda : float, optional
+            Lambda where iteartions will stop.
+        step_size : float, optional
+            Step size for the ODE.
+        interval : int, optional
+            Control the time between frames. Add time in milliseconds.
+
+        """
+
+        swc = Schwarzschild.from_spherical(pos_vec, vel_vec, self.time, self.mass)
+
+        vals = swc.calculate_trajectory(
+            end_lambda=end_lambda, OdeMethodKwargs={"stepsize": step_size}
+        )[1]
+
+        time = vals[:, 0]
+        r = vals[:, 1]
+        # Currently not being used (might be useful in future)
+        # theta = vals[:, 2]
+        phi = vals[:, 3]
+
+        pos_x = r * np.cos(phi)
+        pos_y = r * np.sin(phi)
+        frames = pos_x.shape[0]
+        x_max, x_min = max(pos_x), min(pos_x)
+        y_max, y_min = max(pos_y), min(pos_y)
+        margin_x = (x_max - x_min) * 0.1
+        margin_y = (y_max - y_min) * 0.1
+
+        fig = plt.figure()
+
+        plt.xlim(x_min - margin_x, x_max + margin_x)
+        plt.ylim(y_min - margin_y, y_max + margin_y)
+        pic = plt.scatter([], [], s=1)
+        plt.scatter(0, 0, color="black")
+
+        def _update(frame):
+            pic.set_offsets(np.vstack((pos_x[: frame + 1], pos_y[: frame + 1])).T)
+            pic.set_array(time[: frame + 1])
+            return (pic,)
+
+        self.animated = FuncAnimation(fig, _update, frames=frames, interval=interval)
+
     def show(self):
         plt.show()
 


Test patch:
diff --git a/src/einsteinpy/tests/test_plotting/test_scattergeodesicplotter.py b/src/einsteinpy/tests/test_plotting/test_scattergeodesicplotter.py
index b94319b..053c57b 100644
--- a/src/einsteinpy/tests/test_plotting/test_scattergeodesicplotter.py
+++ b/src/einsteinpy/tests/test_plotting/test_scattergeodesicplotter.py
@@ -41,6 +41,18 @@ def test_plot_calls_plot_attractor(mock_plot_attractor):
     mock_plot_attractor.assert_called_with()
 
 
+@mock.patch("einsteinpy.plotting.geodesics_scatter.plt.figure")
+def test_animate_calls_figure(mock_figure):
+    r = [306 * u.m, np.pi / 2 * u.rad, np.pi / 2 * u.rad]
+    v = [0 * u.m / u.s, 0 * u.rad / u.s, 951.0 * u.rad / u.s]
+    m = 4e24 * u.kg
+    el = 0.002
+    ss = 0.5e-6
+    cl = ScatterGeodesicPlotter(m)
+    cl.animate(r, v, el, ss)
+    mock_figure.assert_called_with()
+
+
 @mock.patch("einsteinpy.plotting.geodesics_scatter.plt.show")
 def test_plot_show_shows_plot(mock_show):
     r = [306 * u.m, np.pi / 2 * u.rad, np.pi / 2 * u.rad]