From d495c3629b43b6b6ba171102d2573820d46698c6 Mon Sep 17 00:00:00 2001
From: "Olivier J.N. Bertrand" <bolirev@hotmail.com>
Date: Sat, 9 Dec 2017 12:11:52 +0100
Subject: [PATCH] Wrote Simple and AbstractAgent, Multi is in progress

---
 navipy/moving/agent.py | 126 +++++++++++++++++++++++++++++++----------
 navipy/moving/maths.py |  36 +++++++++---
 todo                   |  21 +++++++
 3 files changed, 146 insertions(+), 37 deletions(-)

diff --git a/navipy/moving/agent.py b/navipy/moving/agent.py
index 9e18901..ced17a7 100644
--- a/navipy/moving/agent.py
+++ b/navipy/moving/agent.py
@@ -4,24 +4,97 @@
 import numpy as np
 import pandas as pd
 from pandas.api.types import is_numeric_dtype
+from navipy.database import DataBaseLoad
+from .maths import next_pos, \
+    closest_pos, \
+    closest_pos_memory_friendly, \
+    __mode_moves_supported
 
 
-class Single():
+class AbstractAgent():
+    # Define a default mode_of_motion
+    __mode_move = {'mode': 'on_cubic_grid',
+                   'param': {'grid_spacing': 1}}
+
+    def __init__(self,
+                 database,
+                 memory_friendly=False):
+
+        if isinstance(database, DataBaseLoad):
+            self.__db = database
+        else:
+            raise TypeError('database should be of type DataBaseLoad')
+
+        if memory_friendly:
+            self.__posorients = None
+        else:
+            self.__posorients = self.__db.get_posorients()
+
+    @property
+    def mode_of_motion(self):
+        """
+        """
+        toreturn = self.__mode_move
+        toreturn['describe'] = \
+            __mode_moves_supported[self.__mode_move['mode']]['describe']
+        return toreturn
+
+    @mode_of_motion.setter
+    def mode_of_motion(self, mode):
+        """
+
+        """
+        if not isinstance(mode, dict):
+            raise TypeError('Mode is not a dictionary')
+        if 'mode' not in mode:
+            raise KeyError("'mode' is not a key of mode")
+        if 'param' not in mode:
+            raise KeyError("'param' is not a key of mode")
+        if mode['mode'] in __mode_moves_supported.keys:
+            for param in __mode_moves_supported[mode['mode']]['param']:
+                if param not in mode['param']:
+                    raise KeyError(
+                        "'{}' is not in mode['param']".format(param))
+            self.__mode_move = mode
+        else:
+            raise ValueError('mode is not supported')
+
+    def abstractmove(self, posorients_vel):
+
+        # NEED TO CHECK posorients_vel
+
+        # Compute the next position
+        posorients_vel = next_pos(posorients_vel,
+                                  move_mode=self.__mode_move['mode'],
+                                  move_param=self.__mode_move['param'])
+
+        # Compute the closest possible position
+        if posorients_vel is None:
+            posorients_vel[['x', 'y', 'z',
+                            'yaw', 'pitch', 'roll']] = \
+                closest_pos_memory_friendly(posorients_vel, self.__db)
+        else:
+            posorients_vel[['x', 'y', 'z',
+                            'yaw', 'pitch', 'roll']] = \
+                closest_pos(posorients_vel, self.__posorients)
+        return posorients_vel
+
+
+class Single(AbstractAgent):
     __posorientvel = pd.Series(['x', 'y', 'z',
                                 'yaw', 'pitch', 'roll',
                                 'dx', 'dy', 'dy',
                                 'dyaw', 'dpitch', 'droll'],
                                dtype=np.float,
                                data=np.nan)
-    __mode_move = 'on_graph'
-
-    __mode_moves_supported = {
-        'on_grid':
-        "Agent restricted to move on a grid",
-        'on_grid_memoryfriendly':
-        "same as on_grid without loading grid to memory"}
-
-    def __init__(self, database, initial_condition):
+    # Define a list of supported mode in a dictionary
+    # key mode, val is list of required parameters
+
+    def __init__(self,
+                 database,
+                 initial_condition,
+                 memory_friendly=False):
+        super().__init__(database, memory_friendly)
         if isinstance(initial_condition, pd.Series):
             if is_numeric_dtype(initial_condition):
                 for key in self.__posorientvel.index:
@@ -32,19 +105,9 @@ class Single():
         else:
             raise TypeError('vel should be a pandas Series')
 
-        raise NameError('NOT FINISHED, NEED TO CHECK DATABASE')
-        self.__db = database
-
     def move(self):
-        pass
-
-    @property
-    def mode_of_motion(self, mode):
-        if mode in self.__mode_moves_supported:
-            self.__mode_move = mode
-        else:
-            raise ValueError('mode is not supported')
-        raise NameError("NOT FINISHED, NEED TO UPDATE STUFF")
+        # Compute the next position
+        self.__posorientvel = self.abstractmove(self.__posorientvel)
 
     @property
     def position(self):
@@ -91,20 +154,25 @@ class Single():
             raise TypeError('vel should be a pandas Series')
 
 
-def Multi():
+def Multi(AbstractAgent):
 
-    __graph = None
-
-    def __init__(self, database):
-        pass
+    def __init__(self, database,
+                 memory_friendly=False):
+        super().__init__(database, memory_friendly)
+        # Init the graph
+        self.__graph = None
 
     @property
     def graph(self):
         return self.__graph
 
     @graph.setter
-    def graph(self, positions_velocities):
-        # Use Luise Graph builder :)
+    def graph(self, graph):
+        # Check that graph is properly formatted
+        pass
+
+    def build_graph(self, callback_function):
+        # Build a graph with luises code
         pass
 
     def reach_goals():
diff --git a/navipy/moving/maths.py b/navipy/moving/maths.py
index 4265045..4487a37 100644
--- a/navipy/moving/maths.py
+++ b/navipy/moving/maths.py
@@ -5,8 +5,19 @@ geometry, and predefined grids shapes
 import numpy as np
 import pandas as pd
 
+__mode_moves_supported = {
+    'on_cubic_grid': {
+        'param':
+            ['grid_spacing'],
+            'describe':
+            "Agent restricted to move on a grid"},
+    'free_run': {
+        'param': [],
+        'describe':
+        "Freely moving agent, pos(t+dt)=pos+speed (dt=1)"}}
 
-def next_pos(motion_vec, grid_spacing=1, grid_mode='cubic'):
+
+def next_pos(motion_vec, move_mode, move_param=None):
     """return the future position knowing speed and current position
 
     :param motion_vec: the position and speed of the agent
@@ -17,20 +28,19 @@ def next_pos(motion_vec, grid_spacing=1, grid_mode='cubic'):
 
     ..todo: add literal include for supported_grid_mode
     """
-    supported_grid_mode = ['cubic',
-                           None]
     assert isinstance(motion_vec, pd.Series),\
         'motion vector must be a pandas Series'
-    assert grid_mode in supported_grid_mode,\
-        'grid mode must is not supported {}'.format(grid_mode)
+    assert move_mode in __mode_moves_supported,\
+        'move mode must is not supported {}'.format(move_mode)
 
     speed = motion_vec.loc[['dx', 'dy', 'dz']]
     position = motion_vec.loc[['x', 'y', 'z']]
-    if grid_mode == 'cubic':
+    if move_mode == 'on_cubic_grid':
+        grid_spacing = move_param['grid_spacing']
         speed /= np.linalg.norm(speed)
         scaling = grid_spacing / (2 * np.sin(np.pi / 8))
-    elif grid_mode is None:
-        scaling = 1
+    elif move_mode is 'free_run':
+        scaling = 1  # <=> dt = 1, user need to scale speed in dt units
     else:
         raise ValueError('grid_mode is not supported')
 
@@ -49,3 +59,13 @@ def closest_pos(pos, positions):
         + (pos.y - positions.y)**2
         + (pos.z - positions.z)**2)
     return positions.loc[euclidian_dist.idxmin(), :]
+
+
+def closest_pos_memory_friendly(pos, database):
+    """Return the closest position from a list of positions
+
+    :param pos: the position to find (a pandas Series with ['x','y','z']
+    :param database: the possible closest positions
+    (a pandas dataframe with ['x','y','z'])
+    """
+    raise NameError('Not implemated')
diff --git a/todo b/todo
index 612f232..a22ec71 100644
--- a/todo
+++ b/todo
@@ -1,2 +1,23 @@
+Remove DUPLICATE: is_numeric_array is in:
+     processing.__init__
+ and comparing.__init__
+
+ use the one it processing.__init__
+
+Remove DUPLICATE: check_scene is in:
+      processing.__init__
+  and comparing.__init__
+  
+Move check_scene, is_numeric_array to:
+      processing.__init__
+
+Move function in processing/__init__ to processing/place_code.py
+Move function optic_flow in processing/place_code.py to processing/motion_code.py
+
+
 - Fix db in test function such that it work from any location. (probably need to add a module resources and function to load them)
 - Test are failing WHY???
+
+- Adapt GraphManager from Downlowd/clusterstuff for moving/agent/Multi
+	- Change init so that no need to rebuild grid
+	- Use Charlotte next pos 
-- 
GitLab