Block Auto-Position - Split Block 85/18185/19
Chenfeng Zhu [Wed, 25 May 2016 11:19:20 +0000 (13:19 +0200)]
Option to set a new position for the SplitBlock.
Select the SplitBlocks and press 'P' to find a new position for the SplitBlocks and their links.

 * Automatically move the SplitBlocks.
 * Also, rework their links if it brings a better alignment.
 * Add menus in the menubar and contextmenu (only enabled when a SplitBlock is selected).
 * If there are multiple selected SplitBlocks on a link, they will be reordered if it provides a better layout.

Change-Id: I59dbbd734eb1ad5e3e4ea37523179474828a3fe2

19 files changed:
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_menu_format_auto_position.png [new file with mode: 0644]
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/fr_FR/xcos_menu_format_auto_position.png [new file with mode: 0644]
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/fr_FR/xcos_auto_position_split_block_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/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
scilab/modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosRouteUtils.java

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_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 cd45b81..fb20644 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/>
index 51c0f69..0a02215 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/>
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
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
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/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
index 92e8af6..f1d607e 100644 (file)
@@ -677,6 +677,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"
 
index 7f0a3df..aef0f1f 100644 (file)
@@ -1061,6 +1061,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
@@ -1089,7 +1104,7 @@ msgid "Vertical"
 msgstr ""
 
 #
-# File: src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 
+# 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 ""
index 4acc1f7..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;
@@ -145,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;
@@ -336,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));
     }
 
     /*
@@ -493,6 +495,12 @@ 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));
index bac3b80..2675dbd 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;
@@ -750,6 +751,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);
+            }
+        }
+
+    }
+
+
+}
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 a47c339..7f3c723 100644 (file)
@@ -210,7 +210,6 @@ public final class XcosMessages {
     public static final String LINK_STYLE_STRAIGHT = Messages.gettext("Straight");
     public static final String LINK_STYLE_HORIZONTAL = Messages.gettext("Horizontal");
     public static final String LINK_STYLE_VERTICAL = Messages.gettext("Vertical");
-    public static final String LINK_STYLE_OPTIMAL = Messages.gettext("Optimal");
 
     public static final String DEBUG_LEVEL_LABEL = "<html>" + Messages.gettext("Set debugging level (0,1,2,3) <br/> it performs scicos_debug(n)") + "</html>";
     public static final String SET_DEBUG = Messages.gettext("Execution trace and Debug");
@@ -311,6 +310,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
index eae33b8..ac8c617 100755 (executable)
@@ -36,28 +36,49 @@ import com.mxgraph.view.mxCellState;
 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(link, allOtherCells, graph);\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 {\r
-                // if it cannot get the route, keep the same or change it to\r
-                // straight or give a pop windows to inform user.\r
+            } else if (!lockPort) {\r
+                // if it cannot get the route, change it to straight.\r
             }\r
         }\r
     }\r
@@ -66,14 +87,13 @@ public class XcosRoute {
      * Get the turning points for the optimal route. If the straight route is the optimal route,\r
      * return null.\r
      *\r
-     * @param link\r
+     * @param sourceCell the source port\r
+     * @param targetCell the target port\r
      * @param allCells\r
      * @return list of turning points\r
      */\r
-    private boolean computeRoute(BasicLink link, Object[] allCells, XcosDiagram graph) {\r
+    protected boolean computeRoute(mxICell sourceCell, mxICell targetCell, Object[] allCells, XcosDiagram graph) {\r
         listRoute.clear();\r
-        mxICell sourceCell = link.getSource();\r
-        mxICell targetCell = link.getTarget();\r
         // if the link is not connected with BasicPort.\r
         if (!(sourceCell instanceof BasicPort) || !(targetCell instanceof BasicPort)) {\r
             return false;\r
@@ -116,29 +136,45 @@ public class XcosRoute {
             tgtx = targetCell.getParent().getGeometry().getCenterX();\r
             tgty = targetCell.getParent().getGeometry().getCenterY();\r
         }\r
-        // if two ports are aligned and there are no blocks between them,\r
-        // use straight route.\r
-        if ((XcosRouteUtils.isStrictlyAligned(srcx, srcy, tgtx, tgty))\r
-                && !XcosRouteUtils.checkObstacle(srcx, srcy, tgtx, tgty, allCells)) {\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
-            sourcePortOrien = this.getNewOrientation(sourceCell, srcx, srcy, targetCell, tgtx, tgty, link, graph);\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
-            targetPortOrien = this.getNewOrientation(targetCell, tgtx, tgty, sourceCell, srcx, srcy, link, graph);\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
+                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
+                    allCells, XcosRouteUtils.TRY_TIMES);\r
             if (list != null && list.size() > 0) {\r
                 listRoute.addAll(list);\r
                 return true;\r
@@ -152,7 +188,7 @@ public class XcosRoute {
      *\r
      * @return a list without non-redundant points\r
      */\r
-    private List<mxPoint> getNonRedundantPoints() {\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
@@ -160,8 +196,7 @@ public class XcosRoute {
                 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(),\r
-                                                      p3.getY())) {\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
@@ -187,45 +222,45 @@ public class XcosRoute {
      * @return\r
      */\r
     private mxPoint getPointAwayPort(mxICell port, double portX, double portY, Orientation orien,\r
-                                     Object[] allCells, XcosDiagram graph) {\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) || XcosRouteUtils\r
-                            .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) || XcosRouteUtils\r
-                            .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) || XcosRouteUtils\r
-                            .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) || XcosRouteUtils\r
-                            .checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
-                    point.setY(point.getY() + XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
-                }\r
-                break;\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
@@ -277,12 +312,11 @@ public class XcosRoute {
      * @param otherCell\r
      * @param ox\r
      * @param oy\r
-     * @param link\r
      * @param graph\r
      * @return\r
      */\r
     private Orientation getNewOrientation(mxICell cell, double cx, double cy, mxICell otherCell, double ox,\r
-                                          double oy, BasicLink link, XcosDiagram graph) {\r
+            double oy, XcosDiagram graph) {\r
         Orientation orientation = Orientation.EAST;\r
         if (cell.getParent() instanceof SplitBlock) {\r
             SplitBlock block = (SplitBlock) cell.getParent();\r
@@ -441,7 +475,7 @@ public class XcosRoute {
      *            SplitBlock, too.\r
      * @return a new array of all objects excluding selves\r
      */\r
-    private Object[] getAllOtherCells(Object[] all, Object... self) {\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
@@ -461,9 +495,11 @@ public class XcosRoute {
         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)) {\r
-                // only add normal Blocks.\r
-                listnew.add(o);\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
index 85b6b12..aebf918 100755 (executable)
@@ -17,7 +17,6 @@ import java.util.Collections;
 import java.util.LinkedHashMap;\r
 import java.util.List;\r
 import java.util.Map;\r
-\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
@@ -41,6 +40,8 @@ public abstract class XcosRouteUtils {
      */\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
@@ -162,14 +163,34 @@ public abstract class XcosRouteUtils {
      * @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)) {\r
+                    if (checkLinesIntersection(x1, y1, x2, y2, l) && isStrict) {\r
                         // check intersection.\r
-                        // return true;\r
+                        return true;\r
                     }\r
                     if (checkLinesCoincide(x1, y1, x2, y2, l)) {\r
                         // check superimposition.\r
@@ -224,7 +245,7 @@ public abstract class XcosRouteUtils {
                         continue;\r
                     }\r
                     mxGeometry childGeo = new mxGeometry(child.getGeometry().getX(), child.getGeometry().getY(),\r
-                                                         child.getGeometry().getWidth(), child.getGeometry().getHeight());\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
@@ -248,7 +269,7 @@ public abstract class XcosRouteUtils {
                 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
+                if (x >= blockx && x <= (blockx + width) && y >= blocky && y <= (blocky + height)) {\r
                     return true;\r
                 }\r
             }\r
@@ -257,6 +278,25 @@ public abstract class XcosRouteUtils {
     }\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
@@ -315,9 +355,15 @@ public abstract class XcosRouteUtils {
             // if the edge is straight or vertical or horizontal style, there is\r
             // no way to check.\r
         } else {\r
-            listPoints.add(getLinkPortPosition(link, true));\r
+            if (getLinkPortPosition(link, true) != null) {\r
+                listPoints.add(getLinkPortPosition(link, true));\r
+            }\r
             listPoints.addAll(link.getGeometry().getPoints());\r
-            listPoints.add(getLinkPortPosition(link, false));\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
@@ -354,8 +400,7 @@ public abstract class XcosRouteUtils {
      *            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,\r
-                                         double x4, double y4) {\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
@@ -406,8 +451,7 @@ public abstract class XcosRouteUtils {
      *            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,\r
-            double x4, double y4) {\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
@@ -428,17 +472,74 @@ public abstract class XcosRouteUtils {
     }\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\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
@@ -683,7 +784,7 @@ public abstract class XcosRouteUtils {
                     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
+                        || (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
@@ -699,7 +800,7 @@ public abstract class XcosRouteUtils {
         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
+                || ((mid_temp < nMax && mid_temp > nMin) && (mid < nMin || mid > nMax))) {\r
             start = start_temp;\r
             end = end_temp;\r
         }\r
@@ -911,7 +1012,7 @@ public abstract class XcosRouteUtils {
     }\r
 \r
     /**\r
-     * Get the position of the source/target of a link. If dest is true, return source's position.\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
@@ -955,4 +1056,95 @@ public abstract class XcosRouteUtils {
         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