Merge branch 'master' into 'xcos-layout' 13/18813/1 xcos-layout
Clément DAVID [Fri, 23 Dec 2016 07:25:54 +0000 (08:25 +0100)]
Change-Id: I08059fe94df60d9b68962127fa087b08a81ad8a2

32 files changed:
scilab/CHANGES.md
scilab/CHANGES_6.0.0 [new file with mode: 0644]
scilab/modules/core/includes/version.h.in
scilab/modules/core/includes/version.h.vc
scilab/modules/helptools/images/xcos_auto_position_split_block_en_US.png [new file with mode: 0644]
scilab/modules/helptools/images/xcos_auto_position_split_block_fr_FR.png [new file with mode: 0644]
scilab/modules/helptools/images/xcos_link_optimal_en_US.png [new file with mode: 0644]
scilab/modules/helptools/images/xcos_link_optimal_fr_FR.png [new file with mode: 0644]
scilab/modules/helptools/images/xcos_menu_format_auto_position.png [new file with mode: 0644]
scilab/modules/helptools/images/xcos_menu_format_link_style.png
scilab/modules/xcos/help/en_US/xcos_menu_entries.xml
scilab/modules/xcos/help/fr_FR/xcos_menu_entries.xml
scilab/modules/xcos/help/gui/xcos_menu_entries/en_US/xcos_menu_format_auto_position.png [new file with mode: 0644]
scilab/modules/xcos/help/gui/xcos_menu_entries/en_US/xcos_menu_format_link_style.png
scilab/modules/xcos/help/gui/xcos_menu_entries/fr_FR/xcos_menu_format_auto_position.png [new file with mode: 0644]
scilab/modules/xcos/help/gui/xcos_menu_entries/fr_FR/xcos_menu_format_link_style.png
scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_auto_position_split_block_en_US.png [new file with mode: 0644]
scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_link_optimal_en_US.png [new file with mode: 0644]
scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_auto_position_split_block_fr_FR.png [new file with mode: 0644]
scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_link_optimal_fr_FR.png [new file with mode: 0644]
scilab/modules/xcos/locales/fr_FR.po
scilab/modules/xcos/locales/xcos.pot
scilab/modules/xcos/src/java/org/scilab/modules/xcos/XcosTab.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/block/BasicBlock.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/block/SplitBlock.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/block/actions/AutoPositionSplitBlockAction.java [new file with mode: 0644]
scilab/modules/xcos/src/java/org/scilab/modules/xcos/link/BasicLink.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/link/actions/StyleOptimalAction.java [new file with mode: 0755]
scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/BlockAutoPositionUtils.java [new file with mode: 0644]
scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java
scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosRoute.java [new file with mode: 0755]
scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosRouteUtils.java [new file with mode: 0755]

index f7f9365..0113d5c 100644 (file)
@@ -248,6 +248,8 @@ The memory usage on diagram edition is also slightly reduced.
 Scilab 5.5.2 is able to open the newly saved files, but the ports have to be repositioned manually.
 * Implicit fixed-size step ODE solver added: Crank-Nicolson 2(3).
 Added to the CVode package, it also benefits from the CVode rootfinding feature.
+* Added a new link style (`Optimal`) for automatically finding the optimal route.
+* Automatically reposition split blocks for better-looking layout.
 * Block modifications :
   - `INVBLK`: add a divide by zero parameter to ignore the error
   - `PRODUCT`: add a divide by zero parameter to ignore the error
diff --git a/scilab/CHANGES_6.0.0 b/scilab/CHANGES_6.0.0
new file mode 100644 (file)
index 0000000..17fac6a
--- /dev/null
@@ -0,0 +1,8 @@
+                     Changes between version 5.5.2 and 6.0.0-beta-1
+                     ==============================================
+
+Xcos
+====
+
+* Optimal - new link style for avoiding links crossing the blocks.
+
index 81b3192..0de0a08 100644 (file)
@@ -18,8 +18,8 @@
 #define SCI_VERSION_MAJOR @SCILAB_VERSION_MAJOR@
 #define SCI_VERSION_MINOR @SCILAB_VERSION_MINOR@
 #define SCI_VERSION_MAINTENANCE @SCILAB_VERSION_MAINTENANCE@
-#define SCI_VERSION_STRING "scilab-branch-master"
-#define SCI_VERSION_WIDE_STRING L"scilab-branch-master"
+#define SCI_VERSION_STRING "scilab-branch-xcos-layout"
+#define SCI_VERSION_WIDE_STRING L"scilab-branch-xcos-layout"
 /* SCI_VERSION_REVISION --> hash key commit */
 #define SCI_VERSION_REVISION 0
 #define SCI_VERSION_TIMESTAMP 0
@@ -29,7 +29,7 @@ void disp_scilab_version(void);
 /* for compatibility */
 /* Deprecated */
 #define SCI_VERSION SCI_VERSION_STRING
-#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-master"
+#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-xcos-layout"
 
 #endif
 /*--------------------------------------------------------------------------*/
index f916a8f..e1baba8 100644 (file)
@@ -18,8 +18,8 @@
 #define SCI_VERSION_MAJOR 6
 #define SCI_VERSION_MINOR 0
 #define SCI_VERSION_MAINTENANCE 0
-#define SCI_VERSION_STRING "scilab-branch-master"
-#define SCI_VERSION_WIDE_STRING L"scilab-branch-master"
+#define SCI_VERSION_STRING "scilab-branch-xcos-layout"
+#define SCI_VERSION_WIDE_STRING L"scilab-branch-xcos-layout"
 /* SCI_VERSION_REVISION --> hash key commit */
 #define SCI_VERSION_REVISION 0
 #define SCI_VERSION_TIMESTAMP 0
@@ -29,7 +29,7 @@ void disp_scilab_version(void);
 /* for compatibility */
 /* Deprecated */
 #define SCI_VERSION SCI_VERSION_STRING
-#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-master"
+#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-xcos-layout"
 
 #endif
 /*--------------------------------------------------------------------------*/
diff --git a/scilab/modules/helptools/images/xcos_auto_position_split_block_en_US.png b/scilab/modules/helptools/images/xcos_auto_position_split_block_en_US.png
new file mode 100644 (file)
index 0000000..de44432
Binary files /dev/null and b/scilab/modules/helptools/images/xcos_auto_position_split_block_en_US.png differ
diff --git a/scilab/modules/helptools/images/xcos_auto_position_split_block_fr_FR.png b/scilab/modules/helptools/images/xcos_auto_position_split_block_fr_FR.png
new file mode 100644 (file)
index 0000000..de44432
Binary files /dev/null and b/scilab/modules/helptools/images/xcos_auto_position_split_block_fr_FR.png differ
diff --git a/scilab/modules/helptools/images/xcos_link_optimal_en_US.png b/scilab/modules/helptools/images/xcos_link_optimal_en_US.png
new file mode 100644 (file)
index 0000000..8c2ba81
Binary files /dev/null and b/scilab/modules/helptools/images/xcos_link_optimal_en_US.png differ
diff --git a/scilab/modules/helptools/images/xcos_link_optimal_fr_FR.png b/scilab/modules/helptools/images/xcos_link_optimal_fr_FR.png
new file mode 100644 (file)
index 0000000..8c2ba81
Binary files /dev/null and b/scilab/modules/helptools/images/xcos_link_optimal_fr_FR.png differ
diff --git a/scilab/modules/helptools/images/xcos_menu_format_auto_position.png b/scilab/modules/helptools/images/xcos_menu_format_auto_position.png
new file mode 100644 (file)
index 0000000..9ba676d
Binary files /dev/null and b/scilab/modules/helptools/images/xcos_menu_format_auto_position.png differ
index 2e246a3..6a06eab 100644 (file)
Binary files a/scilab/modules/helptools/images/xcos_menu_format_link_style.png and b/scilab/modules/helptools/images/xcos_menu_format_link_style.png differ
index 40794cc..fa9b4e0 100644 (file)
             </listitem>
             <listitem>
                 <para>
+                    <emphasis role="bold">Format:Auto-Position</emphasis>
+                </para>
+                <para/>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata align="center" fileref="../gui/xcos_menu_entries/en_US/xcos_menu_format_auto_position.png"/>
+                    </imageobject>
+                </mediaobject>
+                <para/>
+                <para>
+                    This menu allows to change the position of the block.
+                </para>
+                <para>
+                    First select the block(s) and select the appropriate menu item or use the shortcuts
+                    (<emphasis>P</emphasis>).
+                    The following list shows the results obtained.
+                </para>
+                <itemizedlist>
+                    <listitem>
+                        <para>
+                            Split Block (<emphasis>P</emphasis>)
+                        </para>
+                        <mediaobject>
+                            <imageobject>
+                                <imagedata fileref="../images/xcos_menu_entries/en_US/xcos_auto_position_split_block_en_US.png"/>
+                            </imageobject>
+                        </mediaobject>
+                    </listitem>
+                </itemizedlist>
+            </listitem>
+            <listitem>
+                <para>
                     <emphasis role="bold">Format:Link Style</emphasis>
                 </para>
                 <para/>
                 </para>
                 <para>
                     First select the link and select the appropriate menu item or use the shortcuts
-                    (<emphasis>H</emphasis>), <emphasis>S</emphasis>, <emphasis>V</emphasis>).
+                    (<emphasis>H</emphasis>), (<emphasis>S</emphasis>), (<emphasis>V</emphasis>), (<emphasis>O</emphasis>).
                     The following list shows the results obtained.
                 </para>
                 <itemizedlist>
                             </imageobject>
                         </mediaobject>
                     </listitem>
+                    <listitem>
+                        <para>
+                            Optimal (<emphasis>O</emphasis>)
+                        </para>
+                        <mediaobject>
+                            <imageobject>
+                                <imagedata fileref="../images/xcos_menu_entries/en_US/xcos_link_optimal_en_US.png"/>
+                            </imageobject>
+                        </mediaobject>
+                    </listitem>
                 </itemizedlist>
             </listitem>
             <listitem>
index 91cbc79..63bf295 100644 (file)
             </listitem>
             <listitem>
                 <para>
+                    <emphasis role="bold">Format : Auto-positionnement</emphasis>
+                </para>
+                <para/>
+                <mediaobject>
+                    <imageobject>
+                        <imagedata align="center" fileref="../gui/xcos_menu_entries/fr_FR/xcos_menu_format_auto_position.png"/>
+                    </imageobject>
+                </mediaobject>
+                <para/>
+                <para>
+                    Ce menu permet de modifier automatiquement la position des blocs.
+                </para>
+                <para>
+                    Sélectionner le(s) bloc(s) puis le menu adéquat ou utiliser le raccourci
+                    (<emphasis>P</emphasis>).
+                    La liste suivante montre les résultats obtenus.
+                </para>
+                <itemizedlist>
+                    <listitem>
+                        <para>
+                            Bloc split (<emphasis>P</emphasis>)
+                        </para>
+                        <mediaobject>
+                            <imageobject>
+                                <imagedata fileref="../images/xcos_menu_entries/fr_FR/xcos_auto_position_split_block_fr_FR.png"/>
+                            </imageobject>
+                        </mediaobject>
+                    </listitem>
+                </itemizedlist>
+            </listitem>
+            <listitem>
+                <para>
                     <emphasis role="bold">Format : Style de liaison</emphasis>
                 </para>
                 <para/>
                             </imageobject>
                         </mediaobject>
                     </listitem>
+                    <listitem>
+                        <para>Optimal</para>
+                        <mediaobject>
+                            <imageobject>
+                                <imagedata fileref="../images/xcos_menu_entries/fr_FR/xcos_link_optimal_fr_FR.png"/>
+                            </imageobject>
+                        </mediaobject>
+                    </listitem>
                 </itemizedlist>
                 <para/>
             </listitem>
diff --git a/scilab/modules/xcos/help/gui/xcos_menu_entries/en_US/xcos_menu_format_auto_position.png b/scilab/modules/xcos/help/gui/xcos_menu_entries/en_US/xcos_menu_format_auto_position.png
new file mode 100644 (file)
index 0000000..9ba676d
Binary files /dev/null and b/scilab/modules/xcos/help/gui/xcos_menu_entries/en_US/xcos_menu_format_auto_position.png differ
index 2e246a3..d78e90d 100644 (file)
Binary files a/scilab/modules/xcos/help/gui/xcos_menu_entries/en_US/xcos_menu_format_link_style.png and b/scilab/modules/xcos/help/gui/xcos_menu_entries/en_US/xcos_menu_format_link_style.png differ
diff --git a/scilab/modules/xcos/help/gui/xcos_menu_entries/fr_FR/xcos_menu_format_auto_position.png b/scilab/modules/xcos/help/gui/xcos_menu_entries/fr_FR/xcos_menu_format_auto_position.png
new file mode 100644 (file)
index 0000000..9ba676d
Binary files /dev/null and b/scilab/modules/xcos/help/gui/xcos_menu_entries/fr_FR/xcos_menu_format_auto_position.png differ
index 19c5a41..6a06eab 100644 (file)
Binary files a/scilab/modules/xcos/help/gui/xcos_menu_entries/fr_FR/xcos_menu_format_link_style.png and b/scilab/modules/xcos/help/gui/xcos_menu_entries/fr_FR/xcos_menu_format_link_style.png differ
diff --git a/scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_auto_position_split_block_en_US.png b/scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_auto_position_split_block_en_US.png
new file mode 100644 (file)
index 0000000..de44432
Binary files /dev/null and b/scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_auto_position_split_block_en_US.png differ
diff --git a/scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_link_optimal_en_US.png b/scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_link_optimal_en_US.png
new file mode 100644 (file)
index 0000000..8c2ba81
Binary files /dev/null and b/scilab/modules/xcos/help/images/xcos_menu_entries/en_US/xcos_link_optimal_en_US.png differ
diff --git a/scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_auto_position_split_block_fr_FR.png b/scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_auto_position_split_block_fr_FR.png
new file mode 100644 (file)
index 0000000..de44432
Binary files /dev/null and b/scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_auto_position_split_block_fr_FR.png differ
diff --git a/scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_link_optimal_fr_FR.png b/scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_link_optimal_fr_FR.png
new file mode 100644 (file)
index 0000000..8c2ba81
Binary files /dev/null and b/scilab/modules/xcos/help/images/xcos_menu_entries/fr_FR/xcos_link_optimal_fr_FR.png differ
index 1fdbecf..027c069 100644 (file)
@@ -673,6 +673,15 @@ msgstr "Couleur de remplissage"
 msgid "Text Color"
 msgstr "Couleur du texte"
 
+msgid "Auto-Position Block"
+msgstr "Auto-positionnement de bloc"
+
+msgid "Split Block"
+msgstr "Bloc Split"
+
+msgid "Auto-Position Split Block"
+msgstr "Auto-positionnement de bloc Split"
+
 msgid "Link Style"
 msgstr "Style de liens"
 
@@ -685,6 +694,9 @@ msgstr "Horizontal"
 msgid "Vertical"
 msgstr "Vertical"
 
+msgid "Optimal"
+msgstr "Optimal"
+
 msgid "Set debugging level (0,1,2,3) <br/> it performs scicos_debug(n)"
 msgstr ""
 "Configurer le niveau de débogage (0, 1, 2, 3) <br/> avec scicos_debug(n)"
index 94ecd5b..00fb995 100644 (file)
@@ -1062,6 +1062,21 @@ msgid "Text Color"
 msgstr ""
 
 #
+# File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 
+msgid "Auto-Position Block"
+msgstr ""
+
+#
+# File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 
+msgid "Split Block"
+msgstr ""
+
+#
+# File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 
+msgid "Auto-Position Split Block"
+msgstr ""
+
+#
 # File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 209
 # File: modules/xcos/src/xcos_fake_xml.c, line: 1831
 # File: modules/xcos/src/xcos_fake_xml.c, line: 1979
@@ -1090,6 +1105,12 @@ msgid "Vertical"
 msgstr ""
 
 #
+# File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 
+# File: modules/xcos/src/xcos_fake_xml.c, line: 
+msgid "Optimal"
+msgstr ""
+
+#
 # File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 214
 msgid "Set debugging level (0,1,2,3) <br/> it performs scicos_debug(n)"
 msgstr ""
index e0c296b..a22ef7d 100644 (file)
@@ -93,6 +93,7 @@ import org.scilab.modules.xcos.actions.ViewGridAction;
 import org.scilab.modules.xcos.actions.ViewViewportAction;
 import org.scilab.modules.xcos.actions.XcosDemonstrationsAction;
 import org.scilab.modules.xcos.actions.XcosDocumentationAction;
+import org.scilab.modules.xcos.block.actions.AutoPositionSplitBlockAction;
 import org.scilab.modules.xcos.block.actions.BlockDocumentationAction;
 import org.scilab.modules.xcos.block.actions.BlockParametersAction;
 import org.scilab.modules.xcos.block.actions.BorderColorAction;
@@ -114,6 +115,7 @@ import org.scilab.modules.xcos.configuration.model.DocumentType;
 import org.scilab.modules.xcos.configuration.utils.ConfigurationConstants;
 import org.scilab.modules.xcos.graph.XcosDiagram;
 import org.scilab.modules.xcos.link.actions.StyleHorizontalAction;
+import org.scilab.modules.xcos.link.actions.StyleOptimalAction;
 import org.scilab.modules.xcos.link.actions.StyleStraightAction;
 import org.scilab.modules.xcos.link.actions.StyleVerticalAction;
 import org.scilab.modules.xcos.palette.actions.ViewPaletteBrowserAction;
@@ -144,6 +146,7 @@ public class XcosTab extends SwingScilabDockablePanel implements SimpleTab {
     private Menu simulate;
     private Menu format;
     private Menu alignMenu;
+    private Menu blockPosition;
     private Menu linkStyle;
     private Menu tools;
     private Menu help;
@@ -335,9 +338,9 @@ public class XcosTab extends SwingScilabDockablePanel implements SimpleTab {
             BarUpdater.updateBars(tab.getParentWindowId(), tab.getMenuBar(), tab.getToolBar(), tab.getInfoBar(), tab.getName(), tab.getWindowIcon());
         }
 
-        ClosingOperationsManager.addDependencyWithRoot((SwingScilabDockablePanel) tab);
-        ClosingOperationsManager.registerClosingOperation((SwingScilabDockablePanel) tab, new ClosingOperation(graph));
-        WindowsConfigurationManager.registerEndedRestoration((SwingScilabDockablePanel) tab, new EndedRestoration(graph));
+        ClosingOperationsManager.addDependencyWithRoot(tab);
+        ClosingOperationsManager.registerClosingOperation(tab, new ClosingOperation(graph));
+        WindowsConfigurationManager.registerEndedRestoration(tab, new EndedRestoration(graph));
     }
 
     /*
@@ -492,11 +495,18 @@ public class XcosTab extends SwingScilabDockablePanel implements SimpleTab {
         format.add(FilledColorAction.createMenu(diagram));
         format.addSeparator();
 
+        blockPosition = ScilabMenu.createMenu();
+        blockPosition.setText(XcosMessages.BLOCK_AUTO_POSITION);
+        blockPosition.add(AutoPositionSplitBlockAction.createMenu(diagram));
+        format.add(blockPosition);
+        format.addSeparator();
+
         linkStyle = ScilabMenu.createMenu();
         linkStyle.setText(XcosMessages.LINK_STYLE);
         linkStyle.add(StyleHorizontalAction.createMenu(diagram));
         linkStyle.add(StyleStraightAction.createMenu(diagram));
         linkStyle.add(StyleVerticalAction.createMenu(diagram));
+        linkStyle.add(StyleOptimalAction.createMenu(diagram));
         format.add(linkStyle);
         format.addSeparator();
 
index 2847566..28ae4ae 100644 (file)
@@ -54,6 +54,7 @@ import org.scilab.modules.xcos.Xcos;
 import org.scilab.modules.xcos.XcosTab;
 import org.scilab.modules.xcos.actions.EditFormatAction;
 import org.scilab.modules.xcos.actions.ShowHideShadowAction;
+import org.scilab.modules.xcos.block.actions.AutoPositionSplitBlockAction;
 import org.scilab.modules.xcos.block.actions.BlockDocumentationAction;
 import org.scilab.modules.xcos.block.actions.BlockParametersAction;
 import org.scilab.modules.xcos.block.actions.BorderColorAction;
@@ -756,6 +757,14 @@ public class BasicBlock extends XcosCell implements Serializable {
         /*--- */
         format.addSeparator();
         /*--- */
+        MenuItem sbapMenuItem = AutoPositionSplitBlockAction.createMenu(graph);
+        sbapMenuItem.setText(XcosMessages.BLOCK_AUTO_POSITION_SPLIT_BLOCK_CONTEXTUAL);
+        sbapMenuItem.setEnabled(false);
+        menuList.put(AutoPositionSplitBlockAction.class, sbapMenuItem);
+        format.add(sbapMenuItem);
+        /*--- */
+        format.addSeparator();
+        /*--- */
         if (graph.getSelectionCells().length > 1) {
             format.add(BorderColorAction.createMenu(graph));
             format.add(FilledColorAction.createMenu(graph));
index 8090de3..aebe3df 100644 (file)
  */
 package org.scilab.modules.xcos.block;
 
+import java.util.Map;
 import java.util.logging.Logger;
 
 import org.scilab.modules.xcos.port.BasicPort;
 
 import com.mxgraph.model.mxGeometry;
 import com.mxgraph.model.mxICell;
+
+import org.scilab.modules.graph.actions.base.DefaultAction;
+import org.scilab.modules.gui.menu.Menu;
 import org.scilab.modules.xcos.JavaController;
 import org.scilab.modules.xcos.Kind;
+import org.scilab.modules.xcos.block.actions.AutoPositionSplitBlockAction;
 
 /**
  * A SplitBlock is used on a junction between links.
@@ -42,6 +47,14 @@ public final class SplitBlock extends BasicBlock {
     }
 
     /**
+     * Set the "BAP - Split Block" menu enabled.
+     */
+    @Override
+    protected void customizeMenu(Map<Class<? extends DefaultAction>, Menu> menuList) {
+        menuList.get(AutoPositionSplitBlockAction.class).setEnabled(true);
+    }
+
+    /**
      * Add a port on the block.
      *
      * @param child
diff --git a/scilab/modules/xcos/src/java/org/scilab/modules/xcos/block/actions/AutoPositionSplitBlockAction.java b/scilab/modules/xcos/src/java/org/scilab/modules/xcos/block/actions/AutoPositionSplitBlockAction.java
new file mode 100644 (file)
index 0000000..a2beaa3
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
+ * Copyright (C) 2016 - Chenfeng ZHU
+ *
+ * This file must be used under the terms of the CeCILL.
+ * This source file is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
+ *
+ */
+package org.scilab.modules.xcos.block.actions;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import org.scilab.modules.graph.ScilabComponent;
+import org.scilab.modules.graph.ScilabGraph;
+import org.scilab.modules.graph.actions.base.ActionConstraint;
+import org.scilab.modules.graph.actions.base.DefaultAction;
+import org.scilab.modules.graph.actions.base.VertexSelectionDependantAction;
+import org.scilab.modules.gui.menuitem.MenuItem;
+import org.scilab.modules.xcos.block.SplitBlock;
+import org.scilab.modules.xcos.graph.XcosDiagram;
+import org.scilab.modules.xcos.utils.BlockAutoPositionUtils;
+import org.scilab.modules.xcos.utils.XcosMessages;
+
+import com.mxgraph.model.mxCell;
+import com.mxgraph.util.mxEvent;
+import com.mxgraph.util.mxEventObject;
+import com.mxgraph.view.mxGraphSelectionModel;
+
+/**
+ * SplitBlock auto Position.
+ */
+@SuppressWarnings(value = { "serial" })
+public class AutoPositionSplitBlockAction extends VertexSelectionDependantAction {
+
+    /** Name of the action */
+    public static final String NAME = XcosMessages.BLOCK_AUTO_POSITION_SPLIT_BLOCK;
+    /** Icon name of the action */
+    public static final String SMALL_ICON = "";
+    /** Mnemonic key of the action */
+    public static final int MNEMONIC_KEY = KeyEvent.VK_P;
+    /** Accelerator key for the action */
+    public static final int ACCELERATOR_KEY = 0;
+
+    /**
+     * Default constructor the associated graph
+     *
+     * @param scilabGraph
+     *            the graph to associate
+     */
+    public AutoPositionSplitBlockAction(ScilabGraph scilabGraph) {
+        super(scilabGraph);
+
+        // The MenuItem is enabled only when SplitBlock is selected.
+        if (scilabGraph != null) {
+            SplitBlockSelectionDependantConstraint c = new SplitBlockSelectionDependantConstraint();
+            c.install(this, scilabGraph);
+        }
+    }
+
+    /**
+     * @param scilabGraph
+     * @return menu item
+     */
+    public static MenuItem createMenu(ScilabGraph scilabGraph) {
+        return createMenu(scilabGraph, AutoPositionSplitBlockAction.class);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        XcosDiagram graph = (XcosDiagram) getGraph(e);
+        if (graph.getSelectionCells().length == 0) {
+            return;
+        }
+
+        // action disabled when the cell is edited
+        final ScilabComponent comp = ((ScilabComponent) graph.getAsComponent());
+        if (comp.isEditing()) {
+            return;
+        }
+
+        Object[] cells = graph.getSelectionCells();
+
+        graph.getModel().beginUpdate();
+        try {
+            double scale = graph.getView().getScale();
+            graph.getView().setScale(1.0);
+            BlockAutoPositionUtils.changeSplitBlocksPosition((XcosDiagram) getGraph(null), cells);
+            graph.getView().setScale(scale);
+        } finally {
+            graph.getModel().endUpdate();
+        }
+    }
+
+
+    /**
+     * Enable the selection if there is at least a SplitBlock in the selection.
+     */
+    private final class SplitBlockSelectionDependantConstraint extends ActionConstraint {
+
+        /**
+         * Default constructor
+         */
+        public SplitBlockSelectionDependantConstraint() {
+            super();
+        }
+
+        /**
+         * @param action the action
+         * @param scilabGraph the current graph
+         * @see org.scilab.modules.graph.actions.base.ActionConstraint#install(org.scilab.modules.graph.actions.base.DefaultAction,
+         *      org.scilab.modules.graph.ScilabGraph)
+         */
+        @Override
+        public void install(DefaultAction action, ScilabGraph scilabGraph) {
+            super.install(action, scilabGraph);
+            scilabGraph.getSelectionModel().addListener(mxEvent.UNDO, this);
+        }
+
+        /**
+         * @param sender the sender
+         * @param evt the event
+         * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject)
+         */
+        @Override
+        public void invoke(Object sender, mxEventObject evt) {
+            mxGraphSelectionModel selection = (mxGraphSelectionModel) sender;
+            Object[] cells = selection.getCells();
+            boolean splitblockFound = false;
+            if (cells != null) {
+                for (Object object : cells) {
+                    if (object instanceof mxCell) {
+                        mxCell cell = (mxCell) object;
+                        splitblockFound = (cell instanceof SplitBlock);
+                    }
+                    if (splitblockFound) {
+                        break;
+                    }
+                }
+                setEnabled(splitblockFound);
+            }
+        }
+
+    }
+
+
+}
index 5ff0723..1c4fbb6 100644 (file)
@@ -36,6 +36,7 @@ import org.scilab.modules.xcos.actions.EditFormatAction;
 import org.scilab.modules.xcos.block.actions.BorderColorAction;
 import org.scilab.modules.xcos.graph.model.XcosCell;
 import org.scilab.modules.xcos.link.actions.StyleHorizontalAction;
+import org.scilab.modules.xcos.link.actions.StyleOptimalAction;
 import org.scilab.modules.xcos.link.actions.StyleStraightAction;
 import org.scilab.modules.xcos.link.actions.StyleVerticalAction;
 import org.scilab.modules.xcos.preferences.XcosOptions;
@@ -287,6 +288,7 @@ public abstract class BasicLink extends XcosCell {
         linkStyle.add(StyleHorizontalAction.createMenu(graph));
         linkStyle.add(StyleStraightAction.createMenu(graph));
         linkStyle.add(StyleVerticalAction.createMenu(graph));
+        linkStyle.add(StyleOptimalAction.createMenu(graph));
 
         menu.add(linkStyle);
 
diff --git a/scilab/modules/xcos/src/java/org/scilab/modules/xcos/link/actions/StyleOptimalAction.java b/scilab/modules/xcos/src/java/org/scilab/modules/xcos/link/actions/StyleOptimalAction.java
new file mode 100755 (executable)
index 0000000..70afc4a
--- /dev/null
@@ -0,0 +1,106 @@
+/*\r
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab\r
+ * Copyright (C) 2015 - Chenfeng ZHU\r
+ *\r
+ * This file must be used under the terms of the CeCILL.\r
+ * This source file is licensed as described in the file COPYING, which\r
+ * you should have received as part of this distribution.  The terms\r
+ * are also available at\r
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt\r
+ *\r
+ */\r
+\r
+package org.scilab.modules.xcos.link.actions;\r
+\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.KeyEvent;\r
+\r
+import org.scilab.modules.graph.ScilabComponent;\r
+import org.scilab.modules.graph.ScilabGraph;\r
+import org.scilab.modules.gui.menuitem.MenuItem;\r
+import org.scilab.modules.xcos.graph.XcosDiagram;\r
+import org.scilab.modules.xcos.link.BasicLink;\r
+import org.scilab.modules.xcos.utils.XcosMessages;\r
+import org.scilab.modules.xcos.utils.XcosRoute;\r
+\r
+import com.mxgraph.util.mxConstants;\r
+\r
+/**\r
+ * Implement the set link optimal action.\r
+ */\r
+@SuppressWarnings(value = { "serial" })\r
+public class StyleOptimalAction extends StyleAction {\r
+    /** Name of the action */\r
+    public static final String NAME = XcosMessages.LINK_STYLE_OPTIMAL;\r
+    /** Icon name of the action */\r
+    public static final String SMALL_ICON = "";\r
+    /** Mnemonic key of the action */\r
+    public static final int MNEMONIC_KEY = KeyEvent.VK_O;\r
+\r
+    /**\r
+     * Default constructor the associated graph\r
+     *\r
+     * @param scilabGraph\r
+     *            the graph to associate\r
+     */\r
+    public StyleOptimalAction(ScilabGraph scilabGraph) {\r
+        super(scilabGraph);\r
+    }\r
+\r
+    /**\r
+     * @param scilabGraph\r
+     * @return menu item\r
+     */\r
+    public static MenuItem createMenu(ScilabGraph scilabGraph) {\r
+        return createMenu(scilabGraph, StyleOptimalAction.class);\r
+    }\r
+\r
+    /**\r
+     * Action.\r
+     *\r
+     * @param e\r
+     * @see org.scilab.modules.xcos.link.actions.StyleAction#actionPerformed(java.awt.event.ActionEvent)\r
+     */\r
+    @Override\r
+    public void actionPerformed(ActionEvent e) {\r
+        final XcosDiagram graph = (XcosDiagram) getGraph(e);\r
+\r
+        // action disabled when the cell is edited\r
+        final ScilabComponent comp = ((ScilabComponent) graph.getAsComponent());\r
+        if (comp.isEditing()) {\r
+            return;\r
+        }\r
+\r
+        final Object[] links = getLinks();\r
+\r
+        graph.getModel().beginUpdate();\r
+        try {\r
+            double scale = graph.getView().getScale();\r
+            graph.getView().setScale(1.0);\r
+            graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE, "1", links);\r
+            reset(graph, links);\r
+            this.updateLinkOptimal(graph, links);\r
+            graph.getView().setScale(scale);\r
+        } finally {\r
+            graph.getModel().endUpdate();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Update the style of links one by one.\r
+     *\r
+     * @param graph\r
+     * @param links\r
+     */\r
+    public void updateLinkOptimal(XcosDiagram graph, Object[] links) {\r
+        Object[] allChildCells = graph.getChildCells(graph.getDefaultParent());\r
+        XcosRoute route = new XcosRoute();\r
+        for (Object o : links) {\r
+            if (o instanceof BasicLink) {\r
+                BasicLink link = (BasicLink) o;\r
+                route.updateRoute(link, allChildCells, graph);\r
+            }\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/BlockAutoPositionUtils.java b/scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/BlockAutoPositionUtils.java
new file mode 100644 (file)
index 0000000..d905f46
--- /dev/null
@@ -0,0 +1,1273 @@
+/*
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
+ * Copyright (C) 2016 - Chenfeng ZHU
+ *
+ * This file must be used under the terms of the CeCILL.
+ * This source file is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
+ *
+ */
+package org.scilab.modules.xcos.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.scilab.modules.graph.ScilabGraph;
+import org.scilab.modules.xcos.block.SplitBlock;
+import org.scilab.modules.xcos.graph.XcosDiagram;
+import org.scilab.modules.xcos.graph.swing.handler.SelectionCellsHandler;
+import org.scilab.modules.xcos.link.BasicLink;
+import org.scilab.modules.xcos.port.BasicPort;
+import org.scilab.modules.xcos.port.Orientation;
+
+import com.mxgraph.model.mxCell;
+import com.mxgraph.model.mxGeometry;
+import com.mxgraph.model.mxICell;
+import com.mxgraph.util.mxConstants;
+import com.mxgraph.util.mxPoint;
+
+/**
+ * Provide methods to set the new position for SplitBlock.
+ */
+public abstract class BlockAutoPositionUtils {
+
+    /**
+     * Change the position of the SplitBlocks including their links.
+     *
+     * @param graph
+     * @param cells
+     *            all selected cells
+     */
+    public static void changeSplitBlocksPosition(XcosDiagram graph, Object[] cells) {
+        Object[] all = graph.getChildCells(graph.getDefaultParent());
+        Object[] selectedCells = selectRootSplitBlock(graph, cells);
+        for (Object o : selectedCells) {
+            if (o instanceof SplitBlock) {
+                SplitBlock cell = (SplitBlock) o;
+                if (getSplitBlockNumber(cell) == 1) {
+                    changeSplitBlockPosition(cell, all, graph);
+                } else {
+                    changeSplitBlockPositionMulti(cell, all, graph);
+                }
+            }
+        }
+    }
+
+    /**
+     * Only select the root Split Block.
+     *
+     * @param graph
+     * @param cells
+     *            all selected cells
+     * @return all the root Split Blocks
+     */
+    private static Object[] selectRootSplitBlock(XcosDiagram graph, Object[] cells) {
+        List<Object> list = new ArrayList<>(0);
+        for (Object o : cells) {
+            if (o instanceof SplitBlock) {
+                SplitBlock cell = getRootSplitBlock((SplitBlock) o);
+                if (!list.contains(cell) && !isListContainsCell(list, cell)) {
+                    list.add(cell);
+                }
+            }
+        }
+        return list.toArray();
+    }
+
+    /**
+     * Get the first(root) Split Block in the link where the Split Block is.
+     *
+     * @param splitblock the Split Block
+     * @return the first Split Block
+     */
+    private static SplitBlock getRootSplitBlock(SplitBlock splitblock) {
+        mxICell port = getSplitInLinkPort(splitblock);
+        while ((port != null) && (port.getParent() instanceof SplitBlock)) {
+            port = getSplitInLinkPort(((SplitBlock) port.getParent()));
+        }
+        mxICell edge = port.getEdgeAt(0);
+        mxICell cell = ((mxCell) edge).getTarget();
+        return ((SplitBlock) cell.getParent());
+    }
+
+    /**
+     * Check whether the list contains one split block in the link where the
+     * Split Block is.
+     *
+     * @param list
+     *            a list of Cells
+     * @param splitBlock
+     *            the Split Block
+     * @return <b>true</b> if the list contains the split block.
+     */
+    private static boolean isListContainsCell(List<Object> list, SplitBlock splitBlock) {
+        for (Object o : list) {
+            if (o instanceof SplitBlock) {
+                SplitBlock split = (SplitBlock) o;
+                List<mxICell> listSplit = getAllChildrenSplitBlockByLevel(split);
+                if (listSplit.contains(splitBlock)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get the number of Split Blocks in the link where the Split Block is.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @return the number of Split Blocks
+     */
+    private static int getSplitBlockNumber(SplitBlock splitblock) {
+        SplitBlock root = getRootSplitBlock(splitblock);
+        List<mxICell> list = getAllChildrenSplitBlockByLevel(root);
+        return list.size() + 1;
+    }
+
+    /**
+     * Change the position of the SplitBlock including its links.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @param all
+     *            all cells in graph
+     * @param graph
+     */
+    protected static void changeSplitBlockPosition(SplitBlock splitblock, Object[] all, XcosDiagram graph) {
+        mxICell sourcePort = getSplitInLinkPort(splitblock);
+        mxICell targetPort1 = getSplitLinkPort(splitblock, splitblock.getOut1());
+        mxICell targetPort2 = getSplitLinkPort(splitblock, splitblock.getOut2());
+
+        // get the optimal routes for 2 target ports.
+        Object[] allObstacles = getObstacles(splitblock, all);
+        List<mxPoint> list1 = getRoute(splitblock, sourcePort, targetPort1, allObstacles, graph);
+        List<mxPoint> list2 = getRoute(splitblock, sourcePort, targetPort2, allObstacles, graph);
+
+        // adjust the routes.
+        List<mxICell> listPorts = new ArrayList<>(0);
+        listPorts.add(sourcePort);
+        listPorts.add(targetPort1);
+        listPorts.add(targetPort2);
+        adjustRoutes(list1, list2, allObstacles, listPorts);
+
+        // get the position according to the routes.
+        mxPoint point = getSplitPoint(list1, list2);
+        if (point == null) {
+            // keep it in the original position.
+            return;
+        }
+        updatePortOrientation(splitblock, list1, list2, point);
+
+        // update split block's position and update the corresponding link.
+        mxGeometry splitGeo = (mxGeometry) graph.getModel().getGeometry(splitblock).clone();
+        splitGeo.setX(point.getX() - splitGeo.getWidth() / 2);
+        splitGeo.setY(point.getY() - splitGeo.getHeight() / 2);
+        graph.getModel().setGeometry(splitblock, splitGeo);
+        updateSplitLink(splitblock, allObstacles, graph);
+    }
+
+    /**
+     * Change the position for multiple SplitBlocks including their links.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @param all
+     *            all cells in graph
+     * @param graph
+     */
+    protected static void changeSplitBlockPositionMulti(SplitBlock splitblock, Object[] all, XcosDiagram graph) {
+        adjustSplitBlock(splitblock);
+        mxICell sourcePort = getSplitInLinkPort(splitblock);
+        List<mxICell> listTargetPorts = getSplitAllTargetPort(splitblock);
+        List<mxICell> listSplitBlocks = new ArrayList<>(0);
+        listSplitBlocks.add(splitblock);
+        listSplitBlocks.addAll(getAllChildrenSplitBlockByLevel(splitblock));
+
+        // get all optimal routes for source to each target including the source and target.
+        Map<mxICell, List<mxPoint>> mapRoutes = new HashMap<>(0);
+        List<List<mxPoint>> listRoutes = new ArrayList<>(0);
+        Object[] allObstacles = getObstacles(splitblock, all);
+        XcosRoute util = new XcosRoute();
+        for (mxICell targetPort : listTargetPorts) {
+            List<mxPoint> list = new ArrayList<mxPoint>(0);
+            if (sourcePort != null) {
+                list.add(getPortPosition(sourcePort));
+            }
+            boolean flag = util.computeRoute(sourcePort, targetPort, allObstacles, graph);
+            if (flag) {
+                for (mxPoint point : util.getNonRedundantPoints()) {
+                    // add all points in the route
+                    list.add(new mxPoint(Math.round(point.getX()), Math.round(point.getY())));
+                }
+            } else {
+                // keep it in the original position.
+                return;
+            }
+            if (targetPort != null) {
+                list.add(getPortPosition(targetPort));
+            }
+            mapRoutes.put(targetPort, list);
+            listRoutes.add(list);
+        }
+
+        // adjust the routes.
+        List<mxICell> listPorts = new ArrayList<>(0);
+        listPorts.add(sourcePort);
+        listPorts.addAll(listTargetPorts);
+        adjustRoutes(listRoutes, allObstacles, listPorts);
+
+        // set the new position for each Split Block according to all existing
+        // optimal routes.
+        for (int i = 0; i < listSplitBlocks.size(); i++) {
+            mxICell cell = listSplitBlocks.get(i);
+            SplitBlock split = (SplitBlock) cell;
+            List<mxICell> listTargets = null;
+            if (i == 0) {
+                listTargets = getSplitAllTargetPort(split);
+            } else {
+                for (int j = 0; j < i; j++) {
+                    SplitBlock temp = (SplitBlock) listSplitBlocks.get(j);
+                    // get its connected previous split block.
+                    if (isSplitBlocksConnected(split, temp) != null) {
+                        listTargets = getSplitAllTargetPort(split, temp);
+                        break;
+                    }
+                }
+            }
+            List<List<mxPoint>> listTargetRoutes = new ArrayList<>(0);
+            for (mxICell target : listTargets) {
+                listTargetRoutes.add(mapRoutes.get(target));
+            }
+            mxPoint splitPoint = getSplitPoint(listTargetRoutes);
+            if (splitPoint == null) {
+                // keep it in the original position.
+                return;
+            }
+            mxGeometry splitGeo = (mxGeometry) graph.getModel().getGeometry(split).clone();
+            splitGeo.setX(splitPoint.getX() - splitGeo.getWidth() / 2);
+            splitGeo.setY(splitPoint.getY() - splitGeo.getHeight() / 2);
+            graph.getModel().setGeometry(split, splitGeo);
+        }
+
+        // update the orientation for all ports of each Split Block based on
+        // corresponding routes and position.
+        for (int i = 0; i < listSplitBlocks.size(); i++) {
+            mxICell cell = listSplitBlocks.get(i);
+            SplitBlock split = (SplitBlock) cell;
+            List<mxICell> listTargets = null;
+            BasicPort inPort = null;
+            if (i == 0) {
+                listTargets = getSplitAllTargetPort(split);
+            } else {
+                for (int j = 0; j < i; j++) {
+                    SplitBlock temp = (SplitBlock) listSplitBlocks.get(j);
+                    // get its previous split block.
+                    if (isSplitBlocksConnected(split, temp) != null) {
+                        listTargets = getSplitAllTargetPort(split, temp);
+                        inPort = isSplitBlocksConnected(split, temp);
+                        break;
+                    }
+                }
+            }
+            List<List<mxPoint>> listTargetRoutes = new ArrayList<>(0);
+            for (mxICell target : listTargets) {
+                listTargetRoutes.add(mapRoutes.get(target));
+            }
+            updatePortOrientation(split, listTargetRoutes, graph, inPort);
+        }
+
+        // update the relative links
+        for (mxICell cell : listSplitBlocks) {
+            SplitBlock split = (SplitBlock) cell;
+            updateSplitLink(split, allObstacles, graph);
+        }
+    }
+
+    /**
+     * Get the linked Port of a SplitBlock according to its Input.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @return
+     */
+    private static mxICell getSplitInLinkPort(SplitBlock splitblock) {
+        mxICell cell = null;
+        BasicPort in = splitblock.getIn();
+        mxICell edge = in.getEdgeAt(0);
+        if (edge != null && edge instanceof mxCell) {
+            cell = ((mxCell) edge).getSource();
+            // sometimes the input port of a split block might be source instead of a target.
+            if (cell == in) {
+                cell = ((mxCell) edge).getTarget();
+            }
+        }
+        return cell;
+    }
+
+    /**
+     * Get the linked Port of a SplitBlock according to its Port.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @param port
+     *            the port in the Split Block
+     * @return
+     */
+    private static mxICell getSplitLinkPort(SplitBlock splitblock, BasicPort port) {
+        mxICell cell = null;
+        mxICell edge = port.getEdgeAt(0);
+        if (edge != null && edge instanceof mxCell) {
+            cell = ((mxCell) edge).getTarget();
+            if (cell == port) {
+                cell = ((mxCell) edge).getSource();
+            }
+        }
+        return cell;
+    }
+
+    /**
+     * Check whether two split blocks are connected.
+     *
+     * @param split1
+     *            Split Block 1
+     * @param split2
+     *            Split Block 2
+     * @return the connected port of split1
+     */
+    private static BasicPort isSplitBlocksConnected(SplitBlock split1, SplitBlock split2) {
+        BasicPort in1 = split1.getIn();
+        BasicPort out11 = split1.getOut1();
+        BasicPort out12 = split1.getOut2();
+        BasicPort in2 = split2.getIn();
+        BasicPort out21 = split2.getOut1();
+        BasicPort out22 = split2.getOut2();
+        if ((in1.getEdgeAt(0) == in2.getEdgeAt(0))
+                || (in1.getEdgeAt(0) == out21.getEdgeAt(0))
+                || (in1.getEdgeAt(0) == out22.getEdgeAt(0))) {
+            return in1;
+        } else if ((out11.getEdgeAt(0) == in2.getEdgeAt(0))
+                || (out11.getEdgeAt(0) == out21.getEdgeAt(0))
+                || (out11.getEdgeAt(0) == out22.getEdgeAt(0))) {
+            return out11;
+        } else if ((out12.getEdgeAt(0) == in2.getEdgeAt(0))
+                || (out12.getEdgeAt(0) == out21.getEdgeAt(0))
+                || (out12.getEdgeAt(0) == out22.getEdgeAt(0))) {
+            return out12;
+        }
+        return null;
+    }
+
+    /**
+     * See {@link #getSplitAllTargetPort(SplitBlock, SplitBlock)}.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @return
+     */
+    private static List<mxICell> getSplitAllTargetPort(SplitBlock splitblock) {
+        return getSplitAllTargetPort(splitblock, null);
+    }
+
+    /**
+     * Get all the final target Ports of a root SplitBlock. Add them in an order
+     * based on split blocks' order.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @param previous
+     *            the previous Split Block
+     * @return
+     */
+    private static List<mxICell> getSplitAllTargetPort(SplitBlock splitblock, SplitBlock previous) {
+        List<mxICell> list = new ArrayList<>(0);
+        BasicPort out1 = splitblock.getOut1();
+        BasicPort out2 = splitblock.getOut2();
+        mxICell sourcePort = getSplitInLinkPort(splitblock);
+        mxICell targetPort1 = getSplitLinkPort(splitblock, out1);
+        mxICell targetPort2 = getSplitLinkPort(splitblock, out2);
+
+        // if its linked block was a normal block, add it firstly
+        if (previous != null && !(sourcePort.getParent() instanceof SplitBlock)) {
+            list.add(sourcePort);
+        }
+        if (!(targetPort1.getParent() instanceof SplitBlock)) {
+            list.add(targetPort1);
+        }
+        if (!(targetPort2.getParent() instanceof SplitBlock)) {
+            list.add(targetPort2);
+        }
+
+        // if it was a split block, add its final targets.
+        if (previous != null && sourcePort.getParent() instanceof SplitBlock && previous != sourcePort.getParent()) {
+            list.addAll(getSplitAllTargetPort((SplitBlock) sourcePort.getParent(), splitblock));
+        }
+        if (targetPort1.getParent() instanceof SplitBlock && previous != targetPort1.getParent()) {
+            list.addAll(getSplitAllTargetPort((SplitBlock) targetPort1.getParent(), splitblock));
+        }
+        if (targetPort2.getParent() instanceof SplitBlock && previous != targetPort2.getParent()) {
+            list.addAll(getSplitAllTargetPort((SplitBlock) targetPort2.getParent(), splitblock));
+        }
+
+        return list;
+    }
+
+    /**
+     * See {@link #getAllChildrenSplitBlockByLevel(SplitBlock, List)}
+     *
+     * @param splitblock
+     *            the Split Block
+     * @return
+     */
+    private static List<mxICell> getAllChildrenSplitBlockByLevel(SplitBlock splitblock) {
+        return getAllChildrenSplitBlockByLevel(splitblock, null);
+    }
+
+    /**
+     * Get all the children Split Blocks of a Split Block excluding itself. Add
+     * them in an order according to their level.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @param previous
+     *            the previous Split Block
+     * @return
+     */
+    private static List<mxICell> getAllChildrenSplitBlockByLevel(SplitBlock splitblock, SplitBlock previous) {
+        List<mxICell> listCells = new ArrayList<>(0);
+        BasicPort out1 = splitblock.getOut1();
+        BasicPort out2 = splitblock.getOut2();
+        mxICell sourcePort = getSplitInLinkPort(splitblock);
+        mxICell targetPort1 = getSplitLinkPort(splitblock, out1);
+        mxICell targetPort2 = getSplitLinkPort(splitblock, out2);
+
+        // if its connected block was a Split Block, add these Split Blocks in this level.
+        if ((sourcePort.getParent() instanceof SplitBlock) && (previous != sourcePort.getParent())) {
+            listCells.add(sourcePort.getParent());
+        }
+        if ((targetPort1.getParent() instanceof SplitBlock) && (previous != targetPort1.getParent())) {
+            listCells.add(targetPort1.getParent());
+        }
+        if ((targetPort2.getParent() instanceof SplitBlock) && (previous != targetPort2.getParent())) {
+            listCells.add(targetPort2.getParent());
+        }
+
+        // then add its children.
+        if ((sourcePort.getParent() instanceof SplitBlock) && (previous != sourcePort.getParent())) {
+            listCells.addAll(getAllChildrenSplitBlockByLevel((SplitBlock) sourcePort.getParent(), splitblock));
+        }
+        if ((targetPort1.getParent() instanceof SplitBlock) && (previous != targetPort1.getParent())) {
+            listCells.addAll(getAllChildrenSplitBlockByLevel((SplitBlock) targetPort1.getParent(), splitblock));
+        }
+        if ((targetPort2.getParent() instanceof SplitBlock) && (previous != targetPort2.getParent())) {
+            listCells.addAll(getAllChildrenSplitBlockByLevel((SplitBlock) targetPort2.getParent(), splitblock));
+        }
+
+        return listCells;
+    }
+
+    /**
+     * Get all links on this Split Block.<br/>
+     * Each Split Block has an IN link and two OUT links which would be also the
+     * IN link for its child split block.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @return
+     */
+    private static List<mxICell> getAllLinksOnSplitBlock(SplitBlock splitblock) {
+        List<mxICell> listLinks = new ArrayList<>(0);
+
+        // get all split blocks in this link.
+        List<mxICell> listSplitBlocks = new ArrayList<>(0);
+        listSplitBlocks.add(splitblock);
+        listSplitBlocks.addAll(getAllChildrenSplitBlockByLevel(splitblock));
+
+        // add the 3 links of each split block without duplication.
+        for (mxICell block : listSplitBlocks) {
+            SplitBlock split = (SplitBlock) block;
+            mxICell link1 = split.getIn().getEdgeAt(0);
+            mxICell link2 = split.getOut1().getEdgeAt(0);
+            mxICell link3 = split.getOut2().getEdgeAt(0);
+            if (!listLinks.contains(link1)) {
+                listLinks.add(link1);
+            }
+            if (!listLinks.contains(link2)) {
+                listLinks.add(link2);
+            }
+            if (!listLinks.contains(link3)) {
+                listLinks.add(link3);
+            }
+        }
+        return listLinks;
+    }
+
+    /**
+     * Adjust the Blocks aligned linked to the split block. Only let SplitBlock
+     * aligned to normal Block.
+     *
+     * @param splitblock
+     *            the Split Block
+     */
+    private static void adjustSplitBlock(SplitBlock splitblock) {
+        BasicPort out1 = splitblock.getOut1();
+        BasicPort out2 = splitblock.getOut2();
+        mxICell sourcePort = getSplitInLinkPort(splitblock);
+        mxICell targetPort1 = getSplitLinkPort(splitblock, out1);
+        mxICell targetPort2 = getSplitLinkPort(splitblock, out2);
+        if (sourcePort.getParent() instanceof SplitBlock) {
+            // if it is a Split Block
+            if (!(targetPort1.getParent() instanceof SplitBlock)) {
+                adjustCell(sourcePort, targetPort1);
+            }
+            if (!(targetPort2.getParent() instanceof SplitBlock)) {
+                adjustCell(sourcePort, targetPort2);
+            }
+        }
+        if (targetPort1 instanceof SplitBlock) {
+            if (!(sourcePort.getParent() instanceof SplitBlock)) {
+                adjustCell(targetPort1, sourcePort);
+            }
+        }
+        if (targetPort2 instanceof SplitBlock) {
+            if (!(sourcePort.getParent() instanceof SplitBlock)) {
+                adjustCell(targetPort2, sourcePort);
+            }
+        }
+    }
+
+    /**
+     * Adjust the cell position align to the base one only if their difference
+     * are less than XcosRouteUtils.ALIGN_STRICT_ERROR.
+     *
+     * @param cell
+     *            the cell should be moved
+     * @param cellBase
+     *            the based cell to be aligned to
+     */
+    private static void adjustCell(mxICell cell, mxICell cellBase) {
+        double error = XcosRouteUtils.ALIGN_STRICT_ERROR;
+        mxPoint cellPoint = getPortPosition(cell);
+        mxGeometry cellGeo = cell.getParent().getGeometry();
+        mxPoint cellBasePoint = getPortPosition(cellBase);
+        if (Math.abs(cellPoint.getX() - cellBasePoint.getX()) <= error) {
+            cellGeo.setX(cellBasePoint.getX() - cellGeo.getWidth() / 2);
+        }
+        if (Math.abs(cellPoint.getY() - cellBasePoint.getY()) <= error) {
+            cellGeo.setY(cellBasePoint.getY() - cellGeo.getHeight() / 2);
+        }
+    }
+
+    /**
+     * Adjust the optimal routes and make them aligned with each other when they
+     * are parallel.
+     *
+     * @param list1
+     *            the first route
+     * @param list2
+     *            the second route
+     * @param allObstacles
+     *            all obstacles
+     */
+    private static void adjustRoutes(List<mxPoint> list1, List<mxPoint> list2, Object[] allObstacles, List<mxICell> listPorts) {
+        List<List<mxPoint>> listRoutes = new ArrayList<>(0);
+        listRoutes.add(list1);
+        listRoutes.add(list2);
+        adjustRoutes(listRoutes, allObstacles, listPorts);
+    }
+
+    /**
+     * Adjust the optimal routes and make them aligned with each other when they
+     * are parallel.
+     *
+     * @param listRoutes
+     *            a list of all routes
+     * @param allObstacles
+     *            all obstacles
+     * @param listPorts
+     *            all the source and target ports
+     */
+    private static void adjustRoutes(List<List<mxPoint>> listRoutes, Object[] allObstacles, List<mxICell> listPorts) {
+        // the geometry of all ports.
+        List<mxGeometry> listGeo = new ArrayList<>(0);
+        for (mxICell port : listPorts) {
+            mxGeometry geometry = getPortGeometry(port);
+            listGeo.add(geometry);
+        }
+
+        // compare with every 2 routes. in every routes, there are several segments.
+        // the first two loop is to compare 2 routes
+        // the next two loops is to compare every segments in 2 routes
+        for (int i = 0; i < listRoutes.size() - 1; i++) {
+            List<mxPoint> list1 = listRoutes.get(i);
+            for (int j = i + 1; j < listRoutes.size(); j++) {
+                List<mxPoint> list2 = listRoutes.get(j);
+                for (int m = 0; m < list1.size() - 1; m++) {
+                    mxPoint p11 = list1.get(m);
+                    mxPoint p12 = list1.get(m + 1);
+                    double x11 = p11.getX();
+                    double y11 = p11.getY();
+                    double x12 = p12.getX();
+                    double y12 = p12.getY();
+                    for (int n = 0; n < list2.size() - 1; n++) {
+
+                        // if both are source/target, two lines are not movable.
+                        if ((m == 0 && n == 0)
+                                || ((m == list1.size() - 2) && (n == list2.size() - 2))
+                                || (m == 0 && (n == list2.size() - 2))
+                                || (n == 0 && (m == list1.size() - 2))) {
+                            continue;
+                        }
+
+                        mxPoint p21 = list2.get(n);
+                        mxPoint p22 = list2.get(n + 1);
+                        double x21 = p21.getX();
+                        double y21 = p21.getY();
+                        double x22 = p22.getX();
+                        double y22 = p22.getY();
+                        // if they are already aligned or they are not parallel,
+                        if ((x11 == x12 && x21 == x22 && x11 == x21)
+                                || (y11 == y12 && y21 == y22 && y11 == y21)
+                                || !(XcosRouteUtils.isLineParallel(x11, y11, x12, y12, x21, y21, x22, y22, true))) {
+                            continue;
+                        }
+
+                        if (m == 0 || m == list1.size() - 2) {
+                            // if it is source point or target, line1 is not movable.
+                            mxPoint p20 = list2.get(n - 1);
+                            mxPoint p23 = list2.get(n + 2);
+                            if (x11 == x12) { // the segment is vertical
+                                boolean flag2 = !XcosRouteUtils.checkObstacle(p20.getX(), p20.getY(), x11, y21, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x11, y21, x11, y22, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x11, y22, p23.getX(), p23.getY(), allObstacles);
+                                // if the new points of the segment was in one of the ports
+                                for (mxGeometry geometry : listGeo) {
+                                    if (XcosRouteUtils.checkPointInGeometry(x11, y21, geometry)
+                                            || XcosRouteUtils.checkPointInGeometry(x11, y22, geometry)) {
+                                        flag2 = false;
+                                        break;
+                                    }
+                                }
+                                if (flag2) {
+                                    p21.setX(x11);
+                                    p22.setX(x11);
+                                }
+                            } else if (y11 == y12) { // the segment is horizontal
+                                boolean flag2 = !XcosRouteUtils.checkObstacle(p20.getX(), p20.getY(), x21, y11, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x21, y11, x22, y11, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x22, y11, p23.getX(), p23.getY(), allObstacles);
+                                if (flag2) {
+                                    p21.setY(y11);
+                                    p22.setY(y11);
+                                }
+                            }
+                        } else if (n == 0 || n == list2.size() - 2) {
+                            // if it is source point or target, line2 is not movable.
+                            mxPoint p10 = list1.get(m - 1);
+                            mxPoint p13 = list1.get(m + 2);
+                            if (x11 == x12) { // the segment is vertical
+                                boolean flag1 = !XcosRouteUtils.checkObstacle(p10.getX(), p10.getY(), x21, y11, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x21, y11, x21, y12, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x21, y12, p13.getX(), p13.getY(), allObstacles);
+                                for (mxGeometry geometry : listGeo) {
+                                    if (XcosRouteUtils.checkPointInGeometry(x21, y11, geometry)
+                                            || XcosRouteUtils.checkPointInGeometry(x21, y12, geometry)) {
+                                        flag1 = false;
+                                        break;
+                                    }
+                                }
+                                if (flag1) {
+                                    p11.setX(x21);
+                                    p12.setX(x21);
+                                }
+                            } else if (y11 == y12) { // the segment is horizontal
+                                boolean flag1 = !XcosRouteUtils.checkObstacle(p10.getX(), p10.getY(), x11, y21, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x11, y21, x12, y21, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x12, y21, p13.getX(), p13.getY(), allObstacles);
+                                if (flag1) {
+                                    p11.setY(y21);
+                                    p12.setY(y21);
+                                }
+                            }
+                        } else {
+                            // both are movable.
+                            mxPoint p20 = list2.get(n - 1);
+                            mxPoint p23 = list2.get(n + 2);
+                            mxPoint p10 = list1.get(m - 1);
+                            mxPoint p13 = list1.get(m + 2);
+                            if (x11 == x12) { // the segment is vertical
+                                boolean flag2 = !XcosRouteUtils.checkObstacle(p20.getX(), p20.getY(), x11, y21, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x11, y21, x11, y22, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x11, y22, p23.getX(), p23.getY(), allObstacles);
+                                boolean flag1 = !XcosRouteUtils.checkObstacle(p10.getX(), p10.getY(), x21, y11, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x21, y11, x21, y12, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x21, y12, p13.getX(), p13.getY(), allObstacles);
+                                for (mxGeometry geometry : listGeo) {
+                                    if (XcosRouteUtils.checkPointInGeometry(x11, y21, geometry)
+                                            || XcosRouteUtils.checkPointInGeometry(x11, y22, geometry)) {
+                                        flag2 = false;
+                                    }
+                                    if (XcosRouteUtils.checkPointInGeometry(x21, y11, geometry)
+                                            || XcosRouteUtils.checkPointInGeometry(x21, y12, geometry)) {
+                                        flag1 = false;
+                                    }
+                                    if (!flag1 && !flag2) {
+                                        break;
+                                    }
+                                }
+                                if (flag2) {
+                                    p21.setX(x11);
+                                    p22.setX(x11);
+                                } else if (flag1) {
+                                    p11.setX(x21);
+                                    p12.setX(x21);
+                                }
+                            } else if (y11 == y12) { // the segment is horizontal
+                                boolean flag2 = !XcosRouteUtils.checkObstacle(p20.getX(), p20.getY(), x21, y11, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x21, y11, x22, y11, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x22, y11, p23.getX(), p23.getY(), allObstacles);
+                                boolean flag1 = !XcosRouteUtils.checkObstacle(p10.getX(), p10.getY(), x11, y21, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x11, y21, x12, y21, allObstacles)
+                                        && !XcosRouteUtils.checkObstacle(x12, y21, p13.getX(), p13.getY(), allObstacles);
+                                for (mxGeometry geometry : listGeo) {
+                                    if (XcosRouteUtils.checkPointInGeometry(x21, y11, geometry)
+                                            || XcosRouteUtils.checkPointInGeometry(x22, y11, geometry)) {
+                                        flag2 = false;
+                                    }
+                                    if (XcosRouteUtils.checkPointInGeometry(x11, y21, geometry)
+                                            || XcosRouteUtils.checkPointInGeometry(x12, y21, geometry)) {
+                                        flag1 = false;
+                                    }
+                                    if (!flag1 && !flag2) {
+                                        break;
+                                    }
+                                }
+                                if (flag2) {
+                                    p21.setY(y11);
+                                    p22.setY(y11);
+                                } else if (flag1) {
+                                    p11.setY(y21);
+                                    p12.setY(y21);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the route for the source and the target ignoring the SplitBlock.
+     *
+     * @param source
+     *            the source port
+     * @param target
+     *            the target port
+     * @param all
+     *            all cells in graph
+     * @param graph
+     * @return all turning points in a route including the start and end points
+     */
+    private static List<mxPoint> getRoute(SplitBlock splitblock, mxICell source, mxICell target, Object[] all, XcosDiagram graph) {
+        XcosRoute util = new XcosRoute();
+        // get all obstacles, excluding splitblock itself or its relative link.
+        mxICell link1 = splitblock.getIn().getEdgeAt(0);
+        mxICell link2 = splitblock.getOut1().getEdgeAt(0);
+        mxICell link3 = splitblock.getOut2().getEdgeAt(0);
+        Object[] allOtherCells = util.getAllOtherCells(all, source, target, source.getEdgeAt(0), target.getEdgeAt(0), link1, link2, link3);
+
+        // get the route and add all points in the route including source and target.
+        List<mxPoint> list = new ArrayList<mxPoint>(0);
+        if (source != null) {
+            list.add(getPortPosition(source));
+        }
+        if (util.computeRoute(source, target, allOtherCells, graph)) {
+            for (mxPoint point : util.getNonRedundantPoints()) {
+                list.add(new mxPoint(Math.round(point.getX()), Math.round(point.getY())));
+            }
+        }
+        if (target != null) {
+            list.add(getPortPosition(target));
+        }
+        return list;
+    }
+
+    /**
+     * Get all obstacle blocks.
+     *
+     * @param splitblock
+     *            the split block
+     * @param all
+     *            all cells in graph
+     * @return
+     */
+    private static Object[] getObstacles(SplitBlock splitblock, Object[] all) {
+        List<mxICell> listTargetPorts = getSplitAllTargetPort(splitblock);
+
+        // get all split blocks
+        List<mxICell> listSplitBlocks = new ArrayList<>(0);
+        listSplitBlocks.add(splitblock);
+        listSplitBlocks.addAll(getAllChildrenSplitBlockByLevel(splitblock));
+
+        // all split blocks, all relative links and it source port and all
+        // target ports should not be considered as obstacles.
+        List<mxICell> listLinks = getAllLinksOnSplitBlock(splitblock);
+        List<mxICell> listNotObstable = new ArrayList<>(0);
+        listNotObstable.add(getSplitInLinkPort(splitblock));
+        listNotObstable.addAll(listTargetPorts);
+        listNotObstable.addAll(listSplitBlocks);
+        listNotObstable.addAll(listLinks);
+        Object[] notObstacles = listNotObstable.toArray();
+
+        XcosRoute util = new XcosRoute();
+        Object[] allObstacles = util.getAllOtherCells(all, notObstacles);
+        return allObstacles;
+    }
+
+    /**
+     * Get a port's geometry.
+     *
+     * @param port
+     *            the port
+     * @return
+     */
+    private static mxGeometry getPortGeometry(mxICell port) {
+        mxGeometry geometry = new mxGeometry();
+        if (port == null) {
+            return null;
+        }
+
+        if (port.getParent() instanceof SplitBlock) {
+            // if the port belongs to a split block
+            SplitBlock cell = (SplitBlock) port.getParent();
+            geometry.setX(cell.getGeometry().getX());
+            geometry.setY(cell.getGeometry().getY());
+            geometry.setWidth(cell.getGeometry().getWidth());
+            geometry.setHeight(cell.getGeometry().getHeight());
+        } else {
+            // if the port belongs to a normal block
+            mxGeometry portGeo = port.getGeometry();
+            double portX = portGeo.getX();
+            double portY = portGeo.getY();
+            mxICell parent = port.getParent();
+            mxGeometry parentGeo = parent.getGeometry();
+            if (portGeo.isRelative()) {
+                portX *= parentGeo.getWidth();
+                portY *= parentGeo.getHeight();
+            }
+            geometry.setX(parentGeo.getX() + portX);
+            geometry.setY(parentGeo.getY() + portY);
+            geometry.setWidth(portGeo.getWidth());
+            geometry.setHeight(portGeo.getHeight());
+        }
+        return geometry;
+    }
+
+
+    /**
+     * Get the position of a port.
+     *
+     * @param port
+     *            the port
+     * @return
+     */
+    private static mxPoint getPortPosition(mxICell port) {
+        mxPoint point = new mxPoint();
+        if (port == null) {
+            return null;
+        }
+
+        if (port.getParent() instanceof SplitBlock) {
+            // if the port belongs to a split block, position is the center of the split block,
+            SplitBlock cell = (SplitBlock) port.getParent();
+            point.setX(cell.getGeometry().getCenterX());
+            point.setY(cell.getGeometry().getCenterY());
+        } else {
+            // if the port belongs to a normal block, the position is relative to its parent.
+            mxGeometry portGeo = port.getGeometry();
+            double portX = portGeo.getX();
+            double portY = portGeo.getY();
+            double portW = portGeo.getWidth();
+            double portH = portGeo.getHeight();
+            mxICell parent = port.getParent();
+            mxGeometry parentGeo = parent.getGeometry();
+            if (portGeo.isRelative()) {
+                portX *= parentGeo.getWidth();
+                portY *= parentGeo.getHeight();
+            }
+            point.setX(parentGeo.getX() + portX + portW / 2);
+            point.setY(parentGeo.getY() + portY + portH / 2);
+        }
+
+        // update the point.
+        point.setX(Math.round(point.getX()));
+        point.setY(Math.round(point.getY()));
+        return point;
+    }
+
+    /**
+     * Get the split point for two routes.
+     *
+     * @param list1
+     *            the first route
+     * @param list2
+     *            the second route
+     * @return
+     */
+    private static mxPoint getSplitPoint(List<mxPoint> list1, List<mxPoint> list2) {
+        mxPoint point = null;
+        int num = Math.min(list1.size(), list2.size());
+        if (num <= 1 || !list1.get(0).equals(list2.get(0))) {
+            return null;
+        }
+
+        // check the last intersection of two links
+        int iList1 = 1;
+        int iList2 = 1;
+        for (int i = iList1; i < list1.size(); i++) {
+            for (int j = iList2; j < list2.size(); j++) {
+                mxPoint p1 = list1.get(i - 1);
+                mxPoint p2 = list1.get(i);
+                mxPoint p3 = list2.get(j - 1);
+                mxPoint p4 = list2.get(j);
+                mxPoint p0 = XcosRouteUtils.getIntersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY(), p4.getX(), p4.getY());
+                if (p0 != null) {
+                    iList1 = i;
+                    iList2 = j;
+                    point = (mxPoint) p0.clone();
+                }
+            }
+        }
+        return point;
+    }
+
+    /**
+     * Get the split point for multiple routes.
+     *
+     * @param listRoutes
+     *            the list of routes
+     * @return
+     */
+    private static mxPoint getSplitPoint(List<List<mxPoint>> listRoutes) {
+        List<mxPoint> listAllSplitPoints = new ArrayList<>(0);
+
+        // get all intersections of every 2 routes.
+        for (int i = 0; i < listRoutes.size() - 1; i++) {
+            List<mxPoint> list1 = listRoutes.get(i);
+            for (int j = i + 1; j < listRoutes.size(); j++) {
+                List<mxPoint> list2 = listRoutes.get(j);
+                mxPoint point = getSplitPoint(list1, list2);
+                if (point == null || listAllSplitPoints.contains(point)) {
+                    continue;
+                }
+                listAllSplitPoints.add(point);
+            }
+        }
+
+        // choose the one point which every route will go through.
+        mxPoint splitPoint = null;
+        for (mxPoint point : listAllSplitPoints) {
+            double x = point.getX();
+            double y = point.getY();
+            for (int i = 0; i < listRoutes.size(); i++) {
+                if (!XcosRouteUtils.pointInLink(x, y, listRoutes.get(i))) {
+                    // if there is one route doesn't go through the point
+                    break;
+                }
+                if (i == listRoutes.size() - 1) {
+                    splitPoint = new mxPoint();
+                    splitPoint.setX(x);
+                    splitPoint.setY(y);
+                }
+            }
+        }
+        return splitPoint;
+    }
+
+    /**
+     * Update port orientation.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @param list1
+     *            the first optimal route
+     * @param list2
+     *            the second optimal route
+     * @param splitPoint
+     *            the position for the split block
+     */
+    private static void updatePortOrientation(SplitBlock splitblock, List<mxPoint> list1, List<mxPoint> list2, mxPoint splitPoint) {
+        Orientation orientationIn = getInportOrientation(list1, list2, splitPoint);
+        if (orientationIn != null) {
+            splitblock.getIn().setOrientation(orientationIn);
+        }
+        Orientation orientationOut1 = getPortOrientation(list1, splitPoint);
+        if (orientationOut1 != null) {
+            splitblock.getOut1().setOrientation(orientationOut1);
+        }
+        Orientation orientationOut2 = getPortOrientation(list2, splitPoint);
+        if (orientationOut2 != null) {
+            splitblock.getOut2().setOrientation(orientationOut2);
+        }
+    }
+
+    /**
+     * Get the orientation for the Input Port of a Split Block.<br/>
+     * See {@link #getInputOrientation(List, mxPoint, mxPoint)}.
+     *
+     * @param list1
+     *            the first optimal route including start Port and end Port
+     * @param list2
+     *            the second optimal route including start Port and end Port
+     * @param splitPoint
+     *            the new position for the Split Block
+     * @return
+     */
+    private static Orientation getInportOrientation(List<mxPoint> list1, List<mxPoint> list2, mxPoint splitPoint) {
+        List<List<mxPoint>> list = new ArrayList<>(0);
+        list.add(list1);
+        list.add(list2);
+        mxPoint startPoint = list1.get(0);
+        return getInputOrientation(list, startPoint, splitPoint);
+    }
+
+    /**
+     * Get the orientation for the Input Port of a Split Block.<br/>
+     * There are multiple routes which can contain go through the point. Choose
+     * the one with least turning point and then use it to decide the
+     * orientation of the real income port.
+     *
+     * @param list
+     *            the list of all optimal routes
+     * @param startPoint
+     *            the start point
+     * @param splitPoint
+     *            the new position for the Split Block
+     * @return
+     */
+    private static Orientation getInputOrientation(List<List<mxPoint>> list, mxPoint startPoint, mxPoint splitPoint) {
+        int[] turning = new int[list.size()];
+        Orientation[] orientation = new Orientation[list.size()];
+        double x = splitPoint.getX();
+        double y = splitPoint.getY();
+        double xStart = startPoint.getX();
+        double yStart = startPoint.getY();
+
+        for (int p = 0; p < list.size(); p++) {
+            List<mxPoint> list1 = list.get(p);
+            int num1 = list1.size();
+            if (num1 <= 1) {
+                continue;
+            }
+
+            // calculate the number of turning points before the route goes through the point.
+            for (int i = 1, count = 0; i < num1; i++, count++) {
+                mxPoint p0 = list1.get(i - 1);
+                mxPoint p1 = list1.get(i);
+                double x0 = p0.getX();
+                double y0 = p0.getY();
+                double x1 = p1.getX();
+                double y1 = p1.getY();
+
+                // start from the start point,
+                if (XcosRouteUtils.pointInLineSegment(xStart, yStart, x0, y0, x1, y1)) {
+                    count = 0;
+                }
+
+                // if the point is in this segment,
+                if (XcosRouteUtils.pointInLineSegment(x, y, x0, y0, x1, y1)) {
+                    turning[p] = count;
+                    if (x1 == x0 && y1 > y0) { // segment: south
+                        orientation[p] = Orientation.NORTH;
+                    } else if (x1 == x0 && y1 < y0) { // segment: north
+                        orientation[p] = Orientation.SOUTH;
+                    } else if (y1 == y0 && x1 > x0) { // segment: east
+                        orientation[p] = Orientation.WEST;
+                    } else if (y1 == y0 && x1 < x0) { // segment: west
+                        orientation[p] = Orientation.EAST;
+                    }
+                    break;
+                }
+            }
+        }
+
+        // choose the one with least turning point.
+        int index = 0;
+        int tmp = turning[0];
+        for (int i = 1; i < turning.length; i++) {
+            if (turning[i] < tmp) {
+                tmp = turning[i];
+                index = i;
+            }
+        }
+        return orientation[index];
+    }
+
+    /**
+     * Get the orientation for the one Port of a Split Block according to
+     * its optimal route.
+     *
+     * @param list
+     *            the optimal route including start Port and end Port
+     * @param splitPoint
+     *            the new position for the Split Block
+     * @return
+     */
+    private static Orientation getPortOrientation(List<mxPoint> list, mxPoint splitPoint) {
+        double x = splitPoint.getX();
+        double y = splitPoint.getY();
+        int num = list.size();
+        if (num <= 1) {
+            return null;
+        }
+
+        for (int i = 1; i < num; i++) {
+            mxPoint p0 = list.get(i - 1);
+            mxPoint p1 = list.get(i);
+            double x0 = p0.getX();
+            double y0 = p0.getY();
+            double x1 = p1.getX();
+            double y1 = p1.getY();
+
+            // if the point is in this segment,
+            if (XcosRouteUtils.pointInLineSegment(x, y, x0, y0, x1, y1)) {
+                // if the point is in the next turning point,
+                if (x == x1 && y == y1 && i + 1 != num) {
+                    mxPoint p2 = list.get(i + 1);
+                    double x2 = p2.getX();
+                    double y2 = p2.getY();
+                    if (x == x2 && y < y2) { // segment: south
+                        return Orientation.SOUTH;
+                    } else if (x == x2 && y > y2) { // segment: north
+                        return Orientation.NORTH;
+                    } else if (y == y2 && x < x2) { // segment: east
+                        return Orientation.EAST;
+                    } else if (y == y2 && x > x2) { // segment: west
+                        return Orientation.WEST;
+                    }
+                }
+
+                if (x1 == x0 && y1 > y0) { // segment: south
+                    return Orientation.SOUTH;
+                } else if (x1 == x0 && y1 < y0) { // segment: north
+                    return Orientation.NORTH;
+                } else if (y1 == y0 && x1 > x0) { // segment: east
+                    return Orientation.EAST;
+                } else if (y1 == y0 && x1 < x0) { // segment: west
+                    return Orientation.WEST;
+                }
+                break;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Update SplitBlock's input port and output ports.
+     *
+     * @param split
+     *            the Split Block
+     * @param listRoutes
+     *            the list of routes
+     * @param graph
+     * @param input
+     *            the input port of the Split Block
+     */
+    private static void updatePortOrientation(SplitBlock split, List<List<mxPoint>> listRoutes, XcosDiagram graph, BasicPort input) {
+        mxGeometry splitGeo = graph.getModel().getGeometry(split);
+        mxPoint splitPoint = new mxPoint(splitGeo.getCenterX(), splitGeo.getCenterY());
+
+        // get the positions of the ports which connect to the corresponding ports of split block.
+        BasicPort inport = split.getIn();
+        BasicPort outport1 = split.getOut1();
+        BasicPort outport2 = split.getOut2();
+        mxPoint inPosition = getPortPosition(getSplitLinkPort(split, inport));
+        mxPoint out1Position = getPortPosition(getSplitLinkPort(split, outport1));
+        mxPoint out2Position = getPortPosition(getSplitLinkPort(split, outport2));
+
+        // set the orientation for each ports according to which is the real income port.
+        if (input == null || input == inport) {
+            // if the previous split block connects to the input port or it is the starting split block
+            // get Input Port Orientation
+            inport.setOrientation(getInputOrientation(listRoutes, inPosition, splitPoint));
+            // get OutPut Port Orientation
+            for (int p = 0; p < listRoutes.size(); p++) {
+                List<mxPoint> list1 = listRoutes.get(p);
+                if (XcosRouteUtils.pointInLink(out1Position.getX(), out1Position.getY(), list1)) {
+                    outport1.setOrientation(getPortOrientation(list1, splitPoint));
+                }
+                if (XcosRouteUtils.pointInLink(out2Position.getX(), out2Position.getY(), list1)) {
+                    outport2.setOrientation(getPortOrientation(list1, splitPoint));
+                }
+            }
+        } else if (input == outport1) {
+            // if the previous split block connects to the output port 1
+            outport1.setOrientation(getInputOrientation(listRoutes, out1Position, splitPoint));
+            for (int p = 0; p < listRoutes.size(); p++) {
+                List<mxPoint> list1 = listRoutes.get(p);
+                if (XcosRouteUtils.pointInLink(inPosition.getX(), inPosition.getY(), list1)) {
+                    inport.setOrientation(getPortOrientation(list1, splitPoint));
+                }
+                if (XcosRouteUtils.pointInLink(out2Position.getX(), out2Position.getY(), list1)) {
+                    outport2.setOrientation(getPortOrientation(list1, splitPoint));
+                }
+            }
+        } else if (input == outport2) {
+            // if the previous split block connects to the output port 2
+            outport2.setOrientation(getInputOrientation(listRoutes, out2Position, splitPoint));
+            for (int p = 0; p < listRoutes.size(); p++) {
+                List<mxPoint> list1 = listRoutes.get(p);
+                if (XcosRouteUtils.pointInLink(out1Position.getX(), out1Position.getY(), list1)) {
+                    outport1.setOrientation(getPortOrientation(list1, splitPoint));
+                }
+                if (XcosRouteUtils.pointInLink(inPosition.getX(), inPosition.getY(), list1)) {
+                    inport.setOrientation(getPortOrientation(list1, splitPoint));
+                }
+            }
+        }
+    }
+
+    /**
+     * Update the links' style of a SplitBlock.
+     *
+     * @param splitblock
+     *            the Split Block
+     * @param all
+     *            all the cells in graph
+     * @param graph
+     */
+    private static void updateSplitLink(SplitBlock splitblock, Object[] all, XcosDiagram graph) {
+        XcosRoute route = new XcosRoute();
+        BasicLink linkIn = (BasicLink) splitblock.getIn().getEdgeAt(0);
+        BasicLink linkOut1 = (BasicLink) splitblock.getOut1().getEdgeAt(0);
+        BasicLink linkOut2 = (BasicLink) splitblock.getOut2().getEdgeAt(0);
+        boolean lockPort = true;
+        reset(graph, linkIn);
+        reset(graph, linkOut1);
+        reset(graph, linkOut2);
+        graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE, "1", new BasicLink[] { linkIn, linkOut1, linkOut2 });
+        route.updateRoute(linkIn, all, graph, lockPort);
+        route.updateRoute(linkOut1, all, graph, lockPort);
+        route.updateRoute(linkOut2, all, graph, lockPort);
+    }
+
+    /**
+     * Reset the link.
+     *
+     * @param graph
+     * @param edge
+     */
+    private static void reset(final ScilabGraph graph, final Object edge) {
+        final SelectionCellsHandler selectionCellsHandler = (SelectionCellsHandler) graph.getAsComponent()
+                .getSelectionCellsHandler();
+        graph.resetEdge(edge);
+        selectionCellsHandler.clearCellHandler(edge);
+    }
+
+}
index 5b46466..db55f5f 100644 (file)
@@ -311,6 +311,12 @@ public final class XcosMessages {
     public static final String TOOLTIP_LINK_LABEL = Messages.gettext("Label: ");
     public static final String TOOLTIP_LINK_STYLE = Messages.gettext("Style: ");
 
+    /* Automatic Layout */
+    public static final String LINK_STYLE_OPTIMAL = Messages.gettext("Optimal");
+    public static final String BLOCK_AUTO_POSITION = Messages.gettext("Auto-Position Block");
+    public static final String BLOCK_AUTO_POSITION_SPLIT_BLOCK = Messages.gettext("Split Block");
+    public static final String BLOCK_AUTO_POSITION_SPLIT_BLOCK_CONTEXTUAL = Messages.gettext("Auto-Position Split Block");
+
     // CSON: JavadocVariable
     // CSON: LineLength
     // CSON: MultipleStringLiterals
diff --git a/scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosRoute.java b/scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosRoute.java
new file mode 100755 (executable)
index 0000000..ac8c617
--- /dev/null
@@ -0,0 +1,517 @@
+/*\r
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab\r
+ * Copyright (C) 2015 - Chenfeng ZHU\r
+ *\r
+ * This file must be used under the terms of the CeCILL.\r
+ * This source file is licensed as described in the file COPYING, which\r
+ * you should have received as part of this distribution.  The terms\r
+ * are also available at\r
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt\r
+ *\r
+ */\r
+\r
+package org.scilab.modules.xcos.utils;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import org.scilab.modules.xcos.block.BasicBlock;\r
+import org.scilab.modules.xcos.block.SplitBlock;\r
+import org.scilab.modules.xcos.graph.XcosDiagram;\r
+import org.scilab.modules.xcos.link.BasicLink;\r
+import org.scilab.modules.xcos.port.BasicPort;\r
+import org.scilab.modules.xcos.port.Orientation;\r
+import org.scilab.modules.xcos.port.command.CommandPort;\r
+import org.scilab.modules.xcos.port.control.ControlPort;\r
+import org.scilab.modules.xcos.port.input.InputPort;\r
+import org.scilab.modules.xcos.port.output.OutputPort;\r
+\r
+import com.mxgraph.model.mxGeometry;\r
+import com.mxgraph.model.mxGraphModel;\r
+import com.mxgraph.model.mxICell;\r
+import com.mxgraph.util.mxPoint;\r
+import com.mxgraph.view.mxCellState;\r
+\r
+public class XcosRoute {\r
+\r
+    private List<mxPoint> listRoute = new ArrayList<mxPoint>(0);\r
+    // if it is true, the position of the port will not be changed.\r
+    private boolean lockPortPosition = false;\r
+\r
+    public  List<mxPoint> getList() {\r
+        return listRoute;\r
+    }\r
+\r
+    /**\r
+     * Update the Edge.\r
+     *\r
+     * @param link\r
+     * @param allCells\r
+     * @param graph\r
+     */\r
+    public void updateRoute(BasicLink link, Object[] allCells, XcosDiagram graph) {\r
+        this.lockPortPosition = false;\r
+        this.updateRoute(link, allCells, graph, lockPortPosition);\r
+    }\r
+\r
+    /**\r
+     * Update the Edge.\r
+     *\r
+     * @param link\r
+     * @param allCells\r
+     * @param graph\r
+     * @param lockPort\r
+     *            if it is true, there is no need to calculate the orientation.\r
+     */\r
+    public void updateRoute(BasicLink link, Object[] allCells, XcosDiagram graph, boolean lockPort) {\r
+        this.lockPortPosition = lockPort;\r
+        mxICell sourceCell = link.getSource();\r
+        mxICell targetCell = link.getTarget();\r
+        Object[] allOtherCells = getAllOtherCells(allCells, link, sourceCell, targetCell);\r
+        if (sourceCell != null && targetCell != null) {\r
+            boolean isGetRoute = this.computeRoute(sourceCell, targetCell, allOtherCells, graph);\r
+            if (isGetRoute) {\r
+                List<mxPoint> list = this.getNonRedundantPoints();\r
+                mxGeometry geometry = new mxGeometry();\r
+                geometry.setPoints(list);\r
+                ((mxGraphModel) (graph.getModel())).setGeometry(link, geometry);\r
+                listRoute.clear();\r
+            } else if (!lockPort) {\r
+                // if it cannot get the route, change it to straight.\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Get the turning points for the optimal route. If the straight route is the optimal route,\r
+     * return null.\r
+     *\r
+     * @param sourceCell the source port\r
+     * @param targetCell the target port\r
+     * @param allCells\r
+     * @return list of turning points\r
+     */\r
+    protected boolean computeRoute(mxICell sourceCell, mxICell targetCell, Object[] allCells, XcosDiagram graph) {\r
+        listRoute.clear();\r
+        // if the link is not connected with BasicPort.\r
+        if (!(sourceCell instanceof BasicPort) || !(targetCell instanceof BasicPort)) {\r
+            return false;\r
+        }\r
+        Orientation sourcePortOrien = null;\r
+        Orientation targetPortOrien = null;\r
+        double srcx = 0;\r
+        double srcy = 0;\r
+        double tgtx = 0;\r
+        double tgty = 0;\r
+        mxPoint sourcePoint = new mxPoint(srcx, srcy);\r
+        mxPoint targetPoint = new mxPoint(tgtx, tgty);\r
+        // if source is a port, get a new start point.\r
+        if (sourceCell instanceof BasicPort) {\r
+            mxCellState state = graph.getView().getState(sourceCell);\r
+            if (state != null) {\r
+                srcx = state.getCenterX();\r
+                srcy = state.getCenterY();\r
+                BasicPort sourcePort = (BasicPort) sourceCell;\r
+                sourcePortOrien = getPortRelativeOrientation(sourcePort, graph);\r
+            }\r
+        }\r
+        // if target is a port, get a new end point.\r
+        if (targetCell instanceof BasicPort) {\r
+            mxCellState state = graph.getView().getState(targetCell);\r
+            if (state != null) {\r
+                tgtx = state.getCenterX();\r
+                tgty = state.getCenterY();\r
+                BasicPort targetPort = (BasicPort) targetCell;\r
+                targetPortOrien = getPortRelativeOrientation(targetPort, graph);\r
+            }\r
+        }\r
+        // if source belongs to a SplitBlock\r
+        if (sourceCell.getParent() instanceof SplitBlock) {\r
+            srcx = sourceCell.getParent().getGeometry().getCenterX();\r
+            srcy = sourceCell.getParent().getGeometry().getCenterY();\r
+        }\r
+        // if target is a SplitBlock\r
+        if (targetCell.getParent() instanceof SplitBlock) {\r
+            tgtx = targetCell.getParent().getGeometry().getCenterX();\r
+            tgty = targetCell.getParent().getGeometry().getCenterY();\r
+        }\r
+        // if two points are coincident, use straignt route.\r
+        boolean isSplitBlock = (sourceCell.getParent() instanceof SplitBlock) && (targetCell.getParent() instanceof SplitBlock);\r
+        if (XcosRouteUtils.isPointCoincident(srcx, srcy, tgtx, tgty, isSplitBlock)) {\r
+            return true;\r
+        }\r
+        // if two ports are aligned and there are no blocks between them, use straight route.\r
+        if (XcosRouteUtils.isStrictlyAligned(srcx, srcy, tgtx, tgty)\r
+                && !XcosRouteUtils.checkObstacle(srcx, srcy, tgtx, tgty, allCells)\r
+                && !XcosRouteUtils.isOrientationParallel(srcx, srcy, tgtx, tgty, sourcePortOrien, targetPortOrien)) {\r
+            return true;\r
+        }\r
+        // re-calculate the orientation for the SplitBlock.\r
+        if (sourceCell.getParent() instanceof SplitBlock) {\r
+            if (lockPortPosition) { // if it is locked, use the default orientation.\r
+                sourcePortOrien = ((BasicPort) sourceCell).getOrientation();\r
+            } else {\r
+                sourcePortOrien = this.getNewOrientation(sourceCell, srcx, srcy, targetCell, tgtx, tgty, graph);\r
+            }\r
+        }\r
+        if (targetCell.getParent() instanceof SplitBlock) {\r
+            if (lockPortPosition) { // if it is locked, use the default orientation.\r
+                targetPortOrien = ((BasicPort) targetCell).getOrientation();\r
+            } else {\r
+                targetPortOrien = this.getNewOrientation(targetCell, tgtx, tgty, sourceCell, srcx, srcy, graph);\r
+            }\r
+        }\r
+        sourcePoint = getPointAwayPort(sourceCell, srcx, srcy, sourcePortOrien, allCells, graph);\r
+        targetPoint = getPointAwayPort(targetCell, tgtx, tgty, targetPortOrien, allCells, graph);\r
+        allCells = Arrays.copyOf(allCells, allCells.length + 2);\r
+        allCells[allCells.length - 2] = sourceCell;\r
+        allCells[allCells.length - 1] = targetCell;\r
+        List<mxPoint> list = XcosRouteUtils.getSimpleRoute(sourcePoint, sourcePortOrien, targetPoint,\r
+                targetPortOrien, allCells);\r
+        if (list != null && list.size() > 0) {\r
+            listRoute.addAll(list);\r
+            return true;\r
+        } else {\r
+            list = XcosRouteUtils.getComplexRoute(sourcePoint, sourcePortOrien, targetPoint, targetPortOrien,\r
+                    allCells, XcosRouteUtils.TRY_TIMES);\r
+            if (list != null && list.size() > 0) {\r
+                listRoute.addAll(list);\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Remove the redundancy points from the route.\r
+     *\r
+     * @return a list without non-redundant points\r
+     */\r
+    protected List<mxPoint> getNonRedundantPoints() {\r
+        List<mxPoint> list = new ArrayList<mxPoint>(0);\r
+        if (listRoute.size() > 2) {\r
+            list.add(listRoute.get(0));\r
+            for (int i = 1; i < listRoute.size() - 1; i++) {\r
+                mxPoint p1 = list.get(list.size() - 1);\r
+                mxPoint p2 = listRoute.get(i);\r
+                mxPoint p3 = listRoute.get(i + 1);\r
+                if (XcosRouteUtils.pointInLineSegment(p2.getX(), p2.getY(), p1.getX(), p1.getY(), p3.getX(), p3.getY())) {\r
+                    // if p2 is in the line segment between p1 and p3, remove it.\r
+                    continue;\r
+                } else {\r
+                    list.add(p2);\r
+                }\r
+            }\r
+            list.add(listRoute.get(listRoute.size() - 1));\r
+        } else {\r
+            // if the route has less than 3 points, there is no redundancy.\r
+            list.addAll(listRoute);\r
+        }\r
+        return list;\r
+    }\r
+\r
+    /**\r
+     * According to the relative position (orientation) of the port, get a point which is\r
+     * XcosRoute.BEAUTY_DISTANCE away from the port and out of block.\r
+     *\r
+     * @param port\r
+     * @param portX\r
+     * @param portY\r
+     * @param graph\r
+     * @return\r
+     */\r
+    private mxPoint getPointAwayPort(mxICell port, double portX, double portY, Orientation orien,\r
+            Object[] allCells, XcosDiagram graph) {\r
+        mxPoint point = new mxPoint(portX, portY);\r
+        double distance = XcosRouteUtils.BEAUTY_AWAY_DISTANCE;\r
+        if (port.getParent() instanceof SplitBlock) {\r
+            distance = XcosRouteUtils.SPLITBLOCK_AWAY_DISTANCE;\r
+        }\r
+        switch (orien) {\r
+        case EAST:\r
+            point.setX(point.getX() + distance);\r
+            while (Math.abs(point.getX() - portX) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+                    && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+                            || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+                point.setX(point.getX() - XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+            }\r
+            break;\r
+        case SOUTH:\r
+            point.setY(point.getY() + distance);\r
+            while (Math.abs(point.getY() - portY) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+                    && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+                            || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+                point.setY(point.getY() - XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+            }\r
+            break;\r
+        case WEST:\r
+            point.setX(point.getX() - distance);\r
+            while (Math.abs(point.getX() - portX) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+                    && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+                            || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+                point.setX(point.getX() + XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+            }\r
+            break;\r
+        case NORTH:\r
+            point.setY(point.getY() - distance);\r
+            while (Math.abs(point.getY() - portY) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+                    && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+                            || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+                point.setY(point.getY() + XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+            }\r
+            break;\r
+        }\r
+        return point;\r
+    }\r
+\r
+    /**\r
+     * As BasicPort.getOrientation is the default orientation, the Orientation is not correct when\r
+     * the block is mirrored or flipped. This method could get the current Orientation of the port.\r
+     *\r
+     * @param port\r
+     * @param graph\r
+     * @return\r
+     */\r
+    private Orientation getPortRelativeOrientation(BasicPort port, XcosDiagram graph) {\r
+        // the coordinate (x,y) for the port.\r
+        double portx = graph.getView().getState(port).getCenterX();\r
+        double porty = graph.getView().getState(port).getCenterY();\r
+        // the coordinate (x,y) and the width-height for the parent block\r
+        mxICell parent = port.getParent();\r
+        double blockx = graph.getView().getState(parent).getCenterX();\r
+        double blocky = graph.getView().getState(parent).getCenterY();\r
+        double blockw = parent.getGeometry().getWidth();\r
+        double blockh = parent.getGeometry().getHeight();\r
+        // calculate relative coordinate based on the center of parent block.\r
+        portx -= blockx;\r
+        porty -= blocky;\r
+        Orientation orientation = port.getOrientation();\r
+        if ((portx) >= blockw * Math.abs(porty) / blockh) { // x>=w*|y|/h\r
+            orientation = Orientation.EAST;\r
+        } else if (porty >= blockh * Math.abs(portx) / blockw) { // y>=h*|x|/w\r
+            orientation = Orientation.SOUTH;\r
+        } else if (portx <= -blockw * Math.abs(porty) / blockh) { // x<=-w*|y|/h\r
+            orientation = Orientation.WEST;\r
+        } else if (porty <= -blockh * Math.abs(portx) / blockw) { // y<=-h*|x|/w\r
+            orientation = Orientation.NORTH;\r
+        }\r
+        return orientation;\r
+    }\r
+\r
+    /**\r
+     * Special use to calculate the orientation when\r
+     * <ol>\r
+     * <li>it is a SplitBlock.</li>\r
+     * <li>there is no connection.</li>\r
+     * <ol>\r
+     *\r
+     * @param cell\r
+     * @param cx\r
+     * @param cy\r
+     * @param otherCell\r
+     * @param ox\r
+     * @param oy\r
+     * @param graph\r
+     * @return\r
+     */\r
+    private Orientation getNewOrientation(mxICell cell, double cx, double cy, mxICell otherCell, double ox,\r
+            double oy, XcosDiagram graph) {\r
+        Orientation orientation = Orientation.EAST;\r
+        if (cell.getParent() instanceof SplitBlock) {\r
+            SplitBlock block = (SplitBlock) cell.getParent();\r
+            Orientation inputOrien = this.getSplitBlockInputOrientation(block, graph);\r
+            if ((cell instanceof InputPort) || (cell instanceof ControlPort)) {\r
+                // if it is the InputPort/ControlPort of the SplitBlock,\r
+                orientation = inputOrien;\r
+            } else if ((cell instanceof OutputPort) || (cell instanceof CommandPort)) {\r
+                // if it is one of the OutputPorts/CommandPorts of the SplitBlock,\r
+                // it depends on the Orientation of InputPort and the other OutputPort.\r
+                BasicPort port = (BasicPort) cell;\r
+                BasicPort otherPort = null;\r
+                if (port == block.getOut1()) {\r
+                    otherPort = block.getOut2();\r
+                } else {\r
+                    otherPort = block.getOut1();\r
+                }\r
+                mxICell otherLink = otherPort.getEdgeAt(0);\r
+                if (otherLink instanceof BasicLink) {\r
+                    mxICell otherTarget = ((BasicLink) otherLink).getTarget();\r
+                    mxPoint otgtPoint = this.getCenterPoint(otherTarget, graph);\r
+                    double otx = otgtPoint.getX();\r
+                    double oty = otgtPoint.getY();\r
+                    double otcx = cx - otx;\r
+                    double otcy = cy - oty;\r
+                    double otox = ox - otx;\r
+                    double otoy = oy - oty;\r
+                    double value = (otcx * otoy) - (otox * otcy);\r
+                    if (ox >= cx && oy <= cy) {\r
+                        // when target is on NORTHEAST to source\r
+                        if (value >= 0 && inputOrien != Orientation.NORTH) {\r
+                            // other OutputPort is on the right\r
+                            orientation = Orientation.NORTH;\r
+                        } else if (inputOrien != Orientation.EAST) {\r
+                            // other OutputPort is on the left\r
+                            orientation = Orientation.EAST;\r
+                        } else {\r
+                            orientation = Orientation.NORTH;\r
+                        }\r
+                    } else if (ox >= cx && oy >= cy) {\r
+                        // when target is on SOUTHEAST to source\r
+                        if (value >= 0 && inputOrien != Orientation.EAST) {\r
+                            // other OutputPort is on the right\r
+                            orientation = Orientation.EAST;\r
+                        } else if (inputOrien != Orientation.SOUTH) {\r
+                            // other OutputPort is on the left\r
+                            orientation = Orientation.SOUTH;\r
+                        } else {\r
+                            orientation = Orientation.EAST;\r
+                        }\r
+                    } else if (ox <= cx && oy <= cy) {\r
+                        // when target is on NORTHWEST to source\r
+                        if (value >= 0 && inputOrien != Orientation.WEST) {\r
+                            // other OutputPort is on the right\r
+                            orientation = Orientation.WEST;\r
+                        } else if (inputOrien != Orientation.NORTH) {\r
+                            // other OutputPort is on the left\r
+                            orientation = Orientation.NORTH;\r
+                        } else {\r
+                            orientation = Orientation.WEST;\r
+                        }\r
+                    } else if (ox <= cx && oy >= cy) {\r
+                        // when target is on SOUTHWEST to source\r
+                        if (value >= 0 && inputOrien != Orientation.SOUTH) {\r
+                            // other OutputPort is on the right\r
+                            orientation = Orientation.SOUTH;\r
+                        } else if (inputOrien != Orientation.WEST) {\r
+                            // other OutputPort is on the left\r
+                            orientation = Orientation.WEST;\r
+                        } else {\r
+                            orientation = Orientation.SOUTH;\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return orientation;\r
+    }\r
+\r
+    /**\r
+     * Get the Orientation of the InputPort of a SplitBlock.\r
+     *\r
+     * @param block\r
+     *            the SplitBlock\r
+     * @return the orientation of its InputPort.\r
+     */\r
+    private Orientation getSplitBlockInputOrientation(SplitBlock block, XcosDiagram graph) {\r
+        Orientation orientation = Orientation.EAST;\r
+        BasicPort inp = block.getIn();\r
+        // the position of the Input\r
+        double inX = block.getGeometry().getCenterX();\r
+        double inY = block.getGeometry().getCenterY();\r
+        mxICell link = inp.getEdgeAt(0);\r
+        // the position of the source\r
+        double srcX = 0;\r
+        double srcY = 0;\r
+        if (link instanceof BasicLink) {\r
+            mxICell source = ((BasicLink) link).getSource();\r
+            mxPoint srcPoint = this.getCenterPoint(source, graph);\r
+            srcX = srcPoint.getX();\r
+            srcY = srcPoint.getY();\r
+        }\r
+        // the relative position of source to Input\r
+        double dx = srcX - inX;\r
+        double dy = srcY - inY;\r
+        if (dx >= Math.abs(dy)) { // x>=|y|\r
+            orientation = Orientation.EAST; // source is on the east of Input.\r
+        } else if (dy >= Math.abs(dx)) { // y>=|x|\r
+            orientation = Orientation.SOUTH;\r
+        } else if (dx <= -Math.abs(dy)) { // x<=-|y|\r
+            orientation = Orientation.WEST;\r
+        } else if (dy <= -Math.abs(dx)) { // y<=-|x|\r
+            orientation = Orientation.NORTH;\r
+        }\r
+        return orientation;\r
+    }\r
+\r
+    /**\r
+     * Get the position of a cell.\r
+     *\r
+     * @param cell\r
+     * @return\r
+     */\r
+    private mxPoint getCenterPoint(mxICell cell, XcosDiagram graph) {\r
+        mxPoint point = new mxPoint(0, 0);\r
+        if (cell instanceof BasicPort) {\r
+            BasicPort port = (BasicPort) cell;\r
+            mxICell parent = port.getParent();\r
+            if (parent instanceof SplitBlock) {\r
+                // if it belongs to a SplitBlock, use SplitBlock's geometry.\r
+                point.setX(parent.getGeometry().getCenterX());\r
+                point.setY(parent.getGeometry().getCenterY());\r
+            } else {\r
+                mxCellState state = graph.getView().getState(cell);\r
+                if (state != null) {\r
+                    point.setX(state.getCenterX());\r
+                    point.setY(state.getCenterY());\r
+                }\r
+            }\r
+        } else if (cell instanceof BasicBlock) {\r
+            // if it is a block, use its geometry.\r
+            BasicBlock block = (BasicBlock) cell;\r
+            point.setX(block.getGeometry().getCenterX());\r
+            point.setY(block.getGeometry().getCenterY());\r
+        }\r
+        return point;\r
+    }\r
+\r
+    /**\r
+     * Remove the selves from the array of all.<br/>\r
+     * except for selves, SplitBlock and the ports of links in selves. add other ports of blocks.\r
+     *\r
+     * @param all\r
+     * @param self\r
+     *            If self's source port or target port belongs to a SpliBlock, remove this\r
+     *            SplitBlock, too.\r
+     * @return a new array of all objects excluding selves\r
+     */\r
+    protected Object[] getAllOtherCells(Object[] all, Object... self) {\r
+        List<Object> listNotObs = new ArrayList<Object>(0);\r
+        listNotObs.addAll(Arrays.asList(self));\r
+        for (Object obj : self) {\r
+            // if self contains a link\r
+            if (obj instanceof BasicLink) {\r
+                BasicLink link = (BasicLink) obj;\r
+                // in these selves, if the source/target of this link belongs to a SplitBlock,\r
+                // remove it.\r
+                if (link.getSource() != null && link.getSource().getParent() instanceof SplitBlock) {\r
+                    listNotObs.add(link.getSource().getParent());\r
+                }\r
+                if (link.getTarget() != null && link.getTarget().getParent() instanceof SplitBlock) {\r
+                    listNotObs.add(link.getTarget().getParent());\r
+                }\r
+            }\r
+        }\r
+        List<Object> listnew = new ArrayList<Object>(0);\r
+        for (Object o : all) {\r
+            // if it does not belongs to self,\r
+            if (!listNotObs.contains(o) && !(o instanceof SplitBlock)) { // && !(o instanceof TextBlock)\r
+                // only add normal Blocks excluding SplitBlock or TextBlock.\r
+                if (!listnew.contains(o)) {\r
+                    listnew.add(o);\r
+                }\r
+                // add the ports of the block.\r
+                if (o instanceof BasicBlock) {\r
+                    BasicBlock block = (BasicBlock) o;\r
+                    for (int i = 0; i < block.getChildCount(); i++) {\r
+                        if (!listNotObs.contains(block.getChildAt(i))) {\r
+                            listnew.add(block.getChildAt(i));\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        Object[] newAll = listnew.toArray();\r
+        return newAll;\r
+    }\r
+}\r
diff --git a/scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosRouteUtils.java b/scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosRouteUtils.java
new file mode 100755 (executable)
index 0000000..aebf918
--- /dev/null
@@ -0,0 +1,1150 @@
+/*\r
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab\r
+ * Copyright (C) 2015 - Chenfeng ZHU\r
+ *\r
+ * This file must be used under the terms of the CeCILL.\r
+ * This source file is licensed as described in the file COPYING, which\r
+ * you should have received as part of this distribution.  The terms\r
+ * are also available at\r
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt\r
+ *\r
+ */\r
+\r
+package org.scilab.modules.xcos.utils;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.LinkedHashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import org.scilab.modules.xcos.block.BasicBlock;\r
+import org.scilab.modules.xcos.block.SplitBlock;\r
+import org.scilab.modules.xcos.link.BasicLink;\r
+import org.scilab.modules.xcos.port.BasicPort;\r
+import org.scilab.modules.xcos.port.Orientation;\r
+\r
+import com.mxgraph.model.mxCell;\r
+import com.mxgraph.model.mxGeometry;\r
+import com.mxgraph.model.mxICell;\r
+import com.mxgraph.util.mxPoint;\r
+import com.mxgraph.util.mxUtils;\r
+\r
+/**\r
+ * Provide methods to calculate the route.\r
+ *\r
+ */\r
+public abstract class XcosRouteUtils {\r
+\r
+    /**\r
+     * The error which can be accepted as it is aligned strictly.\r
+     */\r
+    public final static double ALIGN_STRICT_ERROR = 2;\r
+\r
+    public final static double ALIGN_SPLITBLOCK_ERROR = 10;\r
+\r
+    /**\r
+     * The distance for a point away to the port.\r
+     */\r
+    public final static double BEAUTY_AWAY_DISTANCE = 40;\r
+    public final static double BEAUTY_AWAY_REVISION = 10;\r
+    public final static double SPLITBLOCK_AWAY_DISTANCE = 15;\r
+\r
+    /**\r
+     * Define a normal half size of block to avoid.\r
+     */\r
+    public final static double NORMAL_BLOCK_SIZE = 40;\r
+\r
+    /**\r
+     * Times trying to find a complex route.\r
+     */\r
+    public final static int TRY_TIMES = 3;\r
+\r
+    /**\r
+     * Check whether the two points are strictly aligned(vertical or horizontal) or not. (The\r
+     * accepted error is <code>XcosRoute.ALIGN_STRICT_ERROR</code>)\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point\r
+     * @param y1\r
+     *            the y-coordinate of the first point\r
+     * @param x2\r
+     *            the x-coordinate of the second point\r
+     * @param y2\r
+     *            the y-coordinate of the second point\r
+     * @return <b>true</b> if two points are aligned.\r
+     */\r
+    protected static boolean isStrictlyAligned(double x1, double y1, double x2, double y2) {\r
+        double error = XcosRouteUtils.ALIGN_STRICT_ERROR;\r
+        if (Math.abs(x2 - x1) < error) {\r
+            return true;\r
+        }\r
+        if (Math.abs(y2 - y1) < error) {\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Get all points of one link including the source port and target port.\r
+     *\r
+     * @param link\r
+     * @return\r
+     */\r
+    protected static List<mxPoint> getLinkPoints(BasicLink link) {\r
+        List<mxPoint> list = new ArrayList<mxPoint>(0);\r
+        mxPoint offset;\r
+        // add the source point.\r
+        mxICell source = link.getSource();\r
+        if (source != null) {\r
+            mxGeometry sourceGeo = source.getGeometry();\r
+            double sourceX = sourceGeo.getCenterX();\r
+            double sourceY = sourceGeo.getCenterY();\r
+            mxICell sourceParent = source.getParent();\r
+            mxGeometry srcParGeo = sourceParent.getGeometry();\r
+            if (srcParGeo == null) {\r
+                srcParGeo = new mxGeometry(0, 0, 0, 0);\r
+            }\r
+            offset = sourceGeo.getOffset();\r
+            if (offset == null) {\r
+                offset = new mxPoint(0, 0);\r
+            }\r
+            if (sourceGeo.isRelative()) {\r
+                sourceX = srcParGeo.getX() + sourceGeo.getX() * srcParGeo.getWidth() + offset.getX();\r
+                sourceY = srcParGeo.getY() + sourceGeo.getY() * srcParGeo.getHeight() + offset.getY();\r
+            } else {\r
+                sourceX = srcParGeo.getX() + sourceGeo.getX() + offset.getX();\r
+                sourceY = srcParGeo.getY() + sourceGeo.getY() + offset.getY();\r
+            }\r
+            list.add(new mxPoint(sourceX, sourceY));\r
+        }\r
+        // add all the turning points.\r
+        if (link.getGeometry().getPoints() != null) {\r
+            list.addAll(link.getGeometry().getPoints());\r
+        }\r
+        // add the target point.\r
+        mxICell target = link.getTarget();\r
+        if (target != null) {\r
+            mxGeometry targetGeo = target.getGeometry();\r
+            double targetX = targetGeo.getCenterX();\r
+            double targetY = targetGeo.getCenterY();\r
+            mxICell targetParent = target.getParent();\r
+            mxGeometry tgtParGeo = targetParent.getGeometry();\r
+            if (tgtParGeo == null) {\r
+                tgtParGeo = new mxGeometry(0, 0, 0, 0);\r
+            }\r
+            offset = targetGeo.getOffset();\r
+            if (offset == null) {\r
+                offset = new mxPoint(0, 0);\r
+            }\r
+            if (targetGeo.isRelative()) {\r
+                targetX = tgtParGeo.getX() + targetGeo.getX() * tgtParGeo.getWidth() + offset.getX();\r
+                targetY = tgtParGeo.getY() + targetGeo.getY() * tgtParGeo.getHeight() + offset.getY();\r
+            } else {\r
+                targetX = tgtParGeo.getX() + targetGeo.getX() + offset.getX();\r
+                targetY = tgtParGeo.getY() + targetGeo.getY() + offset.getY();\r
+            }\r
+            list.add(new mxPoint(targetX, targetY));\r
+        }\r
+        return list;\r
+    }\r
+\r
+    /**\r
+     * Check whether there are blocks between two points.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the line\r
+     * @param allCells\r
+     * @return <b>true</b> if there is at least one blocks in the line.\r
+     */\r
+    protected static boolean checkObstacle(double x1, double y1, double x2, double y2, Object[] allCells) {\r
+        return checkObstacle(x1, y1, x2, y2, allCells, false);\r
+    }\r
+\r
+    /**\r
+     * Check whether there are blocks between two points.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the line\r
+     * @param allCells\r
+     * @param isStrict\r
+     *            if it is true, there won't be intersection.\r
+     * @return <b>true</b> if there is at least one obstacle in the line.\r
+     */\r
+    protected static boolean checkObstacle(double x1, double y1, double x2, double y2, Object[] allCells, boolean isStrict) {\r
+        for (Object o : allCells) {\r
+            if (o instanceof mxCell) {\r
+                mxCell c = (mxCell) o;\r
+                if (c instanceof BasicLink) {\r
+                    BasicLink l = (BasicLink) c;\r
+                    if (checkLinesIntersection(x1, y1, x2, y2, l) && isStrict) {\r
+                        // check intersection.\r
+                        return true;\r
+                    }\r
+                    if (checkLinesCoincide(x1, y1, x2, y2, l)) {\r
+                        // check superimposition.\r
+                        return true;\r
+                    }\r
+                    if (pointInLink(x1, y1, l) || pointInLink(x2, y2, l)) {\r
+                        return true;\r
+                    }\r
+                } else {\r
+                    mxGeometry geo = c.getGeometry();\r
+                    mxGeometry newGeo = new mxGeometry(geo.getX(), geo.getY(), geo.getWidth(), geo.getHeight());\r
+                    // if it is a BasicPort, re-calculate its geometry position.\r
+                    if (c.getParent().getGeometry() != null && c instanceof BasicPort) {\r
+                        double x = c.getParent().getGeometry().getX() + newGeo.getX();\r
+                        newGeo.setX(x);\r
+                        double y = c.getParent().getGeometry().getY() + newGeo.getY();\r
+                        newGeo.setY(y);\r
+                    }\r
+                    mxPoint interction = newGeo.intersectLine(x1, y1, x2, y2);\r
+                    if (interction != null) {\r
+                        return true;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check whether a point is in the range of one block (including the ports).\r
+     *\r
+     * @param x\r
+     *            the x-coordinate of the point\r
+     * @param y\r
+     *            the y-coordinate of the point\r
+     * @param allCells\r
+     * @return <b>true</b> if one point is in one block.\r
+     */\r
+    protected static boolean checkPointInBlocks(double x, double y, Object[] allCells) {\r
+        for (Object o : allCells) {\r
+            if (o instanceof BasicBlock) {\r
+                BasicBlock block = (BasicBlock) o;\r
+                // if it is a BasicBlock, re-calculate its size with its port.\r
+                double ix = 0;\r
+                double iy = 0;\r
+                double iw = 0;\r
+                double ih = 0;\r
+                mxGeometry g = block.getGeometry();\r
+                for (int i = 0; i < block.getChildCount(); i++) {\r
+                    mxICell child = block.getChildAt(i);\r
+                    if (child.getGeometry() == null) {\r
+                        continue;\r
+                    }\r
+                    mxGeometry childGeo = new mxGeometry(child.getGeometry().getX(), child.getGeometry().getY(),\r
+                            child.getGeometry().getWidth(), child.getGeometry().getHeight());\r
+                    if (child.getGeometry().isRelative()) {\r
+                        childGeo.setX(g.getWidth() * childGeo.getX());\r
+                        childGeo.setY(g.getHeight() * childGeo.getY());\r
+                    }\r
+                    if (childGeo.getX() < 0) {\r
+                        ix = childGeo.getX();\r
+                        iw = Math.abs(ix);\r
+                    }\r
+                    if (childGeo.getX() + childGeo.getWidth() > g.getWidth()) {\r
+                        iw += childGeo.getX() + childGeo.getWidth() - g.getWidth();\r
+                    }\r
+                    if (childGeo.getY() < 0) {\r
+                        iy = childGeo.getY();\r
+                        ih = Math.abs(iy);\r
+                    }\r
+                    if (childGeo.getY() + childGeo.getHeight() > Math.max(block.getGeometry().getHeight(), ih)) {\r
+                        ih += childGeo.getY() + childGeo.getHeight() - g.getHeight();\r
+                    }\r
+                }\r
+                double blockx = g.getX() + ix;\r
+                double blocky = g.getY() + iy;\r
+                double width = g.getWidth() + iw;\r
+                double height = g.getHeight() + ih;\r
+                if (x >= blockx && x <= (blockx + width) && y >= blocky && y <= (blocky + height)) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check whether a point is in a geometry.\r
+     *\r
+     * @param x\r
+     *            the x-coordinate of the point\r
+     * @param y\r
+     *            the y-coordinate of the point\r
+     * @param geometry\r
+     * @return <b>true</b> if one point is in the geometry.\r
+     */\r
+    protected static boolean checkPointInGeometry(double x, double y, mxGeometry geometry) {\r
+        boolean flag = false;\r
+        flag = (x >= geometry.getX())\r
+                && (x <= geometry.getX() + geometry.getWidth())\r
+                && (y >= geometry.getY())\r
+                && (y <= geometry.getY() + geometry.getHeight());\r
+        return flag;\r
+    }\r
+\r
+    /**\r
+     * Check whether two lines have intersection or not.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the first line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the first line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the first line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the first line\r
+     * @param link\r
+     *            the second link\r
+     * @return <b>true</b> if two lines have intersection(s).\r
+     */\r
+    private static boolean checkLinesIntersection(double x1, double y1, double x2, double y2, BasicLink link) {\r
+        List<mxPoint> listPoints = getLinkPoints(link);\r
+        if (listPoints == null || listPoints.size() <= 1) {\r
+        } else {\r
+            for (int i = 1; i < listPoints.size(); i++) {\r
+                mxPoint point3 = listPoints.get(i - 1);\r
+                mxPoint point4 = listPoints.get(i);\r
+                double x3 = point3.getX();\r
+                double y3 = point3.getY();\r
+                double x4 = point4.getX();\r
+                double y4 = point4.getY();\r
+                mxPoint point = mxUtils.intersection(x1, y1, x2, y2, x3, y3, x4, y4);\r
+                if (point != null) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check whether two lines coincide or not. The lines are vertical or horizontal. <br/>\r
+     * <b>NOTE:</b> This method is used to check coincidence, NOT intersection!\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the first line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the first line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the first line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the first line\r
+     * @param link\r
+     *            the second line\r
+     * @return <b>true</b> if two lines coincide completely or partly.\r
+     */\r
+    private static boolean checkLinesCoincide(double x1, double y1, double x2, double y2, BasicLink link) {\r
+        // mxICell source = line.getSource();\r
+        // mxICell target = line.getTarget();\r
+        List<mxPoint> listPoints = new ArrayList<mxPoint>(0);\r
+        if (link.getGeometry().getPoints() == null || link.getGeometry().getPoints().size() == 0) {\r
+            // if the edge is straight or vertical or horizontal style, there is\r
+            // no way to check.\r
+        } else {\r
+            if (getLinkPortPosition(link, true) != null) {\r
+                listPoints.add(getLinkPortPosition(link, true));\r
+            }\r
+            listPoints.addAll(link.getGeometry().getPoints());\r
+            if (getLinkPortPosition(link, false) != null) {\r
+                listPoints.add(getLinkPortPosition(link, false));\r
+            }\r
+            // in Java 8:\r
+            // listPoints.removeIf(Objects::isNull);\r
+            for (int i = 1; i < listPoints.size(); i++) {\r
+                mxPoint point3 = listPoints.get(i - 1);\r
+                mxPoint point4 = listPoints.get(i);\r
+                double x3 = point3.getX();\r
+                double y3 = point3.getY();\r
+                double x4 = point4.getX();\r
+                double y4 = point4.getY();\r
+                if (linesCoincide(x1, y1, x2, y2, x3, y3, x4, y4)) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check whether two lines coincide or not.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the first line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the first line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the first line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the first line\r
+     * @param x3\r
+     *            the x-coordinate of the first point of the second line\r
+     * @param y3\r
+     *            the y-coordinate of the first point of the second line\r
+     * @param x4\r
+     *            the x-coordinate of the second point of the second line\r
+     * @param y4\r
+     *            the y-coordinate of the second point of the second line\r
+     * @return <b>true</b> if two lines coincide.\r
+     */\r
+    private static boolean linesCoincide(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {\r
+        x1 = Math.round(x1);\r
+        y1 = Math.round(y1);\r
+        x2 = Math.round(x2);\r
+        y2 = Math.round(y2);\r
+        x3 = Math.round(x3);\r
+        y3 = Math.round(y3);\r
+        x4 = Math.round(x4);\r
+        y4 = Math.round(y4);\r
+        if (linesStrictlyCoincide(x1, y1, x2, y2, x3, y3, x4, y4)) {\r
+            return true;\r
+        }\r
+        if (x1 == x2) {\r
+            for (double xi = x1 - ALIGN_STRICT_ERROR; xi < x1 + ALIGN_STRICT_ERROR; xi++) {\r
+                xi = Math.round(xi);\r
+                if (linesStrictlyCoincide(xi, y1, xi, y2, x3, y3, x4, y4)) {\r
+                    return true;\r
+                }\r
+            }\r
+        } else if (y1 == y2) {\r
+            for (double yi = y1 - ALIGN_STRICT_ERROR; yi < y1 + ALIGN_STRICT_ERROR; yi++) {\r
+                yi = Math.round(yi);\r
+                if (linesStrictlyCoincide(x1, yi, x2, yi, x3, y3, x4, y4)) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check whether two lines strictly coincide or not.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the first line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the first line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the first line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the first line\r
+     * @param x3\r
+     *            the x-coordinate of the first point of the second line\r
+     * @param y3\r
+     *            the y-coordinate of the first point of the second line\r
+     * @param x4\r
+     *            the x-coordinate of the second point of the second line\r
+     * @param y4\r
+     *            the y-coordinate of the second point of the second line\r
+     * @return <b>true</b> if two lines coincide.\r
+     */\r
+    private static boolean linesStrictlyCoincide(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {\r
+        // the first line is inside the second line.\r
+        if (pointInLineSegment(x1, y1, x3, y3, x4, y4) && pointInLineSegment(x2, y2, x3, y3, x4, y4)) {\r
+            return true;\r
+        }\r
+        // the second line is inside the first line.\r
+        if (pointInLineSegment(x3, y3, x1, y1, x2, y2) && pointInLineSegment(x4, y4, x1, y1, x2, y2)) {\r
+            return true;\r
+        }\r
+        double i = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);\r
+        // two lines are parallel.\r
+        if (i == 0) {\r
+            if (pointInLineSegment(x1, y1, x3, y3, x4, y4) || pointInLineSegment(x2, y2, x3, y3, x4, y4)\r
+                    || pointInLineSegment(x3, y3, x1, y1, x2, y2) || pointInLineSegment(x4, y4, x1, y1, x2, y2)) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Return the intersection of two lines as an mxPoint.\r
+     * If two lines are superposition, return the last point.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the first line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the first line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the first line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the first line\r
+     * @param x3\r
+     *            the x-coordinate of the first point of the second line\r
+     * @param y3\r
+     *            the y-coordinate of the first point of the second line\r
+     * @param x4\r
+     *            the x-coordinate of the second point of the second line\r
+     * @param y4\r
+     *            the y-coordinate of the second point of the second line\r
+     * @return the intersection between the two lines.\r
+     */\r
+    public static mxPoint getIntersection(double x1, double y1, double x2, double y2, double x3, double y3,\r
+            double x4, double y4) {\r
+        x1 = Math.round(x1);\r
+        y1 = Math.round(y1);\r
+        x2 = Math.round(x2);\r
+        y2 = Math.round(y2);\r
+        x3 = Math.round(x3);\r
+        y3 = Math.round(y3);\r
+        x4 = Math.round(x4);\r
+        y4 = Math.round(y4);\r
+        if (linesStrictlyCoincide(x1, y1, x2, y2, x3, y3, x4, y4)) {\r
+            if (pointInLineSegment(x2, y2, x3, y3, x4, y4)) {\r
+                return new mxPoint(x2, y2);\r
+            } else if (pointInLineSegment(x4, y4, x1, y1, x2, y2)) {\r
+                return new mxPoint(x4, y4);\r
+            }\r
+        }\r
+        return mxUtils.intersection(x1, y1, x2, y2, x3, y3, x4, y4);\r
+    }\r
+\r
+    /**\r
+     * Check whether a point is in the Link.\r
+     *\r
+     * @param x\r
+     *            the x-coordinate of the point\r
+     * @param y\r
+     *            the y-coordinate of the point\r
+     * @param link the link\r
+     * @return <b>true</b> if one point is in the Link.\r
+     */\r
+    private static boolean pointInLink(double x, double y, BasicLink link) {\r
+        List<mxPoint> listPoints = link.getPoints(0, false);\r
+        return pointInLink(x, y, listPoints);\r
+    }\r
+\r
+    /**\r
+     * Check whether a point is in the Link (a list of Point).\r
+     *\r
+     * @param x\r
+     *            the x-coordinate of the point\r
+     * @param y\r
+     *            the y-coordinate of the point\r
+     * @param listPoints\r
+     *            the list of points in the link.\r
+     * @return <b>true</b> if one point is in the Link.\r
+     */\r
+    protected static boolean pointInLink(double x, double y, List<mxPoint> listPoints) {\r
+        if (listPoints == null || listPoints.size() <= 1) {\r
+        } else {\r
+            for (int i = 1; i < listPoints.size(); i++) {\r
+                mxPoint point1 = listPoints.get(i - 1);\r
+                mxPoint point2 = listPoints.get(i);\r
+                double x1 = point1.getX();\r
+                double y1 = point1.getY();\r
+                double x2 = point2.getX();\r
+                double y2 = point2.getY();\r
+                if (x1 == x2 && x != x1) {\r
+                    continue;\r
+                }\r
+                if (y1 == y2 && y != y1) {\r
+                    continue;\r
+                }\r
+                if (pointInLineSegment(x, y, x1, y1, x2, y2)) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check whether the point is in the line segment or not.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the point\r
+     * @param y1\r
+     *            the y-coordinate of the point\r
+     * @param x2\r
+     *            the x-coordinate of the first point of the line\r
+     * @param y2\r
+     *            the y-coordinate of the first point of the line\r
+     * @param x3\r
+     *            the x-coordinate of the second point of the line\r
+     * @param y3\r
+     *            the y-coordinate of the second point of the line\r
+     * @return <b>true</b> if the point is in the line segment.\r
+     */\r
+    protected static boolean pointInLineSegment(double x1, double y1, double x2, double y2, double x3, double y3) {\r
+        if (((x3 - x2) * (y1 - y2) == (x1 - x2) * (y3 - y2)) && (x1 >= Math.min(x2, x3) && x1 <= Math.max(x2, x3))\r
+                && (y1 >= Math.min(y2, y3) && y1 <= Math.max(y2, y3))) {\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * In the method, only 4 turning points at most are supported.\r
+     *\r
+     * @param p1\r
+     *            the point away from the first port\r
+     * @param p2\r
+     *            the point away from the second port\r
+     * @param allCells\r
+     *            all the possible\r
+     * @return\r
+     */\r
+    public static List<mxPoint> getSimpleRoute(mxPoint p1, mxPoint p2, Object[] allCells) {\r
+        return getSimpleRoute(p1, null, p2, null, allCells);\r
+    }\r
+\r
+    /**\r
+     * In the method, only 4 turning points at most are supported.\r
+     *\r
+     * @param p1\r
+     *            the point away from the first port\r
+     * @param o1\r
+     *            the orientation of the first port\r
+     * @param p2\r
+     *            the point away from the second port\r
+     * @param o2\r
+     *            the orientation of the second port\r
+     * @param allCells\r
+     *            all the possible\r
+     * @return\r
+     */\r
+    public static List<mxPoint> getSimpleRoute(mxPoint p1, Orientation o1, mxPoint p2, Orientation o2,\r
+            Object[] allCells) {\r
+        // point1 and point2 are not in the vertical or horizontal line.\r
+        List<mxPoint> listSimpleRoute = new ArrayList<mxPoint>(0);\r
+        List<Double> listX = new ArrayList<Double>(0);\r
+        List<Double> listY = new ArrayList<Double>(0);\r
+        double distance = XcosRouteUtils.NORMAL_BLOCK_SIZE;\r
+        double x1 = p1.getX();\r
+        double y1 = p1.getY();\r
+        double x2 = p2.getX();\r
+        double y2 = p2.getY();\r
+        // simplest situation: directly connect.\r
+        if (o1 == Orientation.EAST || o1 == Orientation.WEST) {\r
+            // if the start is horizontal,\r
+            if (!checkObstacle(x1, y1, x2, y1, allCells) && !checkObstacle(x2, y1, x2, y2, allCells)) {\r
+                listSimpleRoute.add(new mxPoint(x2, y1));\r
+                if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+            if (!checkObstacle(x1, y1, x1, y2, allCells) && !checkObstacle(x1, y2, x2, y2, allCells)) {\r
+                listSimpleRoute.add(p1);\r
+                listSimpleRoute.add(new mxPoint(x1, y2));\r
+                if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+        } else if (o1 == Orientation.NORTH || o1 == Orientation.SOUTH) {\r
+            // if the start is vertical,\r
+            if (!checkObstacle(x1, y1, x1, y2, allCells) && !checkObstacle(x1, y2, x2, y2, allCells)) {\r
+                listSimpleRoute.add(new mxPoint(x1, y2));\r
+                if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+            if (!checkObstacle(x1, y1, x2, y1, allCells) && !checkObstacle(x2, y1, x2, y2, allCells)) {\r
+                listSimpleRoute.add(p1);\r
+                listSimpleRoute.add(new mxPoint(x2, y1));\r
+                if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+        }\r
+        if (o1 == Orientation.EAST || o1 == Orientation.WEST) {\r
+            // check the nodes in x-coordinate\r
+            double xmax = Math.max(x1 + distance, x2 + distance);\r
+            double xmin = Math.min(x1 - distance, x2 - distance);\r
+            for (double xi = xmin; xi <= xmax; xi++) {\r
+                if (!checkObstacle(x1, y1, xi, y1, allCells) && !checkObstacle(xi, y1, xi, y2, allCells)\r
+                        && !checkObstacle(xi, y2, x2, y2, allCells)) {\r
+                    listX.add(xi);\r
+                }\r
+            }\r
+            if (listX.size() > 0) {\r
+                double x = choosePoint(listX, x1, x2);\r
+                listSimpleRoute.add(new mxPoint(x, y1));\r
+                listSimpleRoute.add(new mxPoint(x, y2));\r
+                if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+            // check the nodes in y-coordinate\r
+            double ymax = Math.max(y1 + distance, y2 + distance);\r
+            double ymin = Math.min(y1 - distance, y2 - distance);\r
+            for (double yi = ymin; yi <= ymax; yi++) {\r
+                if (!checkObstacle(x1, y1, x1, yi, allCells) && !checkObstacle(x1, yi, x2, yi, allCells)\r
+                        && !checkObstacle(x2, yi, x2, y2, allCells)) {\r
+                    listY.add(yi);\r
+                }\r
+            }\r
+            if (listY.size() > 0) {\r
+                double y = choosePoint(listY, y1, y2);\r
+                listSimpleRoute.add(p1);\r
+                listSimpleRoute.add(new mxPoint(x1, y));\r
+                listSimpleRoute.add(new mxPoint(x2, y));\r
+                if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+        } else if (o1 == Orientation.NORTH || o1 == Orientation.SOUTH) {\r
+            // check the nodes in y-coordinate\r
+            double ymax = Math.max(y1 + distance, y2 + distance);\r
+            double ymin = Math.min(y1 - distance, y2 - distance);\r
+            for (double yi = ymin; yi <= ymax; yi++) {\r
+                if (!checkObstacle(x1, y1, x1, yi, allCells) && !checkObstacle(x1, yi, x2, yi, allCells)\r
+                        && !checkObstacle(x2, yi, x2, y2, allCells)) {\r
+                    listY.add(yi);\r
+                }\r
+            }\r
+            if (listY.size() > 0) {\r
+                double y = choosePoint(listY, y1, y2);\r
+                listSimpleRoute.add(new mxPoint(x1, y));\r
+                listSimpleRoute.add(new mxPoint(x2, y));\r
+                if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+            // check the nodes in x-coordinate\r
+            double xmax = Math.max(x1 + distance, x2 + distance);\r
+            double xmin = Math.min(x1 - distance, x2 - distance);\r
+            for (double xi = xmin; xi <= xmax; xi++) {\r
+                if (!checkObstacle(x1, y1, xi, y1, allCells) && !checkObstacle(xi, y1, xi, y2, allCells)\r
+                        && !checkObstacle(xi, y2, x2, y2, allCells)) {\r
+                    listX.add(xi);\r
+                }\r
+            }\r
+            if (listX.size() > 0) {\r
+                double x = choosePoint(listX, x1, x2);\r
+                listSimpleRoute.add(p1);\r
+                listSimpleRoute.add(new mxPoint(x, y1));\r
+                listSimpleRoute.add(new mxPoint(x, y2));\r
+                if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+                    listSimpleRoute.add(p2);\r
+                }\r
+                return listSimpleRoute;\r
+            }\r
+        }\r
+        return listSimpleRoute;\r
+    }\r
+\r
+    /**\r
+     * Choose a better point (which is the average number in the widest range in a certain density)\r
+     * from the list which contains discrete numbers.\r
+     *\r
+     * @param list\r
+     * @return\r
+     */\r
+    private static double choosePoint(List<Double> list, double p1, double p2) {\r
+        if (list == null || list.size() == 0) {\r
+            return 0;\r
+        }\r
+        Collections.sort(list);\r
+        double nMax = Math.max(p1, p2);\r
+        double nMin = Math.min(p1, p2);\r
+        double start = list.get(0);\r
+        double start_temp = list.get(0);\r
+        double end = list.get(0);\r
+        double end_temp = list.get(0);\r
+        double mid_temp = (end_temp + start_temp) / 2;\r
+        double mid = (end + start) / 2;\r
+        boolean restart = true;\r
+        for (int i = 1; i < list.size(); i++) {\r
+            if (Math.abs(list.get(i) - list.get(i - 1)) <= 1.1) {\r
+                end_temp = list.get(i);\r
+                restart = false;\r
+            } else {\r
+                if (restart) {\r
+                    start_temp = list.get(i);\r
+                    continue;\r
+                }\r
+                mid_temp = (end_temp + start_temp) / 2;\r
+                mid = (end + start) / 2;\r
+                if ((mid_temp < nMin || mid_temp > nMax) && (mid < nMax && mid > nMin)) {\r
+                    // if the new one is out of two points and the previous one\r
+                    // is inside,\r
+                    start_temp = list.get(i);\r
+                    end_temp = list.get(i);\r
+                    restart = true;\r
+                } else if ((Math.abs(end_temp - start_temp) > Math.abs(end - start))\r
+                        || (mid_temp < nMax && mid_temp > nMin) && (mid < nMin || mid > nMax)) {\r
+                    // if the new one in between two points and the previous one\r
+                    // is out of them, or if the new one is longer than the\r
+                    // previous one,\r
+                    start = start_temp;\r
+                    end = end_temp;\r
+                    start_temp = list.get(i);\r
+                    end_temp = list.get(i);\r
+                    restart = true;\r
+                }\r
+            }\r
+        }\r
+        mid_temp = (end_temp + start_temp) / 2;\r
+        mid = (end + start) / 2;\r
+        if ((mid_temp < nMin || mid_temp > nMax) && (mid < nMax && mid > nMin)) {\r
+        } else if ((Math.abs(end_temp - start_temp) > Math.abs(end - start))\r
+                || ((mid_temp < nMax && mid_temp > nMin) && (mid < nMin || mid > nMax))) {\r
+            start = start_temp;\r
+            end = end_temp;\r
+        }\r
+        return (start + end) / 2;\r
+    }\r
+\r
+    /**\r
+     * Based on getSimpleRoute().\r
+     *\r
+     * @param p1\r
+     *            the point away from the first port\r
+     * @param o1\r
+     *            the orientation of the first port\r
+     * @param p2\r
+     *            the point away from the second port\r
+     * @param o2\r
+     *            the orientation of the second port\r
+     * @param allCells\r
+     *            all the possible obstacles\r
+     * @param times\r
+     * @return\r
+     */\r
+    public static List<mxPoint> getComplexRoute(mxPoint p1, Orientation o1, mxPoint p2, Orientation o2,\r
+            Object[] allCells, int times) {\r
+        if (times <= 0) {\r
+            return null;\r
+        }\r
+        double newPosition = NORMAL_BLOCK_SIZE;\r
+        List<mxPoint> listComplexRoute = new ArrayList<mxPoint>(0);\r
+        List<mxPoint> listTmp = new ArrayList<mxPoint>(0);\r
+        listComplexRoute.add(p1);\r
+        Map<Orientation, mxPoint> mapNewP1 = new LinkedHashMap<Orientation, mxPoint>(0);\r
+        if (o1 == Orientation.EAST || o1 == null) {\r
+            // to EAST\r
+            mxPoint npE = new mxPoint(p1.getX() + newPosition, p1.getY());\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npE.getX(), npE.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npE, Orientation.EAST, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.EAST, npE);\r
+            }\r
+            // to NORTH\r
+            mxPoint npN = new mxPoint(p1.getX(), p1.getY() - newPosition);\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npN.getX(), npN.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npN, Orientation.NORTH, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.NORTH, npN);\r
+            }\r
+            // to SOUTH\r
+            mxPoint npS = new mxPoint(p1.getX(), p1.getY() + newPosition);\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npS.getX(), npS.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npS, Orientation.SOUTH, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.SOUTH, npS);\r
+            }\r
+            if (o1 == null) {\r
+                // to WEST\r
+                mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+                if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+                    listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+                    if (listTmp != null && listTmp.size() > 0) {\r
+                        listComplexRoute.addAll(listTmp);\r
+                        return listComplexRoute;\r
+                    }\r
+                    mapNewP1.put(Orientation.WEST, npW);\r
+                }\r
+            }\r
+        } else if (o1 == Orientation.SOUTH) {\r
+            mxPoint npS = new mxPoint(p1.getX(), p1.getY() + newPosition);\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npS.getX(), npS.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npS, Orientation.SOUTH, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.SOUTH, npS);\r
+            }\r
+            mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.WEST, npW);\r
+            }\r
+            mxPoint npE = new mxPoint(p1.getX() + newPosition, p1.getY());\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npE.getX(), npE.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npE, Orientation.EAST, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.EAST, npE);\r
+            }\r
+        } else if (o1 == Orientation.WEST) {\r
+            mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.WEST, npW);\r
+            }\r
+            mxPoint npN = new mxPoint(p1.getX(), p1.getY() - newPosition);\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npN.getX(), npN.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npN, Orientation.NORTH, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.NORTH, npN);\r
+            }\r
+            mxPoint npS = new mxPoint(p1.getX(), p1.getY() + newPosition);\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npS.getX(), npS.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npS, Orientation.SOUTH, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.SOUTH, npS);\r
+            }\r
+        } else if (o1 == Orientation.NORTH) {\r
+            mxPoint npN = new mxPoint(p1.getX(), p1.getY() - newPosition);\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npN.getX(), npN.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npN, Orientation.NORTH, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.NORTH, npN);\r
+            }\r
+            mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.WEST, npW);\r
+            }\r
+            mxPoint npE = new mxPoint(p1.getX() + newPosition, p1.getY());\r
+            if (!checkObstacle(p1.getX(), p1.getY(), npE.getX(), npE.getY(), allCells)) {\r
+                listTmp = getSimpleRoute(npE, Orientation.EAST, p2, o2, allCells);\r
+                if (listTmp != null && listTmp.size() > 0) {\r
+                    listComplexRoute.addAll(listTmp);\r
+                    return listComplexRoute;\r
+                }\r
+                mapNewP1.put(Orientation.EAST, npE);\r
+            }\r
+        }\r
+        for (Map.Entry<Orientation, mxPoint> np1 : mapNewP1.entrySet()) {\r
+            listTmp = getComplexRoute(np1.getValue(), np1.getKey(), p2, o2, allCells, times - 1);\r
+            if (listTmp != null && listTmp.size() > 1) {\r
+                listComplexRoute.addAll(listTmp);\r
+                return listComplexRoute;\r
+            }\r
+        }\r
+        listComplexRoute.clear();\r
+        return listComplexRoute;\r
+    }\r
+\r
+    /**\r
+     * Get the Orientation of a port to its parent.\r
+     *\r
+     * @param port\r
+     * @return\r
+     */\r
+    protected Orientation getPortOrientation(BasicPort port) {\r
+        if (port.getParent() == null) {\r
+            return Orientation.EAST;\r
+        }\r
+        // the coordinate (x,y) for the port.\r
+        mxGeometry portGeo = port.getGeometry();\r
+        double portX = portGeo.getCenterX();\r
+        double portY = portGeo.getCenterY();\r
+        // the coordinate (x,y) and the width-height for the parent block\r
+        mxICell parent = port.getParent();\r
+        mxGeometry parentGeo = parent.getGeometry();\r
+        double blockW = parentGeo.getWidth();\r
+        double blockH = parentGeo.getHeight();\r
+        if (portGeo.isRelative()) {\r
+            portX *= blockW;\r
+            portY *= blockH;\r
+        }\r
+        // calculate relative coordinate based on the center of parent block.\r
+        portX -= blockW / 2;\r
+        portY -= blockH / 2;\r
+        Orientation orientation = Orientation.EAST;\r
+        if ((portX) >= blockW * Math.abs(portY) / blockH) { // x>=w*|y|/h\r
+            orientation = Orientation.EAST;\r
+        } else if (portY >= blockH * Math.abs(portX) / blockW) { // y>=h*|x|/w\r
+            orientation = Orientation.SOUTH;\r
+        } else if (portX <= -blockW * Math.abs(portY) / blockH) { // x<=-w*|y|/h\r
+            orientation = Orientation.WEST;\r
+        } else if (portY <= -blockH * Math.abs(portX) / blockW) { // y<=-h*|x|/w\r
+            orientation = Orientation.NORTH;\r
+        }\r
+        return orientation;\r
+    }\r
+\r
+    /**\r
+     * Get the position of the source/target of a link.If dest is true, return source's position.\r
+     * If dest is false, return target's position.\r
+     *\r
+     * @param link\r
+     * @param dest\r
+     * @return the point of the position\r
+     */\r
+    private static mxPoint getLinkPortPosition(BasicLink link, boolean dest) {\r
+        mxPoint point = new mxPoint();\r
+        mxICell port = null;\r
+        if (dest) {\r
+            port = link.getSource();\r
+        } else {\r
+            port = link.getTarget();\r
+        }\r
+        if (port == null) {\r
+            return null;\r
+        }\r
+        if (port.getParent() instanceof SplitBlock) {\r
+            SplitBlock block = (SplitBlock) port.getParent();\r
+            point.setX(block.getGeometry().getCenterX());\r
+            point.setY(block.getGeometry().getCenterY());\r
+        } else if (port.getParent() instanceof BasicBlock) {\r
+            mxGeometry portGeo = port.getGeometry();\r
+            double portX = portGeo.getCenterX();\r
+            double portY = portGeo.getCenterY();\r
+            double portW = portGeo.getWidth();\r
+            double portH = portGeo.getHeight();\r
+            BasicBlock parent = (BasicBlock) port.getParent();\r
+            mxGeometry parentGeo = parent.getGeometry();\r
+            double blockX = parentGeo.getX();\r
+            double blockY = parentGeo.getY();\r
+            double blockW = parentGeo.getWidth();\r
+            double blockH = parentGeo.getHeight();\r
+            if (portGeo.isRelative()) {\r
+                portX *= blockW;\r
+                portY *= blockH;\r
+            }\r
+            point.setX(blockX + portX + portW / 2);\r
+            point.setY(blockY + portY + portH / 2);\r
+        }\r
+        return point;\r
+    }\r
+\r
+    /**\r
+     * Check if two points are coincident.\r
+     *\r
+     * @param x1\r
+     * @param y1\r
+     * @param x2\r
+     * @param y2\r
+     * @param isSplitBlock\r
+     * @return\r
+     */\r
+    protected static boolean isPointCoincident(double x1, double y1, double x2, double y2, boolean isSplitBlock) {\r
+        double error = XcosRouteUtils.ALIGN_STRICT_ERROR;\r
+        if (isSplitBlock) {\r
+            error = XcosRouteUtils.ALIGN_SPLITBLOCK_ERROR;\r
+        }\r
+        if (Math.abs(y1 - y2) <= error && Math.abs(x1 - x2) <= error\r
+                && (Math.abs(y1 - y2) + Math.abs(x1 - x2)) < error * 2) {\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check if two port are parallel.\r
+     *\r
+     * @param orientation1\r
+     * @param orientation2\r
+     * @return\r
+     */\r
+    protected static boolean isOrientationParallel(double x1, double y1, double x2, double y2, Orientation orientation1, Orientation orientation2) {\r
+        if (orientation1 == orientation2) {\r
+            return true;\r
+        }\r
+        double error = XcosRouteUtils.ALIGN_STRICT_ERROR;\r
+        if (Math.abs(y1 - y2) > error) {\r
+            // if they are not aligned horizontally,\r
+            if (orientation1 == Orientation.EAST && orientation2 == Orientation.WEST) {\r
+                return true;\r
+            }\r
+            if (orientation1 == Orientation.WEST && orientation2 == Orientation.EAST) {\r
+                return true;\r
+            }\r
+        }\r
+        if (Math.abs(x1 - x2) > error) {\r
+            // if they are not aligned vertically,\r
+            if (orientation1 == Orientation.SOUTH && orientation2 == Orientation.NORTH) {\r
+                return true;\r
+            }\r
+            if (orientation1 == Orientation.NORTH && orientation2 == Orientation.SOUTH) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Check if two lines are parallel.\r
+     *\r
+     * @param x1\r
+     *            the x-coordinate of the first point of the first line\r
+     * @param y1\r
+     *            the y-coordinate of the first point of the first line\r
+     * @param x2\r
+     *            the x-coordinate of the second point of the first line\r
+     * @param y2\r
+     *            the y-coordinate of the second point of the first line\r
+     * @param x3\r
+     *            the x-coordinate of the first point of the second line\r
+     * @param y3\r
+     *            the y-coordinate of the first point of the second line\r
+     * @param x4\r
+     *            the x-coordinate of the second point of the second line\r
+     * @param y4\r
+     *            the y-coordinate of the second point of the second line\r
+     * @param includeCoincide\r
+     *            true if including coincide situation\r
+     * @return <b>true</b> if two lines are parallel.\r
+     */\r
+    protected static boolean isLineParallel(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, boolean includeCoincide) {\r
+        double i = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);\r
+        // two lines are parallel and not coincide.\r
+        if (i == 0) {\r
+            if (includeCoincide || (!pointInLineSegment(x1, y1, x3, y3, x4, y4)\r
+                    && !pointInLineSegment(x2, y2, x3, y3, x4, y4) && !pointInLineSegment(x3, y3, x1, y1, x2, y2)\r
+                    && !pointInLineSegment(x4, y4, x1, y1, x2, y2))) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+}\r