From 0bcff139b22bf1edac291a87bd1e601a907f110d Mon Sep 17 00:00:00 2001
From: unknown <david.hoksza@gmail.com>
Date: Thu, 31 Oct 2019 09:34:39 +0100
Subject: [PATCH] when laying connecting species on a line consider their
 position to the closest copied species

---
 talent/layout.py | 64 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 53 insertions(+), 11 deletions(-)

diff --git a/talent/layout.py b/talent/layout.py
index ebbfd15..8c62b72 100644
--- a/talent/layout.py
+++ b/talent/layout.py
@@ -165,7 +165,7 @@ def layout_on_line(s_ids: List[str], center: np.ndarray, dir: np.ndarray, forbid
     return rv
 
 
-def layout_on_perp_line(s_ids, center, dir, forbidden: List[gr.Rectangle]=[]) -> Dict[str, np.ndarray]:
+def layout_on_perp_line(g, s_ids, center, dir, forbidden: List[gr.Rectangle]=[]) -> Dict[str, np.ndarray]:
     """
     Get center of positions for the input species. The positions will be on a line perependicular
     to the line defined by center and direction vector (which is normalized). The purpose is to lay out
@@ -187,9 +187,43 @@ def layout_on_perp_line(s_ids, center, dir, forbidden: List[gr.Rectangle]=[]) ->
         return {}
 
     pos = center + dir * settings.render.optimal_species_reaction_dist
-    dir_ortho = gr.utils.orthogonal(dir)
 
-    return layout_on_line(s_ids, pos, dir_ortho, forbidden)
+    dir_ortho1 = gr.utils.orthogonal(dir)
+    dir_ortho2 = - dir_ortho1
+
+    # Connsider both possible directions of orthogonal line. This might be important if we are laying out
+    # a connecting species between existing reaction and an inserted reaction while the connecting species is
+    # inserted as well. In such a case we need the connecting species to be positioned between the two reaction.
+    # However, it could happen that if we chose the wrong direction it would be positioned on the outside of the
+    # space between the two reactions.
+    #  S1 ------r1-----> S2
+    #             \
+    #              \
+    #               X
+    #              /
+    #             /
+    #  S3 ------r2-----> S4
+
+    poss1 = layout_on_line(s_ids, pos, dir_ortho1, forbidden)
+    poss2 = layout_on_line(s_ids, pos, dir_ortho2, forbidden)
+
+    # For each species we consider all the distances of its tentative position to all the species which has been
+    # copied from template and pick position with lowest sum of the distances.
+
+    poss = {}
+    for s_id in s_ids:
+        pos1 = poss1[s_id]
+        pos2 = poss2[s_id]
+        pos_nbs = [gu.get_predicted_layout(g, id).get_center() for id in gu.get_neibhbors_in_distance(g, s_id, 2) if gu.get_predicted_layout(g, id).is_copied()]
+
+        sum_dist1 = sum([gr.utils.dist(pos1, pos_nb) for pos_nb in pos_nbs])
+        sum_dist2 = sum([gr.utils.dist(pos2, pos_nb) for pos_nb in pos_nbs])
+        if len(pos_nbs) == 0 or sum_dist1 < sum_dist2:
+            poss[s_id] = pos1
+        else:
+            poss[s_id] = pos2
+
+    return poss
 
 
 def impute_dirs(dir_reactants: np.ndarray, dir_products: np.ndarray, dir_modifiers: np.ndarray) -> List[np.ndarray]:
@@ -273,10 +307,11 @@ def get_pos_for_species(g: nx.MultiGraph, r_id: str, s_ids: Collection[str], r_k
                 rv[id] = []
             rv[id].append(d[id])
 
-    add_dict(layout_on_perp_line(list(s_id_products), r_center, dir_products))
-    add_dict(layout_on_perp_line(list(s_id_reactans), r_center, dir_reactants))
-    add_dict(layout_on_perp_line(list(s_id_modifiers), r_center, dir_modifiers))
-    add_dict(layout_on_perp_line(list(s_id_modifiers), r_center, -dir_modifiers))
+    forbidden: List[gr.Rectangle] = [gu.get_predicted_layout(g, s_id).get_bb().get() for s_id in known_cs_and_copied]
+    add_dict(layout_on_perp_line(g, list(s_id_products), r_center, dir_products, forbidden))
+    add_dict(layout_on_perp_line(g, list(s_id_reactans), r_center, dir_reactants, forbidden))
+    add_dict(layout_on_perp_line(g, list(s_id_modifiers), r_center, dir_modifiers, forbidden))
+    add_dict(layout_on_perp_line(g, list(s_id_modifiers), r_center, -dir_modifiers, forbidden))
 
     return rv
 
@@ -333,9 +368,16 @@ def lay_out_reaction_center(g: nx.MultiGraph, r_id: str, known_connecting_s_ids:
 def get_correctly_laid_connecting_species(g:nx.MultiGraph, rg: nx.MultiGraph, r_id:str, not_laid_r_ids:List[str]) -> List[str]:
     css = set()
     for r_id1 in rg[r_id]:
+        # cn_ids = []
+        # for cn_id in gu.get_connecting_nodes(g, r_id1, r_id):
+        #     if r_id1 not in not_laid_r_ids or gu.get_predicted_layout(g, cn_id).is_copied():
+        #         cn_ids.append(cn_id)
+        #
+        # css = css.union(cn_ids)
         if r_id1 in not_laid_r_ids:
             continue
         css = css.union(gu.get_connecting_nodes(g, r_id1, r_id))
+
     return list(css)
 
 def aux_lay_out_reaction_center(g, rg, r_id, not_laid_r_ids):
@@ -859,11 +901,11 @@ def layout_ods_for_inserted_reaction(g: nx.MultiGraph, r_id) -> List[Dict[str, n
     not_laid_modifiers_ids = list(set(r_s_ids.modifierIds).difference(laid_out_s_ids))
 
     #TODO when a group of species is laid out the taken_bb should be extended to encompass also the bbs of the new species
-    s_id_pos = {**layout_on_perp_line(not_laid_reactant_ids, r_center, dir_reactants, taken_bb)}
-    s_id_pos = {**s_id_pos, **layout_on_perp_line(not_laid_products_ids, r_center, dir_products, taken_bb)}
+    s_id_pos = {**layout_on_perp_line(g, not_laid_reactant_ids, r_center, dir_reactants, taken_bb)}
+    s_id_pos = {**s_id_pos, **layout_on_perp_line(g, not_laid_products_ids, r_center, dir_products, taken_bb)}
     if len(not_laid_modifiers_ids) > 0:
-        s_id_pos_mod1 = {**s_id_pos, **layout_on_perp_line(not_laid_modifiers_ids, r_center, dir_modifiers, taken_bb)}
-        s_id_pos_mod2 = {**s_id_pos, **layout_on_perp_line(not_laid_modifiers_ids, r_center, -dir_modifiers, taken_bb)}
+        s_id_pos_mod1 = {**s_id_pos, **layout_on_perp_line(g, not_laid_modifiers_ids, r_center, dir_modifiers, taken_bb)}
+        s_id_pos_mod2 = {**s_id_pos, **layout_on_perp_line(g, not_laid_modifiers_ids, r_center, -dir_modifiers, taken_bb)}
         return [s_id_pos_mod1, s_id_pos_mod2]
     else:
         return [s_id_pos]
-- 
GitLab