From e6f44d40d2a25168a1365b670dfc2f9ff0446aaa Mon Sep 17 00:00:00 2001
From: unknown <david.hoksza@gmail.com>
Date: Mon, 21 Oct 2019 08:09:38 +0200
Subject: [PATCH] handling specifically situation with two reactions connected
 with two identical species

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

diff --git a/talent/layout.py b/talent/layout.py
index b526eaa..c74563e 100644
--- a/talent/layout.py
+++ b/talent/layout.py
@@ -1413,9 +1413,12 @@ def tl_mapped_connecting_species(g_res: nx.MultiGraph, tmp, tgt, md, unmapped_re
 
                 ods_tgt = [id for id in gu.get_ods_nbs(tgt_orig, r_tgt_mapped).keys() if not rs_used(laid_out_rs_tgt, r_tmp_mapped, id) ]#make sure that this ODS has not been used for other reaction as a connecting species
                 if len(ods_tgt) > 0:
+                    # free_tmp_ods_ids = ods_tmp
+
+                    # TODO: Not sure whether the following should be commented out or not. It would ensure that we only use for connecting species those species which would otherwise not be used at all. On the other hand, we migh miss relevant positions for the connecting species
                     lsa = get_ods_mapping(ods_tgt, r_tgt_mapped, tgt_orig, ods_tmp, r_tmp_mapped, tmp_orig)
                     ods_tmp_not_mapped_ixs = list(lsa[1]) # species from r_tmp_mapped which will be mapped
-                    ods_tmp_not_mapped_ixs = set(range(len(ods_tmp))).difference(ods_tmp_not_mapped_ixs)
+                    ods_tmp_not_mapped_ixs = set(range(len(ods_tmp))).difference(ods_tmp_not_mapped_ixs) # Keep in the not mapped on those
                     free_tmp_ods_ids = [ods_tmp[ix] for ix in ods_tmp_not_mapped_ixs]
                 else:
                     free_tmp_ods_ids = ods_tmp
@@ -1666,74 +1669,85 @@ def l_not_mapped_connecting_species(g_res, tgt_orig, to_layout, nodes_mapping, t
                 poss_filtered = [x for x in poss if x is not None]
                 return sum(poss_filtered) / len(ids) if len(poss_filtered) > 0 else None
 
-            def impute_mirrored(center: np.ndarray, p1: np.ndarray, p2: np.ndarray):
-                if p1 is None and p2 is None:
-                    return None, None
-                if p1 is None:
-                    p1 = center - (p2 - center)
-                else:
-                    p2 = center - (p1 - center)
-
-                return p1, p2
-
             # 1. Find out whether to use horizontal or vertical vector
-            # r1_center = gu.get_predicted_layout(g_res, r1_tgt).get_center()
-            # r2_center = gu.get_predicted_layout(g_res, r2_tgt).get_center()
             r1_center = get_average_pos(list(g_res[r1_tgt]))
             r2_center = get_average_pos(list(g_res[r2_tgt]))
-            r_vect = r1_center - r2_center
-            # TODO: consider the role of the added species (we can place reactants and and products in such a way that the lines will cross unnecessarilly)
-            if gr.utils.is_more_horizontal(r_vect):
-                dir = np.array([0, 1])
+
+            if r1_center[0] == r2_center[0] and r1_center[1] == r2_center[1] and len(s_ids_to_add):
+                # This is a situation when two species are connected with two reactions which is a special situations and needs to be handled separately
+                # We need to find the orientation of the reactions which is based on existing connecting species and their already laid out neighbors
+                aux_poss = []
+                cn_existing_tgt = [nodes_mapping.getSourceMapping(id)[0] for id in cn_existing]
+                for cn_id in cn_existing_tgt:
+                    nbs_centers = [gu.get_predicted_layout(g_res, id).get_center() for id in gu.get_neibhbors_in_distance(g_res, cn_id, 2)]
+                    nbs_centers_existing = [pos for pos in nbs_centers if pos is not None]
+                    aux_poss.append(sum(nbs_centers_existing)/len(nbs_centers_existing))
+                aux_pos = sum(aux_poss)/len(aux_poss)
+                cn_pos = sum([gu.get_predicted_layout(g_res, id).get_center() for id in cn_existing_tgt])/len(cn_existing_tgt)
+                dir = cn_pos - aux_pos
+                if gr.utils.is_more_horizontal(dir):
+                    dir = np.array([0, 1]) if dir[1] > 0 else np.array([0, -1])
+                else:
+                    dir = np.array([1, 0]) if dir[0] > 0 else np.array([-1, 0])
+
+                s_id = s_ids_to_add[0]
+                poss = {}
+                poss[s_id] = np.array(cn_pos + dir * settings.render.optimal_species_reaction_dist*2)
             else:
-                dir = np.array([1, 0])
-
-            # Check if there is more reactants then products in the connecting species to lay out.
-            # If so, change the direction of the direction vector
-            r1_roles_score = sum([gu.get_roles_score(gu.get_roles(tgt_orig, r1_tgt, s_id)) for s_id in s_ids_to_add])
-            r2_roles_score = sum([gu.get_roles_score(gu.get_roles(tgt_orig, r1_tgt, s_id)) for s_id in s_ids_to_add])
-            if r1_roles_score + r2_roles_score < 0:
-                dir = -dir
-
-            # 2. Find the center
-            cn_existing_lts: List[be.SpeciesLayout] = [
-                gu.get_predicted_layout(g_res, nodes_mapping.getSourceMapping(cn_id)[0]) for cn_id in cn_existing]
-            center = sum([lt.get_center() for lt in cn_existing_lts]) / len(cn_existing)
-            if len(cn_existing) == 1:
-                # If there is only one existing species, the center lies in that species which would then surely overlap
-                # while laying on the perpendicular line so we can immediately shift it
-                center += settings.render.optimal_neighboring_species_dist * dir
-
-            # 3. Git bounding boxes of existing species (forbidden regions)
-            forbidden = [lt.get_bb().get() for lt in cn_existing_lts]
-
-            # 4. Get positions on the line around the center
-            # We should optimally start at the side which is closer to the tentative "side" of the not mapped species.
-            # This means that, for example, if there exists a connecting species s1 which is a reactant of r1 and r2 and
-            # r1 go horizontally from let to right and the not mapped species s1 is in the product role in r1 and r2 then
-            # s2 needs to be laid out right of s1. If these are the only connecting species, length of poss will be 1 and
-            # the position needs to be right of s1. Therefore, the dir vector needs to be in the correct direction.
-            s_id_roles_scores: Dict[str, int] = {}
 
-            for s_id in s_ids_to_add:
-                roles1: List[be.SPECIES_ROLE] = gu.get_roles(tgt_orig, r1_tgt, s_id)
-                roles2: List[be.SPECIES_ROLE] = gu.get_roles(tgt_orig, r2_tgt, s_id)
-                s_id_roles_scores[s_id] = gu.get_roles_score(roles1) + gu.get_roles_score(roles2)
-
-            sum_roles_score: int = sum(list(s_id_roles_scores.values()))
-            if sum_roles_score < 0:
-                # Majority of the new connecting species are in the reactant role and since the dir vector
-                # points toward product roles we need to switch the direction so that first will be laid out reactants
-                # (if there is space)
-                dir -= dir
-
-            # Sort the species from reactants to products since the positions will be assigned in this order
-            # and they will go from "left" to "right" with respect to the center and direction vector
-            aux_s_ids_to_add = [x[0] for x in sorted(s_id_roles_scores.items(), key=lambda x: x[1])]
-            poss = layout_on_line(s_ids=aux_s_ids_to_add, center=center, dir=dir, forbidden=forbidden)
-
-            # 5. Swap positions to minimize number of line overlaps
-            # TODO
+                r_vect = r1_center - r2_center
+                # TODO: consider the role of the added species (we can place reactants and and products in such a way that the lines will cross unnecessarilly)
+                if gr.utils.is_more_horizontal(r_vect):
+                    dir = np.array([0, 1])
+                else:
+                    dir = np.array([1, 0])
+
+                # Check if there is more reactants then products in the connecting species to lay out.
+                # If so, change the direction of the direction vector
+                r1_roles_score = sum([gu.get_roles_score(gu.get_roles(tgt_orig, r1_tgt, s_id)) for s_id in s_ids_to_add])
+                r2_roles_score = sum([gu.get_roles_score(gu.get_roles(tgt_orig, r1_tgt, s_id)) for s_id in s_ids_to_add])
+                if r1_roles_score + r2_roles_score < 0:
+                    dir = -dir
+
+                # 2. Find the center
+                cn_existing_lts: List[be.SpeciesLayout] = [
+                    gu.get_predicted_layout(g_res, nodes_mapping.getSourceMapping(cn_id)[0]) for cn_id in cn_existing]
+                center = sum([lt.get_center() for lt in cn_existing_lts]) / len(cn_existing)
+                if len(cn_existing) == 1:
+                    # If there is only one existing species, the center lies in that species which would then surely overlap
+                    # while laying on the perpendicular line so we can immediately shift it
+                    center += settings.render.optimal_neighboring_species_dist * dir
+
+                # 3. Git bounding boxes of existing species (forbidden regions)
+                forbidden = [lt.get_bb().get() for lt in cn_existing_lts]
+
+                # 4. Get positions on the line around the center
+                # We should optimally start at the side which is closer to the tentative "side" of the not mapped species.
+                # This means that, for example, if there exists a connecting species s1 which is a reactant of r1 and r2 and
+                # r1 go horizontally from let to right and the not mapped species s1 is in the product role in r1 and r2 then
+                # s2 needs to be laid out right of s1. If these are the only connecting species, length of poss will be 1 and
+                # the position needs to be right of s1. Therefore, the dir vector needs to be in the correct direction.
+                s_id_roles_scores: Dict[str, int] = {}
+
+                for s_id in s_ids_to_add:
+                    roles1: List[be.SPECIES_ROLE] = gu.get_roles(tgt_orig, r1_tgt, s_id)
+                    roles2: List[be.SPECIES_ROLE] = gu.get_roles(tgt_orig, r2_tgt, s_id)
+                    s_id_roles_scores[s_id] = gu.get_roles_score(roles1) + gu.get_roles_score(roles2)
+
+                sum_roles_score: int = sum(list(s_id_roles_scores.values()))
+                if sum_roles_score < 0:
+                    # Majority of the new connecting species are in the reactant role and since the dir vector
+                    # points toward product roles we need to switch the direction so that first will be laid out reactants
+                    # (if there is space)
+                    dir -= dir
+
+                # Sort the species from reactants to products since the positions will be assigned in this order
+                # and they will go from "left" to "right" with respect to the center and direction vector
+                aux_s_ids_to_add = [x[0] for x in sorted(s_id_roles_scores.items(), key=lambda x: x[1])]
+                poss = layout_on_line(s_ids=aux_s_ids_to_add, center=center, dir=dir, forbidden=forbidden)
+
+                # 5. Swap positions to minimize number of line overlaps
+                # TODO
         else:
             p_r1 = get_node_pos(g_res.nodes()[r1_tgt])
             p_r2 = get_node_pos(g_res.nodes()[r2_tgt])
-- 
GitLab