Scilab 5.5.2 is able to open the newly saved files, but the ports have to be repositioned manually.
* Implicit fixed-size step ODE solver added: Crank-Nicolson 2(3).
Added to the CVode package, it also benefits from the CVode rootfinding feature.
+* Added a new link style (`Optimal`) for automatically finding the optimal route.
+* Automatically reposition split blocks for better-looking layout.
* Block modifications :
- `INVBLK`: add a divide by zero parameter to ignore the error
- `PRODUCT`: add a divide by zero parameter to ignore the error
--- /dev/null
+ Changes between version 5.5.2 and 6.0.0-beta-1
+ ==============================================
+
+Xcos
+====
+
+* Optimal - new link style for avoiding links crossing the blocks.
+
#define SCI_VERSION_MAJOR @SCILAB_VERSION_MAJOR@
#define SCI_VERSION_MINOR @SCILAB_VERSION_MINOR@
#define SCI_VERSION_MAINTENANCE @SCILAB_VERSION_MAINTENANCE@
-#define SCI_VERSION_STRING "scilab-branch-master"
-#define SCI_VERSION_WIDE_STRING L"scilab-branch-master"
+#define SCI_VERSION_STRING "scilab-branch-xcos-layout"
+#define SCI_VERSION_WIDE_STRING L"scilab-branch-xcos-layout"
/* SCI_VERSION_REVISION --> hash key commit */
#define SCI_VERSION_REVISION 0
#define SCI_VERSION_TIMESTAMP 0
/* for compatibility */
/* Deprecated */
#define SCI_VERSION SCI_VERSION_STRING
-#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-master"
+#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-xcos-layout"
#endif
/*--------------------------------------------------------------------------*/
#define SCI_VERSION_MAJOR 6
#define SCI_VERSION_MINOR 0
#define SCI_VERSION_MAINTENANCE 0
-#define SCI_VERSION_STRING "scilab-branch-master"
-#define SCI_VERSION_WIDE_STRING L"scilab-branch-master"
+#define SCI_VERSION_STRING "scilab-branch-xcos-layout"
+#define SCI_VERSION_WIDE_STRING L"scilab-branch-xcos-layout"
/* SCI_VERSION_REVISION --> hash key commit */
#define SCI_VERSION_REVISION 0
#define SCI_VERSION_TIMESTAMP 0
/* for compatibility */
/* Deprecated */
#define SCI_VERSION SCI_VERSION_STRING
-#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-master"
+#define DEFAULT_SCI_VERSION_MESSAGE L"scilab-branch-xcos-layout"
#endif
/*--------------------------------------------------------------------------*/
</listitem>
<listitem>
<para>
+ <emphasis role="bold">Format:Auto-Position</emphasis>
+ </para>
+ <para/>
+ <mediaobject>
+ <imageobject>
+ <imagedata align="center" fileref="../gui/xcos_menu_entries/en_US/xcos_menu_format_auto_position.png"/>
+ </imageobject>
+ </mediaobject>
+ <para/>
+ <para>
+ This menu allows to change the position of the block.
+ </para>
+ <para>
+ First select the block(s) and select the appropriate menu item or use the shortcuts
+ (<emphasis>P</emphasis>).
+ The following list shows the results obtained.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Split Block (<emphasis>P</emphasis>)
+ </para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="../images/xcos_menu_entries/en_US/xcos_auto_position_split_block_en_US.png"/>
+ </imageobject>
+ </mediaobject>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ <listitem>
+ <para>
<emphasis role="bold">Format:Link Style</emphasis>
</para>
<para/>
</para>
<para>
First select the link and select the appropriate menu item or use the shortcuts
- (<emphasis>H</emphasis>), <emphasis>S</emphasis>, <emphasis>V</emphasis>).
+ (<emphasis>H</emphasis>), (<emphasis>S</emphasis>), (<emphasis>V</emphasis>), (<emphasis>O</emphasis>).
The following list shows the results obtained.
</para>
<itemizedlist>
</imageobject>
</mediaobject>
</listitem>
+ <listitem>
+ <para>
+ Optimal (<emphasis>O</emphasis>)
+ </para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="../images/xcos_menu_entries/en_US/xcos_link_optimal_en_US.png"/>
+ </imageobject>
+ </mediaobject>
+ </listitem>
</itemizedlist>
</listitem>
<listitem>
</listitem>
<listitem>
<para>
+ <emphasis role="bold">Format : Auto-positionnement</emphasis>
+ </para>
+ <para/>
+ <mediaobject>
+ <imageobject>
+ <imagedata align="center" fileref="../gui/xcos_menu_entries/fr_FR/xcos_menu_format_auto_position.png"/>
+ </imageobject>
+ </mediaobject>
+ <para/>
+ <para>
+ Ce menu permet de modifier automatiquement la position des blocs.
+ </para>
+ <para>
+ Sélectionner le(s) bloc(s) puis le menu adéquat ou utiliser le raccourci
+ (<emphasis>P</emphasis>).
+ La liste suivante montre les résultats obtenus.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Bloc split (<emphasis>P</emphasis>)
+ </para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="../images/xcos_menu_entries/fr_FR/xcos_auto_position_split_block_fr_FR.png"/>
+ </imageobject>
+ </mediaobject>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ <listitem>
+ <para>
<emphasis role="bold">Format : Style de liaison</emphasis>
</para>
<para/>
</imageobject>
</mediaobject>
</listitem>
+ <listitem>
+ <para>Optimal</para>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="../images/xcos_menu_entries/fr_FR/xcos_link_optimal_fr_FR.png"/>
+ </imageobject>
+ </mediaobject>
+ </listitem>
</itemizedlist>
<para/>
</listitem>
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"
msgid "Vertical"
msgstr "Vertical"
+msgid "Optimal"
+msgstr "Optimal"
+
msgid "Set debugging level (0,1,2,3) <br/> it performs scicos_debug(n)"
msgstr ""
"Configurer le niveau de débogage (0, 1, 2, 3) <br/> avec scicos_debug(n)"
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
msgstr ""
#
+# File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line:
+# File: modules/xcos/src/xcos_fake_xml.c, line:
+msgid "Optimal"
+msgstr ""
+
+#
# File: modules/xcos/src/java/org/scilab/modules/xcos/utils/XcosMessages.java, line: 214
msgid "Set debugging level (0,1,2,3) <br/> it performs scicos_debug(n)"
msgstr ""
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;
import org.scilab.modules.xcos.configuration.utils.ConfigurationConstants;
import org.scilab.modules.xcos.graph.XcosDiagram;
import org.scilab.modules.xcos.link.actions.StyleHorizontalAction;
+import org.scilab.modules.xcos.link.actions.StyleOptimalAction;
import org.scilab.modules.xcos.link.actions.StyleStraightAction;
import org.scilab.modules.xcos.link.actions.StyleVerticalAction;
import org.scilab.modules.xcos.palette.actions.ViewPaletteBrowserAction;
private Menu simulate;
private Menu format;
private Menu alignMenu;
+ private Menu blockPosition;
private Menu linkStyle;
private Menu tools;
private Menu help;
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));
}
/*
format.add(FilledColorAction.createMenu(diagram));
format.addSeparator();
+ blockPosition = ScilabMenu.createMenu();
+ blockPosition.setText(XcosMessages.BLOCK_AUTO_POSITION);
+ blockPosition.add(AutoPositionSplitBlockAction.createMenu(diagram));
+ format.add(blockPosition);
+ format.addSeparator();
+
linkStyle = ScilabMenu.createMenu();
linkStyle.setText(XcosMessages.LINK_STYLE);
linkStyle.add(StyleHorizontalAction.createMenu(diagram));
linkStyle.add(StyleStraightAction.createMenu(diagram));
linkStyle.add(StyleVerticalAction.createMenu(diagram));
+ linkStyle.add(StyleOptimalAction.createMenu(diagram));
format.add(linkStyle);
format.addSeparator();
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;
/*--- */
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));
*/
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.
}
/**
+ * 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
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+ }
+
+
+}
import org.scilab.modules.xcos.block.actions.BorderColorAction;
import org.scilab.modules.xcos.graph.model.XcosCell;
import org.scilab.modules.xcos.link.actions.StyleHorizontalAction;
+import org.scilab.modules.xcos.link.actions.StyleOptimalAction;
import org.scilab.modules.xcos.link.actions.StyleStraightAction;
import org.scilab.modules.xcos.link.actions.StyleVerticalAction;
import org.scilab.modules.xcos.preferences.XcosOptions;
linkStyle.add(StyleHorizontalAction.createMenu(graph));
linkStyle.add(StyleStraightAction.createMenu(graph));
linkStyle.add(StyleVerticalAction.createMenu(graph));
+ linkStyle.add(StyleOptimalAction.createMenu(graph));
menu.add(linkStyle);
--- /dev/null
+/*\r
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab\r
+ * Copyright (C) 2015 - Chenfeng ZHU\r
+ *\r
+ * This file must be used under the terms of the CeCILL.\r
+ * This source file is licensed as described in the file COPYING, which\r
+ * you should have received as part of this distribution. The terms\r
+ * are also available at\r
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt\r
+ *\r
+ */\r
+\r
+package org.scilab.modules.xcos.link.actions;\r
+\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.KeyEvent;\r
+\r
+import org.scilab.modules.graph.ScilabComponent;\r
+import org.scilab.modules.graph.ScilabGraph;\r
+import org.scilab.modules.gui.menuitem.MenuItem;\r
+import org.scilab.modules.xcos.graph.XcosDiagram;\r
+import org.scilab.modules.xcos.link.BasicLink;\r
+import org.scilab.modules.xcos.utils.XcosMessages;\r
+import org.scilab.modules.xcos.utils.XcosRoute;\r
+\r
+import com.mxgraph.util.mxConstants;\r
+\r
+/**\r
+ * Implement the set link optimal action.\r
+ */\r
+@SuppressWarnings(value = { "serial" })\r
+public class StyleOptimalAction extends StyleAction {\r
+ /** Name of the action */\r
+ public static final String NAME = XcosMessages.LINK_STYLE_OPTIMAL;\r
+ /** Icon name of the action */\r
+ public static final String SMALL_ICON = "";\r
+ /** Mnemonic key of the action */\r
+ public static final int MNEMONIC_KEY = KeyEvent.VK_O;\r
+\r
+ /**\r
+ * Default constructor the associated graph\r
+ *\r
+ * @param scilabGraph\r
+ * the graph to associate\r
+ */\r
+ public StyleOptimalAction(ScilabGraph scilabGraph) {\r
+ super(scilabGraph);\r
+ }\r
+\r
+ /**\r
+ * @param scilabGraph\r
+ * @return menu item\r
+ */\r
+ public static MenuItem createMenu(ScilabGraph scilabGraph) {\r
+ return createMenu(scilabGraph, StyleOptimalAction.class);\r
+ }\r
+\r
+ /**\r
+ * Action.\r
+ *\r
+ * @param e\r
+ * @see org.scilab.modules.xcos.link.actions.StyleAction#actionPerformed(java.awt.event.ActionEvent)\r
+ */\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ final XcosDiagram graph = (XcosDiagram) getGraph(e);\r
+\r
+ // action disabled when the cell is edited\r
+ final ScilabComponent comp = ((ScilabComponent) graph.getAsComponent());\r
+ if (comp.isEditing()) {\r
+ return;\r
+ }\r
+\r
+ final Object[] links = getLinks();\r
+\r
+ graph.getModel().beginUpdate();\r
+ try {\r
+ double scale = graph.getView().getScale();\r
+ graph.getView().setScale(1.0);\r
+ graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE, "1", links);\r
+ reset(graph, links);\r
+ this.updateLinkOptimal(graph, links);\r
+ graph.getView().setScale(scale);\r
+ } finally {\r
+ graph.getModel().endUpdate();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Update the style of links one by one.\r
+ *\r
+ * @param graph\r
+ * @param links\r
+ */\r
+ public void updateLinkOptimal(XcosDiagram graph, Object[] links) {\r
+ Object[] allChildCells = graph.getChildCells(graph.getDefaultParent());\r
+ XcosRoute route = new XcosRoute();\r
+ for (Object o : links) {\r
+ if (o instanceof BasicLink) {\r
+ BasicLink link = (BasicLink) o;\r
+ route.updateRoute(link, allChildCells, graph);\r
+ }\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+/*
+ * 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);
+ }
+
+}
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
--- /dev/null
+/*\r
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab\r
+ * Copyright (C) 2015 - Chenfeng ZHU\r
+ *\r
+ * This file must be used under the terms of the CeCILL.\r
+ * This source file is licensed as described in the file COPYING, which\r
+ * you should have received as part of this distribution. The terms\r
+ * are also available at\r
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt\r
+ *\r
+ */\r
+\r
+package org.scilab.modules.xcos.utils;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import org.scilab.modules.xcos.block.BasicBlock;\r
+import org.scilab.modules.xcos.block.SplitBlock;\r
+import org.scilab.modules.xcos.graph.XcosDiagram;\r
+import org.scilab.modules.xcos.link.BasicLink;\r
+import org.scilab.modules.xcos.port.BasicPort;\r
+import org.scilab.modules.xcos.port.Orientation;\r
+import org.scilab.modules.xcos.port.command.CommandPort;\r
+import org.scilab.modules.xcos.port.control.ControlPort;\r
+import org.scilab.modules.xcos.port.input.InputPort;\r
+import org.scilab.modules.xcos.port.output.OutputPort;\r
+\r
+import com.mxgraph.model.mxGeometry;\r
+import com.mxgraph.model.mxGraphModel;\r
+import com.mxgraph.model.mxICell;\r
+import com.mxgraph.util.mxPoint;\r
+import com.mxgraph.view.mxCellState;\r
+\r
+public class XcosRoute {\r
+\r
+ private List<mxPoint> listRoute = new ArrayList<mxPoint>(0);\r
+ // if it is true, the position of the port will not be changed.\r
+ private boolean lockPortPosition = false;\r
+\r
+ public List<mxPoint> getList() {\r
+ return listRoute;\r
+ }\r
+\r
+ /**\r
+ * Update the Edge.\r
+ *\r
+ * @param link\r
+ * @param allCells\r
+ * @param graph\r
+ */\r
+ public void updateRoute(BasicLink link, Object[] allCells, XcosDiagram graph) {\r
+ this.lockPortPosition = false;\r
+ this.updateRoute(link, allCells, graph, lockPortPosition);\r
+ }\r
+\r
+ /**\r
+ * Update the Edge.\r
+ *\r
+ * @param link\r
+ * @param allCells\r
+ * @param graph\r
+ * @param lockPort\r
+ * if it is true, there is no need to calculate the orientation.\r
+ */\r
+ public void updateRoute(BasicLink link, Object[] allCells, XcosDiagram graph, boolean lockPort) {\r
+ this.lockPortPosition = lockPort;\r
+ mxICell sourceCell = link.getSource();\r
+ mxICell targetCell = link.getTarget();\r
+ Object[] allOtherCells = getAllOtherCells(allCells, link, sourceCell, targetCell);\r
+ if (sourceCell != null && targetCell != null) {\r
+ boolean isGetRoute = this.computeRoute(sourceCell, targetCell, allOtherCells, graph);\r
+ if (isGetRoute) {\r
+ List<mxPoint> list = this.getNonRedundantPoints();\r
+ mxGeometry geometry = new mxGeometry();\r
+ geometry.setPoints(list);\r
+ ((mxGraphModel) (graph.getModel())).setGeometry(link, geometry);\r
+ listRoute.clear();\r
+ } else if (!lockPort) {\r
+ // if it cannot get the route, change it to straight.\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the turning points for the optimal route. If the straight route is the optimal route,\r
+ * return null.\r
+ *\r
+ * @param sourceCell the source port\r
+ * @param targetCell the target port\r
+ * @param allCells\r
+ * @return list of turning points\r
+ */\r
+ protected boolean computeRoute(mxICell sourceCell, mxICell targetCell, Object[] allCells, XcosDiagram graph) {\r
+ listRoute.clear();\r
+ // if the link is not connected with BasicPort.\r
+ if (!(sourceCell instanceof BasicPort) || !(targetCell instanceof BasicPort)) {\r
+ return false;\r
+ }\r
+ Orientation sourcePortOrien = null;\r
+ Orientation targetPortOrien = null;\r
+ double srcx = 0;\r
+ double srcy = 0;\r
+ double tgtx = 0;\r
+ double tgty = 0;\r
+ mxPoint sourcePoint = new mxPoint(srcx, srcy);\r
+ mxPoint targetPoint = new mxPoint(tgtx, tgty);\r
+ // if source is a port, get a new start point.\r
+ if (sourceCell instanceof BasicPort) {\r
+ mxCellState state = graph.getView().getState(sourceCell);\r
+ if (state != null) {\r
+ srcx = state.getCenterX();\r
+ srcy = state.getCenterY();\r
+ BasicPort sourcePort = (BasicPort) sourceCell;\r
+ sourcePortOrien = getPortRelativeOrientation(sourcePort, graph);\r
+ }\r
+ }\r
+ // if target is a port, get a new end point.\r
+ if (targetCell instanceof BasicPort) {\r
+ mxCellState state = graph.getView().getState(targetCell);\r
+ if (state != null) {\r
+ tgtx = state.getCenterX();\r
+ tgty = state.getCenterY();\r
+ BasicPort targetPort = (BasicPort) targetCell;\r
+ targetPortOrien = getPortRelativeOrientation(targetPort, graph);\r
+ }\r
+ }\r
+ // if source belongs to a SplitBlock\r
+ if (sourceCell.getParent() instanceof SplitBlock) {\r
+ srcx = sourceCell.getParent().getGeometry().getCenterX();\r
+ srcy = sourceCell.getParent().getGeometry().getCenterY();\r
+ }\r
+ // if target is a SplitBlock\r
+ if (targetCell.getParent() instanceof SplitBlock) {\r
+ tgtx = targetCell.getParent().getGeometry().getCenterX();\r
+ tgty = targetCell.getParent().getGeometry().getCenterY();\r
+ }\r
+ // if two points are coincident, use straignt route.\r
+ boolean isSplitBlock = (sourceCell.getParent() instanceof SplitBlock) && (targetCell.getParent() instanceof SplitBlock);\r
+ if (XcosRouteUtils.isPointCoincident(srcx, srcy, tgtx, tgty, isSplitBlock)) {\r
+ return true;\r
+ }\r
+ // if two ports are aligned and there are no blocks between them, use straight route.\r
+ if (XcosRouteUtils.isStrictlyAligned(srcx, srcy, tgtx, tgty)\r
+ && !XcosRouteUtils.checkObstacle(srcx, srcy, tgtx, tgty, allCells)\r
+ && !XcosRouteUtils.isOrientationParallel(srcx, srcy, tgtx, tgty, sourcePortOrien, targetPortOrien)) {\r
+ return true;\r
+ }\r
+ // re-calculate the orientation for the SplitBlock.\r
+ if (sourceCell.getParent() instanceof SplitBlock) {\r
+ if (lockPortPosition) { // if it is locked, use the default orientation.\r
+ sourcePortOrien = ((BasicPort) sourceCell).getOrientation();\r
+ } else {\r
+ sourcePortOrien = this.getNewOrientation(sourceCell, srcx, srcy, targetCell, tgtx, tgty, graph);\r
+ }\r
+ }\r
+ if (targetCell.getParent() instanceof SplitBlock) {\r
+ if (lockPortPosition) { // if it is locked, use the default orientation.\r
+ targetPortOrien = ((BasicPort) targetCell).getOrientation();\r
+ } else {\r
+ targetPortOrien = this.getNewOrientation(targetCell, tgtx, tgty, sourceCell, srcx, srcy, graph);\r
+ }\r
+ }\r
+ sourcePoint = getPointAwayPort(sourceCell, srcx, srcy, sourcePortOrien, allCells, graph);\r
+ targetPoint = getPointAwayPort(targetCell, tgtx, tgty, targetPortOrien, allCells, graph);\r
+ allCells = Arrays.copyOf(allCells, allCells.length + 2);\r
+ allCells[allCells.length - 2] = sourceCell;\r
+ allCells[allCells.length - 1] = targetCell;\r
+ List<mxPoint> list = XcosRouteUtils.getSimpleRoute(sourcePoint, sourcePortOrien, targetPoint,\r
+ targetPortOrien, allCells);\r
+ if (list != null && list.size() > 0) {\r
+ listRoute.addAll(list);\r
+ return true;\r
+ } else {\r
+ list = XcosRouteUtils.getComplexRoute(sourcePoint, sourcePortOrien, targetPoint, targetPortOrien,\r
+ allCells, XcosRouteUtils.TRY_TIMES);\r
+ if (list != null && list.size() > 0) {\r
+ listRoute.addAll(list);\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Remove the redundancy points from the route.\r
+ *\r
+ * @return a list without non-redundant points\r
+ */\r
+ protected List<mxPoint> getNonRedundantPoints() {\r
+ List<mxPoint> list = new ArrayList<mxPoint>(0);\r
+ if (listRoute.size() > 2) {\r
+ list.add(listRoute.get(0));\r
+ for (int i = 1; i < listRoute.size() - 1; i++) {\r
+ mxPoint p1 = list.get(list.size() - 1);\r
+ mxPoint p2 = listRoute.get(i);\r
+ mxPoint p3 = listRoute.get(i + 1);\r
+ if (XcosRouteUtils.pointInLineSegment(p2.getX(), p2.getY(), p1.getX(), p1.getY(), p3.getX(), p3.getY())) {\r
+ // if p2 is in the line segment between p1 and p3, remove it.\r
+ continue;\r
+ } else {\r
+ list.add(p2);\r
+ }\r
+ }\r
+ list.add(listRoute.get(listRoute.size() - 1));\r
+ } else {\r
+ // if the route has less than 3 points, there is no redundancy.\r
+ list.addAll(listRoute);\r
+ }\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * According to the relative position (orientation) of the port, get a point which is\r
+ * XcosRoute.BEAUTY_DISTANCE away from the port and out of block.\r
+ *\r
+ * @param port\r
+ * @param portX\r
+ * @param portY\r
+ * @param graph\r
+ * @return\r
+ */\r
+ private mxPoint getPointAwayPort(mxICell port, double portX, double portY, Orientation orien,\r
+ Object[] allCells, XcosDiagram graph) {\r
+ mxPoint point = new mxPoint(portX, portY);\r
+ double distance = XcosRouteUtils.BEAUTY_AWAY_DISTANCE;\r
+ if (port.getParent() instanceof SplitBlock) {\r
+ distance = XcosRouteUtils.SPLITBLOCK_AWAY_DISTANCE;\r
+ }\r
+ switch (orien) {\r
+ case EAST:\r
+ point.setX(point.getX() + distance);\r
+ while (Math.abs(point.getX() - portX) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+ && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+ || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+ point.setX(point.getX() - XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+ }\r
+ break;\r
+ case SOUTH:\r
+ point.setY(point.getY() + distance);\r
+ while (Math.abs(point.getY() - portY) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+ && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+ || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+ point.setY(point.getY() - XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+ }\r
+ break;\r
+ case WEST:\r
+ point.setX(point.getX() - distance);\r
+ while (Math.abs(point.getX() - portX) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+ && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+ || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+ point.setX(point.getX() + XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+ }\r
+ break;\r
+ case NORTH:\r
+ point.setY(point.getY() - distance);\r
+ while (Math.abs(point.getY() - portY) > XcosRouteUtils.BEAUTY_AWAY_REVISION\r
+ && (XcosRouteUtils.checkObstacle(portX, portY, point.getX(), point.getY(), allCells)\r
+ || XcosRouteUtils.checkPointInBlocks(point.getX(), point.getY(), allCells))) {\r
+ point.setY(point.getY() + XcosRouteUtils.BEAUTY_AWAY_REVISION);\r
+ }\r
+ break;\r
+ }\r
+ return point;\r
+ }\r
+\r
+ /**\r
+ * As BasicPort.getOrientation is the default orientation, the Orientation is not correct when\r
+ * the block is mirrored or flipped. This method could get the current Orientation of the port.\r
+ *\r
+ * @param port\r
+ * @param graph\r
+ * @return\r
+ */\r
+ private Orientation getPortRelativeOrientation(BasicPort port, XcosDiagram graph) {\r
+ // the coordinate (x,y) for the port.\r
+ double portx = graph.getView().getState(port).getCenterX();\r
+ double porty = graph.getView().getState(port).getCenterY();\r
+ // the coordinate (x,y) and the width-height for the parent block\r
+ mxICell parent = port.getParent();\r
+ double blockx = graph.getView().getState(parent).getCenterX();\r
+ double blocky = graph.getView().getState(parent).getCenterY();\r
+ double blockw = parent.getGeometry().getWidth();\r
+ double blockh = parent.getGeometry().getHeight();\r
+ // calculate relative coordinate based on the center of parent block.\r
+ portx -= blockx;\r
+ porty -= blocky;\r
+ Orientation orientation = port.getOrientation();\r
+ if ((portx) >= blockw * Math.abs(porty) / blockh) { // x>=w*|y|/h\r
+ orientation = Orientation.EAST;\r
+ } else if (porty >= blockh * Math.abs(portx) / blockw) { // y>=h*|x|/w\r
+ orientation = Orientation.SOUTH;\r
+ } else if (portx <= -blockw * Math.abs(porty) / blockh) { // x<=-w*|y|/h\r
+ orientation = Orientation.WEST;\r
+ } else if (porty <= -blockh * Math.abs(portx) / blockw) { // y<=-h*|x|/w\r
+ orientation = Orientation.NORTH;\r
+ }\r
+ return orientation;\r
+ }\r
+\r
+ /**\r
+ * Special use to calculate the orientation when\r
+ * <ol>\r
+ * <li>it is a SplitBlock.</li>\r
+ * <li>there is no connection.</li>\r
+ * <ol>\r
+ *\r
+ * @param cell\r
+ * @param cx\r
+ * @param cy\r
+ * @param otherCell\r
+ * @param ox\r
+ * @param oy\r
+ * @param graph\r
+ * @return\r
+ */\r
+ private Orientation getNewOrientation(mxICell cell, double cx, double cy, mxICell otherCell, double ox,\r
+ double oy, XcosDiagram graph) {\r
+ Orientation orientation = Orientation.EAST;\r
+ if (cell.getParent() instanceof SplitBlock) {\r
+ SplitBlock block = (SplitBlock) cell.getParent();\r
+ Orientation inputOrien = this.getSplitBlockInputOrientation(block, graph);\r
+ if ((cell instanceof InputPort) || (cell instanceof ControlPort)) {\r
+ // if it is the InputPort/ControlPort of the SplitBlock,\r
+ orientation = inputOrien;\r
+ } else if ((cell instanceof OutputPort) || (cell instanceof CommandPort)) {\r
+ // if it is one of the OutputPorts/CommandPorts of the SplitBlock,\r
+ // it depends on the Orientation of InputPort and the other OutputPort.\r
+ BasicPort port = (BasicPort) cell;\r
+ BasicPort otherPort = null;\r
+ if (port == block.getOut1()) {\r
+ otherPort = block.getOut2();\r
+ } else {\r
+ otherPort = block.getOut1();\r
+ }\r
+ mxICell otherLink = otherPort.getEdgeAt(0);\r
+ if (otherLink instanceof BasicLink) {\r
+ mxICell otherTarget = ((BasicLink) otherLink).getTarget();\r
+ mxPoint otgtPoint = this.getCenterPoint(otherTarget, graph);\r
+ double otx = otgtPoint.getX();\r
+ double oty = otgtPoint.getY();\r
+ double otcx = cx - otx;\r
+ double otcy = cy - oty;\r
+ double otox = ox - otx;\r
+ double otoy = oy - oty;\r
+ double value = (otcx * otoy) - (otox * otcy);\r
+ if (ox >= cx && oy <= cy) {\r
+ // when target is on NORTHEAST to source\r
+ if (value >= 0 && inputOrien != Orientation.NORTH) {\r
+ // other OutputPort is on the right\r
+ orientation = Orientation.NORTH;\r
+ } else if (inputOrien != Orientation.EAST) {\r
+ // other OutputPort is on the left\r
+ orientation = Orientation.EAST;\r
+ } else {\r
+ orientation = Orientation.NORTH;\r
+ }\r
+ } else if (ox >= cx && oy >= cy) {\r
+ // when target is on SOUTHEAST to source\r
+ if (value >= 0 && inputOrien != Orientation.EAST) {\r
+ // other OutputPort is on the right\r
+ orientation = Orientation.EAST;\r
+ } else if (inputOrien != Orientation.SOUTH) {\r
+ // other OutputPort is on the left\r
+ orientation = Orientation.SOUTH;\r
+ } else {\r
+ orientation = Orientation.EAST;\r
+ }\r
+ } else if (ox <= cx && oy <= cy) {\r
+ // when target is on NORTHWEST to source\r
+ if (value >= 0 && inputOrien != Orientation.WEST) {\r
+ // other OutputPort is on the right\r
+ orientation = Orientation.WEST;\r
+ } else if (inputOrien != Orientation.NORTH) {\r
+ // other OutputPort is on the left\r
+ orientation = Orientation.NORTH;\r
+ } else {\r
+ orientation = Orientation.WEST;\r
+ }\r
+ } else if (ox <= cx && oy >= cy) {\r
+ // when target is on SOUTHWEST to source\r
+ if (value >= 0 && inputOrien != Orientation.SOUTH) {\r
+ // other OutputPort is on the right\r
+ orientation = Orientation.SOUTH;\r
+ } else if (inputOrien != Orientation.WEST) {\r
+ // other OutputPort is on the left\r
+ orientation = Orientation.WEST;\r
+ } else {\r
+ orientation = Orientation.SOUTH;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return orientation;\r
+ }\r
+\r
+ /**\r
+ * Get the Orientation of the InputPort of a SplitBlock.\r
+ *\r
+ * @param block\r
+ * the SplitBlock\r
+ * @return the orientation of its InputPort.\r
+ */\r
+ private Orientation getSplitBlockInputOrientation(SplitBlock block, XcosDiagram graph) {\r
+ Orientation orientation = Orientation.EAST;\r
+ BasicPort inp = block.getIn();\r
+ // the position of the Input\r
+ double inX = block.getGeometry().getCenterX();\r
+ double inY = block.getGeometry().getCenterY();\r
+ mxICell link = inp.getEdgeAt(0);\r
+ // the position of the source\r
+ double srcX = 0;\r
+ double srcY = 0;\r
+ if (link instanceof BasicLink) {\r
+ mxICell source = ((BasicLink) link).getSource();\r
+ mxPoint srcPoint = this.getCenterPoint(source, graph);\r
+ srcX = srcPoint.getX();\r
+ srcY = srcPoint.getY();\r
+ }\r
+ // the relative position of source to Input\r
+ double dx = srcX - inX;\r
+ double dy = srcY - inY;\r
+ if (dx >= Math.abs(dy)) { // x>=|y|\r
+ orientation = Orientation.EAST; // source is on the east of Input.\r
+ } else if (dy >= Math.abs(dx)) { // y>=|x|\r
+ orientation = Orientation.SOUTH;\r
+ } else if (dx <= -Math.abs(dy)) { // x<=-|y|\r
+ orientation = Orientation.WEST;\r
+ } else if (dy <= -Math.abs(dx)) { // y<=-|x|\r
+ orientation = Orientation.NORTH;\r
+ }\r
+ return orientation;\r
+ }\r
+\r
+ /**\r
+ * Get the position of a cell.\r
+ *\r
+ * @param cell\r
+ * @return\r
+ */\r
+ private mxPoint getCenterPoint(mxICell cell, XcosDiagram graph) {\r
+ mxPoint point = new mxPoint(0, 0);\r
+ if (cell instanceof BasicPort) {\r
+ BasicPort port = (BasicPort) cell;\r
+ mxICell parent = port.getParent();\r
+ if (parent instanceof SplitBlock) {\r
+ // if it belongs to a SplitBlock, use SplitBlock's geometry.\r
+ point.setX(parent.getGeometry().getCenterX());\r
+ point.setY(parent.getGeometry().getCenterY());\r
+ } else {\r
+ mxCellState state = graph.getView().getState(cell);\r
+ if (state != null) {\r
+ point.setX(state.getCenterX());\r
+ point.setY(state.getCenterY());\r
+ }\r
+ }\r
+ } else if (cell instanceof BasicBlock) {\r
+ // if it is a block, use its geometry.\r
+ BasicBlock block = (BasicBlock) cell;\r
+ point.setX(block.getGeometry().getCenterX());\r
+ point.setY(block.getGeometry().getCenterY());\r
+ }\r
+ return point;\r
+ }\r
+\r
+ /**\r
+ * Remove the selves from the array of all.<br/>\r
+ * except for selves, SplitBlock and the ports of links in selves. add other ports of blocks.\r
+ *\r
+ * @param all\r
+ * @param self\r
+ * If self's source port or target port belongs to a SpliBlock, remove this\r
+ * SplitBlock, too.\r
+ * @return a new array of all objects excluding selves\r
+ */\r
+ protected Object[] getAllOtherCells(Object[] all, Object... self) {\r
+ List<Object> listNotObs = new ArrayList<Object>(0);\r
+ listNotObs.addAll(Arrays.asList(self));\r
+ for (Object obj : self) {\r
+ // if self contains a link\r
+ if (obj instanceof BasicLink) {\r
+ BasicLink link = (BasicLink) obj;\r
+ // in these selves, if the source/target of this link belongs to a SplitBlock,\r
+ // remove it.\r
+ if (link.getSource() != null && link.getSource().getParent() instanceof SplitBlock) {\r
+ listNotObs.add(link.getSource().getParent());\r
+ }\r
+ if (link.getTarget() != null && link.getTarget().getParent() instanceof SplitBlock) {\r
+ listNotObs.add(link.getTarget().getParent());\r
+ }\r
+ }\r
+ }\r
+ List<Object> listnew = new ArrayList<Object>(0);\r
+ for (Object o : all) {\r
+ // if it does not belongs to self,\r
+ if (!listNotObs.contains(o) && !(o instanceof SplitBlock)) { // && !(o instanceof TextBlock)\r
+ // only add normal Blocks excluding SplitBlock or TextBlock.\r
+ if (!listnew.contains(o)) {\r
+ listnew.add(o);\r
+ }\r
+ // add the ports of the block.\r
+ if (o instanceof BasicBlock) {\r
+ BasicBlock block = (BasicBlock) o;\r
+ for (int i = 0; i < block.getChildCount(); i++) {\r
+ if (!listNotObs.contains(block.getChildAt(i))) {\r
+ listnew.add(block.getChildAt(i));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ Object[] newAll = listnew.toArray();\r
+ return newAll;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab\r
+ * Copyright (C) 2015 - Chenfeng ZHU\r
+ *\r
+ * This file must be used under the terms of the CeCILL.\r
+ * This source file is licensed as described in the file COPYING, which\r
+ * you should have received as part of this distribution. The terms\r
+ * are also available at\r
+ * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt\r
+ *\r
+ */\r
+\r
+package org.scilab.modules.xcos.utils;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.LinkedHashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import org.scilab.modules.xcos.block.BasicBlock;\r
+import org.scilab.modules.xcos.block.SplitBlock;\r
+import org.scilab.modules.xcos.link.BasicLink;\r
+import org.scilab.modules.xcos.port.BasicPort;\r
+import org.scilab.modules.xcos.port.Orientation;\r
+\r
+import com.mxgraph.model.mxCell;\r
+import com.mxgraph.model.mxGeometry;\r
+import com.mxgraph.model.mxICell;\r
+import com.mxgraph.util.mxPoint;\r
+import com.mxgraph.util.mxUtils;\r
+\r
+/**\r
+ * Provide methods to calculate the route.\r
+ *\r
+ */\r
+public abstract class XcosRouteUtils {\r
+\r
+ /**\r
+ * The error which can be accepted as it is aligned strictly.\r
+ */\r
+ public final static double ALIGN_STRICT_ERROR = 2;\r
+\r
+ public final static double ALIGN_SPLITBLOCK_ERROR = 10;\r
+\r
+ /**\r
+ * The distance for a point away to the port.\r
+ */\r
+ public final static double BEAUTY_AWAY_DISTANCE = 40;\r
+ public final static double BEAUTY_AWAY_REVISION = 10;\r
+ public final static double SPLITBLOCK_AWAY_DISTANCE = 15;\r
+\r
+ /**\r
+ * Define a normal half size of block to avoid.\r
+ */\r
+ public final static double NORMAL_BLOCK_SIZE = 40;\r
+\r
+ /**\r
+ * Times trying to find a complex route.\r
+ */\r
+ public final static int TRY_TIMES = 3;\r
+\r
+ /**\r
+ * Check whether the two points are strictly aligned(vertical or horizontal) or not. (The\r
+ * accepted error is <code>XcosRoute.ALIGN_STRICT_ERROR</code>)\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point\r
+ * @param y1\r
+ * the y-coordinate of the first point\r
+ * @param x2\r
+ * the x-coordinate of the second point\r
+ * @param y2\r
+ * the y-coordinate of the second point\r
+ * @return <b>true</b> if two points are aligned.\r
+ */\r
+ protected static boolean isStrictlyAligned(double x1, double y1, double x2, double y2) {\r
+ double error = XcosRouteUtils.ALIGN_STRICT_ERROR;\r
+ if (Math.abs(x2 - x1) < error) {\r
+ return true;\r
+ }\r
+ if (Math.abs(y2 - y1) < error) {\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Get all points of one link including the source port and target port.\r
+ *\r
+ * @param link\r
+ * @return\r
+ */\r
+ protected static List<mxPoint> getLinkPoints(BasicLink link) {\r
+ List<mxPoint> list = new ArrayList<mxPoint>(0);\r
+ mxPoint offset;\r
+ // add the source point.\r
+ mxICell source = link.getSource();\r
+ if (source != null) {\r
+ mxGeometry sourceGeo = source.getGeometry();\r
+ double sourceX = sourceGeo.getCenterX();\r
+ double sourceY = sourceGeo.getCenterY();\r
+ mxICell sourceParent = source.getParent();\r
+ mxGeometry srcParGeo = sourceParent.getGeometry();\r
+ if (srcParGeo == null) {\r
+ srcParGeo = new mxGeometry(0, 0, 0, 0);\r
+ }\r
+ offset = sourceGeo.getOffset();\r
+ if (offset == null) {\r
+ offset = new mxPoint(0, 0);\r
+ }\r
+ if (sourceGeo.isRelative()) {\r
+ sourceX = srcParGeo.getX() + sourceGeo.getX() * srcParGeo.getWidth() + offset.getX();\r
+ sourceY = srcParGeo.getY() + sourceGeo.getY() * srcParGeo.getHeight() + offset.getY();\r
+ } else {\r
+ sourceX = srcParGeo.getX() + sourceGeo.getX() + offset.getX();\r
+ sourceY = srcParGeo.getY() + sourceGeo.getY() + offset.getY();\r
+ }\r
+ list.add(new mxPoint(sourceX, sourceY));\r
+ }\r
+ // add all the turning points.\r
+ if (link.getGeometry().getPoints() != null) {\r
+ list.addAll(link.getGeometry().getPoints());\r
+ }\r
+ // add the target point.\r
+ mxICell target = link.getTarget();\r
+ if (target != null) {\r
+ mxGeometry targetGeo = target.getGeometry();\r
+ double targetX = targetGeo.getCenterX();\r
+ double targetY = targetGeo.getCenterY();\r
+ mxICell targetParent = target.getParent();\r
+ mxGeometry tgtParGeo = targetParent.getGeometry();\r
+ if (tgtParGeo == null) {\r
+ tgtParGeo = new mxGeometry(0, 0, 0, 0);\r
+ }\r
+ offset = targetGeo.getOffset();\r
+ if (offset == null) {\r
+ offset = new mxPoint(0, 0);\r
+ }\r
+ if (targetGeo.isRelative()) {\r
+ targetX = tgtParGeo.getX() + targetGeo.getX() * tgtParGeo.getWidth() + offset.getX();\r
+ targetY = tgtParGeo.getY() + targetGeo.getY() * tgtParGeo.getHeight() + offset.getY();\r
+ } else {\r
+ targetX = tgtParGeo.getX() + targetGeo.getX() + offset.getX();\r
+ targetY = tgtParGeo.getY() + targetGeo.getY() + offset.getY();\r
+ }\r
+ list.add(new mxPoint(targetX, targetY));\r
+ }\r
+ return list;\r
+ }\r
+\r
+ /**\r
+ * Check whether there are blocks between two points.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the line\r
+ * @param allCells\r
+ * @return <b>true</b> if there is at least one blocks in the line.\r
+ */\r
+ protected static boolean checkObstacle(double x1, double y1, double x2, double y2, Object[] allCells) {\r
+ return checkObstacle(x1, y1, x2, y2, allCells, false);\r
+ }\r
+\r
+ /**\r
+ * Check whether there are blocks between two points.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the line\r
+ * @param allCells\r
+ * @param isStrict\r
+ * if it is true, there won't be intersection.\r
+ * @return <b>true</b> if there is at least one obstacle in the line.\r
+ */\r
+ protected static boolean checkObstacle(double x1, double y1, double x2, double y2, Object[] allCells, boolean isStrict) {\r
+ for (Object o : allCells) {\r
+ if (o instanceof mxCell) {\r
+ mxCell c = (mxCell) o;\r
+ if (c instanceof BasicLink) {\r
+ BasicLink l = (BasicLink) c;\r
+ if (checkLinesIntersection(x1, y1, x2, y2, l) && isStrict) {\r
+ // check intersection.\r
+ return true;\r
+ }\r
+ if (checkLinesCoincide(x1, y1, x2, y2, l)) {\r
+ // check superimposition.\r
+ return true;\r
+ }\r
+ if (pointInLink(x1, y1, l) || pointInLink(x2, y2, l)) {\r
+ return true;\r
+ }\r
+ } else {\r
+ mxGeometry geo = c.getGeometry();\r
+ mxGeometry newGeo = new mxGeometry(geo.getX(), geo.getY(), geo.getWidth(), geo.getHeight());\r
+ // if it is a BasicPort, re-calculate its geometry position.\r
+ if (c.getParent().getGeometry() != null && c instanceof BasicPort) {\r
+ double x = c.getParent().getGeometry().getX() + newGeo.getX();\r
+ newGeo.setX(x);\r
+ double y = c.getParent().getGeometry().getY() + newGeo.getY();\r
+ newGeo.setY(y);\r
+ }\r
+ mxPoint interction = newGeo.intersectLine(x1, y1, x2, y2);\r
+ if (interction != null) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check whether a point is in the range of one block (including the ports).\r
+ *\r
+ * @param x\r
+ * the x-coordinate of the point\r
+ * @param y\r
+ * the y-coordinate of the point\r
+ * @param allCells\r
+ * @return <b>true</b> if one point is in one block.\r
+ */\r
+ protected static boolean checkPointInBlocks(double x, double y, Object[] allCells) {\r
+ for (Object o : allCells) {\r
+ if (o instanceof BasicBlock) {\r
+ BasicBlock block = (BasicBlock) o;\r
+ // if it is a BasicBlock, re-calculate its size with its port.\r
+ double ix = 0;\r
+ double iy = 0;\r
+ double iw = 0;\r
+ double ih = 0;\r
+ mxGeometry g = block.getGeometry();\r
+ for (int i = 0; i < block.getChildCount(); i++) {\r
+ mxICell child = block.getChildAt(i);\r
+ if (child.getGeometry() == null) {\r
+ continue;\r
+ }\r
+ mxGeometry childGeo = new mxGeometry(child.getGeometry().getX(), child.getGeometry().getY(),\r
+ child.getGeometry().getWidth(), child.getGeometry().getHeight());\r
+ if (child.getGeometry().isRelative()) {\r
+ childGeo.setX(g.getWidth() * childGeo.getX());\r
+ childGeo.setY(g.getHeight() * childGeo.getY());\r
+ }\r
+ if (childGeo.getX() < 0) {\r
+ ix = childGeo.getX();\r
+ iw = Math.abs(ix);\r
+ }\r
+ if (childGeo.getX() + childGeo.getWidth() > g.getWidth()) {\r
+ iw += childGeo.getX() + childGeo.getWidth() - g.getWidth();\r
+ }\r
+ if (childGeo.getY() < 0) {\r
+ iy = childGeo.getY();\r
+ ih = Math.abs(iy);\r
+ }\r
+ if (childGeo.getY() + childGeo.getHeight() > Math.max(block.getGeometry().getHeight(), ih)) {\r
+ ih += childGeo.getY() + childGeo.getHeight() - g.getHeight();\r
+ }\r
+ }\r
+ double blockx = g.getX() + ix;\r
+ double blocky = g.getY() + iy;\r
+ double width = g.getWidth() + iw;\r
+ double height = g.getHeight() + ih;\r
+ if (x >= blockx && x <= (blockx + width) && y >= blocky && y <= (blocky + height)) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check whether a point is in a geometry.\r
+ *\r
+ * @param x\r
+ * the x-coordinate of the point\r
+ * @param y\r
+ * the y-coordinate of the point\r
+ * @param geometry\r
+ * @return <b>true</b> if one point is in the geometry.\r
+ */\r
+ protected static boolean checkPointInGeometry(double x, double y, mxGeometry geometry) {\r
+ boolean flag = false;\r
+ flag = (x >= geometry.getX())\r
+ && (x <= geometry.getX() + geometry.getWidth())\r
+ && (y >= geometry.getY())\r
+ && (y <= geometry.getY() + geometry.getHeight());\r
+ return flag;\r
+ }\r
+\r
+ /**\r
+ * Check whether two lines have intersection or not.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the first line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the first line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the first line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the first line\r
+ * @param link\r
+ * the second link\r
+ * @return <b>true</b> if two lines have intersection(s).\r
+ */\r
+ private static boolean checkLinesIntersection(double x1, double y1, double x2, double y2, BasicLink link) {\r
+ List<mxPoint> listPoints = getLinkPoints(link);\r
+ if (listPoints == null || listPoints.size() <= 1) {\r
+ } else {\r
+ for (int i = 1; i < listPoints.size(); i++) {\r
+ mxPoint point3 = listPoints.get(i - 1);\r
+ mxPoint point4 = listPoints.get(i);\r
+ double x3 = point3.getX();\r
+ double y3 = point3.getY();\r
+ double x4 = point4.getX();\r
+ double y4 = point4.getY();\r
+ mxPoint point = mxUtils.intersection(x1, y1, x2, y2, x3, y3, x4, y4);\r
+ if (point != null) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check whether two lines coincide or not. The lines are vertical or horizontal. <br/>\r
+ * <b>NOTE:</b> This method is used to check coincidence, NOT intersection!\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the first line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the first line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the first line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the first line\r
+ * @param link\r
+ * the second line\r
+ * @return <b>true</b> if two lines coincide completely or partly.\r
+ */\r
+ private static boolean checkLinesCoincide(double x1, double y1, double x2, double y2, BasicLink link) {\r
+ // mxICell source = line.getSource();\r
+ // mxICell target = line.getTarget();\r
+ List<mxPoint> listPoints = new ArrayList<mxPoint>(0);\r
+ if (link.getGeometry().getPoints() == null || link.getGeometry().getPoints().size() == 0) {\r
+ // if the edge is straight or vertical or horizontal style, there is\r
+ // no way to check.\r
+ } else {\r
+ if (getLinkPortPosition(link, true) != null) {\r
+ listPoints.add(getLinkPortPosition(link, true));\r
+ }\r
+ listPoints.addAll(link.getGeometry().getPoints());\r
+ if (getLinkPortPosition(link, false) != null) {\r
+ listPoints.add(getLinkPortPosition(link, false));\r
+ }\r
+ // in Java 8:\r
+ // listPoints.removeIf(Objects::isNull);\r
+ for (int i = 1; i < listPoints.size(); i++) {\r
+ mxPoint point3 = listPoints.get(i - 1);\r
+ mxPoint point4 = listPoints.get(i);\r
+ double x3 = point3.getX();\r
+ double y3 = point3.getY();\r
+ double x4 = point4.getX();\r
+ double y4 = point4.getY();\r
+ if (linesCoincide(x1, y1, x2, y2, x3, y3, x4, y4)) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check whether two lines coincide or not.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the first line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the first line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the first line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the first line\r
+ * @param x3\r
+ * the x-coordinate of the first point of the second line\r
+ * @param y3\r
+ * the y-coordinate of the first point of the second line\r
+ * @param x4\r
+ * the x-coordinate of the second point of the second line\r
+ * @param y4\r
+ * the y-coordinate of the second point of the second line\r
+ * @return <b>true</b> if two lines coincide.\r
+ */\r
+ private static boolean linesCoincide(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {\r
+ x1 = Math.round(x1);\r
+ y1 = Math.round(y1);\r
+ x2 = Math.round(x2);\r
+ y2 = Math.round(y2);\r
+ x3 = Math.round(x3);\r
+ y3 = Math.round(y3);\r
+ x4 = Math.round(x4);\r
+ y4 = Math.round(y4);\r
+ if (linesStrictlyCoincide(x1, y1, x2, y2, x3, y3, x4, y4)) {\r
+ return true;\r
+ }\r
+ if (x1 == x2) {\r
+ for (double xi = x1 - ALIGN_STRICT_ERROR; xi < x1 + ALIGN_STRICT_ERROR; xi++) {\r
+ xi = Math.round(xi);\r
+ if (linesStrictlyCoincide(xi, y1, xi, y2, x3, y3, x4, y4)) {\r
+ return true;\r
+ }\r
+ }\r
+ } else if (y1 == y2) {\r
+ for (double yi = y1 - ALIGN_STRICT_ERROR; yi < y1 + ALIGN_STRICT_ERROR; yi++) {\r
+ yi = Math.round(yi);\r
+ if (linesStrictlyCoincide(x1, yi, x2, yi, x3, y3, x4, y4)) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check whether two lines strictly coincide or not.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the first line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the first line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the first line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the first line\r
+ * @param x3\r
+ * the x-coordinate of the first point of the second line\r
+ * @param y3\r
+ * the y-coordinate of the first point of the second line\r
+ * @param x4\r
+ * the x-coordinate of the second point of the second line\r
+ * @param y4\r
+ * the y-coordinate of the second point of the second line\r
+ * @return <b>true</b> if two lines coincide.\r
+ */\r
+ private static boolean linesStrictlyCoincide(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {\r
+ // the first line is inside the second line.\r
+ if (pointInLineSegment(x1, y1, x3, y3, x4, y4) && pointInLineSegment(x2, y2, x3, y3, x4, y4)) {\r
+ return true;\r
+ }\r
+ // the second line is inside the first line.\r
+ if (pointInLineSegment(x3, y3, x1, y1, x2, y2) && pointInLineSegment(x4, y4, x1, y1, x2, y2)) {\r
+ return true;\r
+ }\r
+ double i = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);\r
+ // two lines are parallel.\r
+ if (i == 0) {\r
+ if (pointInLineSegment(x1, y1, x3, y3, x4, y4) || pointInLineSegment(x2, y2, x3, y3, x4, y4)\r
+ || pointInLineSegment(x3, y3, x1, y1, x2, y2) || pointInLineSegment(x4, y4, x1, y1, x2, y2)) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Return the intersection of two lines as an mxPoint.\r
+ * If two lines are superposition, return the last point.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the first line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the first line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the first line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the first line\r
+ * @param x3\r
+ * the x-coordinate of the first point of the second line\r
+ * @param y3\r
+ * the y-coordinate of the first point of the second line\r
+ * @param x4\r
+ * the x-coordinate of the second point of the second line\r
+ * @param y4\r
+ * the y-coordinate of the second point of the second line\r
+ * @return the intersection between the two lines.\r
+ */\r
+ public static mxPoint getIntersection(double x1, double y1, double x2, double y2, double x3, double y3,\r
+ double x4, double y4) {\r
+ x1 = Math.round(x1);\r
+ y1 = Math.round(y1);\r
+ x2 = Math.round(x2);\r
+ y2 = Math.round(y2);\r
+ x3 = Math.round(x3);\r
+ y3 = Math.round(y3);\r
+ x4 = Math.round(x4);\r
+ y4 = Math.round(y4);\r
+ if (linesStrictlyCoincide(x1, y1, x2, y2, x3, y3, x4, y4)) {\r
+ if (pointInLineSegment(x2, y2, x3, y3, x4, y4)) {\r
+ return new mxPoint(x2, y2);\r
+ } else if (pointInLineSegment(x4, y4, x1, y1, x2, y2)) {\r
+ return new mxPoint(x4, y4);\r
+ }\r
+ }\r
+ return mxUtils.intersection(x1, y1, x2, y2, x3, y3, x4, y4);\r
+ }\r
+\r
+ /**\r
+ * Check whether a point is in the Link.\r
+ *\r
+ * @param x\r
+ * the x-coordinate of the point\r
+ * @param y\r
+ * the y-coordinate of the point\r
+ * @param link the link\r
+ * @return <b>true</b> if one point is in the Link.\r
+ */\r
+ private static boolean pointInLink(double x, double y, BasicLink link) {\r
+ List<mxPoint> listPoints = link.getPoints(0, false);\r
+ return pointInLink(x, y, listPoints);\r
+ }\r
+\r
+ /**\r
+ * Check whether a point is in the Link (a list of Point).\r
+ *\r
+ * @param x\r
+ * the x-coordinate of the point\r
+ * @param y\r
+ * the y-coordinate of the point\r
+ * @param listPoints\r
+ * the list of points in the link.\r
+ * @return <b>true</b> if one point is in the Link.\r
+ */\r
+ protected static boolean pointInLink(double x, double y, List<mxPoint> listPoints) {\r
+ if (listPoints == null || listPoints.size() <= 1) {\r
+ } else {\r
+ for (int i = 1; i < listPoints.size(); i++) {\r
+ mxPoint point1 = listPoints.get(i - 1);\r
+ mxPoint point2 = listPoints.get(i);\r
+ double x1 = point1.getX();\r
+ double y1 = point1.getY();\r
+ double x2 = point2.getX();\r
+ double y2 = point2.getY();\r
+ if (x1 == x2 && x != x1) {\r
+ continue;\r
+ }\r
+ if (y1 == y2 && y != y1) {\r
+ continue;\r
+ }\r
+ if (pointInLineSegment(x, y, x1, y1, x2, y2)) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check whether the point is in the line segment or not.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the point\r
+ * @param y1\r
+ * the y-coordinate of the point\r
+ * @param x2\r
+ * the x-coordinate of the first point of the line\r
+ * @param y2\r
+ * the y-coordinate of the first point of the line\r
+ * @param x3\r
+ * the x-coordinate of the second point of the line\r
+ * @param y3\r
+ * the y-coordinate of the second point of the line\r
+ * @return <b>true</b> if the point is in the line segment.\r
+ */\r
+ protected static boolean pointInLineSegment(double x1, double y1, double x2, double y2, double x3, double y3) {\r
+ if (((x3 - x2) * (y1 - y2) == (x1 - x2) * (y3 - y2)) && (x1 >= Math.min(x2, x3) && x1 <= Math.max(x2, x3))\r
+ && (y1 >= Math.min(y2, y3) && y1 <= Math.max(y2, y3))) {\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * In the method, only 4 turning points at most are supported.\r
+ *\r
+ * @param p1\r
+ * the point away from the first port\r
+ * @param p2\r
+ * the point away from the second port\r
+ * @param allCells\r
+ * all the possible\r
+ * @return\r
+ */\r
+ public static List<mxPoint> getSimpleRoute(mxPoint p1, mxPoint p2, Object[] allCells) {\r
+ return getSimpleRoute(p1, null, p2, null, allCells);\r
+ }\r
+\r
+ /**\r
+ * In the method, only 4 turning points at most are supported.\r
+ *\r
+ * @param p1\r
+ * the point away from the first port\r
+ * @param o1\r
+ * the orientation of the first port\r
+ * @param p2\r
+ * the point away from the second port\r
+ * @param o2\r
+ * the orientation of the second port\r
+ * @param allCells\r
+ * all the possible\r
+ * @return\r
+ */\r
+ public static List<mxPoint> getSimpleRoute(mxPoint p1, Orientation o1, mxPoint p2, Orientation o2,\r
+ Object[] allCells) {\r
+ // point1 and point2 are not in the vertical or horizontal line.\r
+ List<mxPoint> listSimpleRoute = new ArrayList<mxPoint>(0);\r
+ List<Double> listX = new ArrayList<Double>(0);\r
+ List<Double> listY = new ArrayList<Double>(0);\r
+ double distance = XcosRouteUtils.NORMAL_BLOCK_SIZE;\r
+ double x1 = p1.getX();\r
+ double y1 = p1.getY();\r
+ double x2 = p2.getX();\r
+ double y2 = p2.getY();\r
+ // simplest situation: directly connect.\r
+ if (o1 == Orientation.EAST || o1 == Orientation.WEST) {\r
+ // if the start is horizontal,\r
+ if (!checkObstacle(x1, y1, x2, y1, allCells) && !checkObstacle(x2, y1, x2, y2, allCells)) {\r
+ listSimpleRoute.add(new mxPoint(x2, y1));\r
+ if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ if (!checkObstacle(x1, y1, x1, y2, allCells) && !checkObstacle(x1, y2, x2, y2, allCells)) {\r
+ listSimpleRoute.add(p1);\r
+ listSimpleRoute.add(new mxPoint(x1, y2));\r
+ if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ } else if (o1 == Orientation.NORTH || o1 == Orientation.SOUTH) {\r
+ // if the start is vertical,\r
+ if (!checkObstacle(x1, y1, x1, y2, allCells) && !checkObstacle(x1, y2, x2, y2, allCells)) {\r
+ listSimpleRoute.add(new mxPoint(x1, y2));\r
+ if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ if (!checkObstacle(x1, y1, x2, y1, allCells) && !checkObstacle(x2, y1, x2, y2, allCells)) {\r
+ listSimpleRoute.add(p1);\r
+ listSimpleRoute.add(new mxPoint(x2, y1));\r
+ if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ }\r
+ if (o1 == Orientation.EAST || o1 == Orientation.WEST) {\r
+ // check the nodes in x-coordinate\r
+ double xmax = Math.max(x1 + distance, x2 + distance);\r
+ double xmin = Math.min(x1 - distance, x2 - distance);\r
+ for (double xi = xmin; xi <= xmax; xi++) {\r
+ if (!checkObstacle(x1, y1, xi, y1, allCells) && !checkObstacle(xi, y1, xi, y2, allCells)\r
+ && !checkObstacle(xi, y2, x2, y2, allCells)) {\r
+ listX.add(xi);\r
+ }\r
+ }\r
+ if (listX.size() > 0) {\r
+ double x = choosePoint(listX, x1, x2);\r
+ listSimpleRoute.add(new mxPoint(x, y1));\r
+ listSimpleRoute.add(new mxPoint(x, y2));\r
+ if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ // check the nodes in y-coordinate\r
+ double ymax = Math.max(y1 + distance, y2 + distance);\r
+ double ymin = Math.min(y1 - distance, y2 - distance);\r
+ for (double yi = ymin; yi <= ymax; yi++) {\r
+ if (!checkObstacle(x1, y1, x1, yi, allCells) && !checkObstacle(x1, yi, x2, yi, allCells)\r
+ && !checkObstacle(x2, yi, x2, y2, allCells)) {\r
+ listY.add(yi);\r
+ }\r
+ }\r
+ if (listY.size() > 0) {\r
+ double y = choosePoint(listY, y1, y2);\r
+ listSimpleRoute.add(p1);\r
+ listSimpleRoute.add(new mxPoint(x1, y));\r
+ listSimpleRoute.add(new mxPoint(x2, y));\r
+ if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ } else if (o1 == Orientation.NORTH || o1 == Orientation.SOUTH) {\r
+ // check the nodes in y-coordinate\r
+ double ymax = Math.max(y1 + distance, y2 + distance);\r
+ double ymin = Math.min(y1 - distance, y2 - distance);\r
+ for (double yi = ymin; yi <= ymax; yi++) {\r
+ if (!checkObstacle(x1, y1, x1, yi, allCells) && !checkObstacle(x1, yi, x2, yi, allCells)\r
+ && !checkObstacle(x2, yi, x2, y2, allCells)) {\r
+ listY.add(yi);\r
+ }\r
+ }\r
+ if (listY.size() > 0) {\r
+ double y = choosePoint(listY, y1, y2);\r
+ listSimpleRoute.add(new mxPoint(x1, y));\r
+ listSimpleRoute.add(new mxPoint(x2, y));\r
+ if (o2 != Orientation.NORTH && o2 != Orientation.SOUTH) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ // check the nodes in x-coordinate\r
+ double xmax = Math.max(x1 + distance, x2 + distance);\r
+ double xmin = Math.min(x1 - distance, x2 - distance);\r
+ for (double xi = xmin; xi <= xmax; xi++) {\r
+ if (!checkObstacle(x1, y1, xi, y1, allCells) && !checkObstacle(xi, y1, xi, y2, allCells)\r
+ && !checkObstacle(xi, y2, x2, y2, allCells)) {\r
+ listX.add(xi);\r
+ }\r
+ }\r
+ if (listX.size() > 0) {\r
+ double x = choosePoint(listX, x1, x2);\r
+ listSimpleRoute.add(p1);\r
+ listSimpleRoute.add(new mxPoint(x, y1));\r
+ listSimpleRoute.add(new mxPoint(x, y2));\r
+ if (o2 != Orientation.EAST && o2 != Orientation.WEST) {\r
+ listSimpleRoute.add(p2);\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+ }\r
+ return listSimpleRoute;\r
+ }\r
+\r
+ /**\r
+ * Choose a better point (which is the average number in the widest range in a certain density)\r
+ * from the list which contains discrete numbers.\r
+ *\r
+ * @param list\r
+ * @return\r
+ */\r
+ private static double choosePoint(List<Double> list, double p1, double p2) {\r
+ if (list == null || list.size() == 0) {\r
+ return 0;\r
+ }\r
+ Collections.sort(list);\r
+ double nMax = Math.max(p1, p2);\r
+ double nMin = Math.min(p1, p2);\r
+ double start = list.get(0);\r
+ double start_temp = list.get(0);\r
+ double end = list.get(0);\r
+ double end_temp = list.get(0);\r
+ double mid_temp = (end_temp + start_temp) / 2;\r
+ double mid = (end + start) / 2;\r
+ boolean restart = true;\r
+ for (int i = 1; i < list.size(); i++) {\r
+ if (Math.abs(list.get(i) - list.get(i - 1)) <= 1.1) {\r
+ end_temp = list.get(i);\r
+ restart = false;\r
+ } else {\r
+ if (restart) {\r
+ start_temp = list.get(i);\r
+ continue;\r
+ }\r
+ mid_temp = (end_temp + start_temp) / 2;\r
+ mid = (end + start) / 2;\r
+ if ((mid_temp < nMin || mid_temp > nMax) && (mid < nMax && mid > nMin)) {\r
+ // if the new one is out of two points and the previous one\r
+ // is inside,\r
+ start_temp = list.get(i);\r
+ end_temp = list.get(i);\r
+ restart = true;\r
+ } else if ((Math.abs(end_temp - start_temp) > Math.abs(end - start))\r
+ || (mid_temp < nMax && mid_temp > nMin) && (mid < nMin || mid > nMax)) {\r
+ // if the new one in between two points and the previous one\r
+ // is out of them, or if the new one is longer than the\r
+ // previous one,\r
+ start = start_temp;\r
+ end = end_temp;\r
+ start_temp = list.get(i);\r
+ end_temp = list.get(i);\r
+ restart = true;\r
+ }\r
+ }\r
+ }\r
+ mid_temp = (end_temp + start_temp) / 2;\r
+ mid = (end + start) / 2;\r
+ if ((mid_temp < nMin || mid_temp > nMax) && (mid < nMax && mid > nMin)) {\r
+ } else if ((Math.abs(end_temp - start_temp) > Math.abs(end - start))\r
+ || ((mid_temp < nMax && mid_temp > nMin) && (mid < nMin || mid > nMax))) {\r
+ start = start_temp;\r
+ end = end_temp;\r
+ }\r
+ return (start + end) / 2;\r
+ }\r
+\r
+ /**\r
+ * Based on getSimpleRoute().\r
+ *\r
+ * @param p1\r
+ * the point away from the first port\r
+ * @param o1\r
+ * the orientation of the first port\r
+ * @param p2\r
+ * the point away from the second port\r
+ * @param o2\r
+ * the orientation of the second port\r
+ * @param allCells\r
+ * all the possible obstacles\r
+ * @param times\r
+ * @return\r
+ */\r
+ public static List<mxPoint> getComplexRoute(mxPoint p1, Orientation o1, mxPoint p2, Orientation o2,\r
+ Object[] allCells, int times) {\r
+ if (times <= 0) {\r
+ return null;\r
+ }\r
+ double newPosition = NORMAL_BLOCK_SIZE;\r
+ List<mxPoint> listComplexRoute = new ArrayList<mxPoint>(0);\r
+ List<mxPoint> listTmp = new ArrayList<mxPoint>(0);\r
+ listComplexRoute.add(p1);\r
+ Map<Orientation, mxPoint> mapNewP1 = new LinkedHashMap<Orientation, mxPoint>(0);\r
+ if (o1 == Orientation.EAST || o1 == null) {\r
+ // to EAST\r
+ mxPoint npE = new mxPoint(p1.getX() + newPosition, p1.getY());\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npE.getX(), npE.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npE, Orientation.EAST, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.EAST, npE);\r
+ }\r
+ // to NORTH\r
+ mxPoint npN = new mxPoint(p1.getX(), p1.getY() - newPosition);\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npN.getX(), npN.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npN, Orientation.NORTH, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.NORTH, npN);\r
+ }\r
+ // to SOUTH\r
+ mxPoint npS = new mxPoint(p1.getX(), p1.getY() + newPosition);\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npS.getX(), npS.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npS, Orientation.SOUTH, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.SOUTH, npS);\r
+ }\r
+ if (o1 == null) {\r
+ // to WEST\r
+ mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.WEST, npW);\r
+ }\r
+ }\r
+ } else if (o1 == Orientation.SOUTH) {\r
+ mxPoint npS = new mxPoint(p1.getX(), p1.getY() + newPosition);\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npS.getX(), npS.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npS, Orientation.SOUTH, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.SOUTH, npS);\r
+ }\r
+ mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.WEST, npW);\r
+ }\r
+ mxPoint npE = new mxPoint(p1.getX() + newPosition, p1.getY());\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npE.getX(), npE.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npE, Orientation.EAST, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.EAST, npE);\r
+ }\r
+ } else if (o1 == Orientation.WEST) {\r
+ mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.WEST, npW);\r
+ }\r
+ mxPoint npN = new mxPoint(p1.getX(), p1.getY() - newPosition);\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npN.getX(), npN.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npN, Orientation.NORTH, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.NORTH, npN);\r
+ }\r
+ mxPoint npS = new mxPoint(p1.getX(), p1.getY() + newPosition);\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npS.getX(), npS.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npS, Orientation.SOUTH, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.SOUTH, npS);\r
+ }\r
+ } else if (o1 == Orientation.NORTH) {\r
+ mxPoint npN = new mxPoint(p1.getX(), p1.getY() - newPosition);\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npN.getX(), npN.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npN, Orientation.NORTH, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.NORTH, npN);\r
+ }\r
+ mxPoint npW = new mxPoint(p1.getX() - newPosition, p1.getY());\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npW.getX(), npW.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npW, Orientation.WEST, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.WEST, npW);\r
+ }\r
+ mxPoint npE = new mxPoint(p1.getX() + newPosition, p1.getY());\r
+ if (!checkObstacle(p1.getX(), p1.getY(), npE.getX(), npE.getY(), allCells)) {\r
+ listTmp = getSimpleRoute(npE, Orientation.EAST, p2, o2, allCells);\r
+ if (listTmp != null && listTmp.size() > 0) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ mapNewP1.put(Orientation.EAST, npE);\r
+ }\r
+ }\r
+ for (Map.Entry<Orientation, mxPoint> np1 : mapNewP1.entrySet()) {\r
+ listTmp = getComplexRoute(np1.getValue(), np1.getKey(), p2, o2, allCells, times - 1);\r
+ if (listTmp != null && listTmp.size() > 1) {\r
+ listComplexRoute.addAll(listTmp);\r
+ return listComplexRoute;\r
+ }\r
+ }\r
+ listComplexRoute.clear();\r
+ return listComplexRoute;\r
+ }\r
+\r
+ /**\r
+ * Get the Orientation of a port to its parent.\r
+ *\r
+ * @param port\r
+ * @return\r
+ */\r
+ protected Orientation getPortOrientation(BasicPort port) {\r
+ if (port.getParent() == null) {\r
+ return Orientation.EAST;\r
+ }\r
+ // the coordinate (x,y) for the port.\r
+ mxGeometry portGeo = port.getGeometry();\r
+ double portX = portGeo.getCenterX();\r
+ double portY = portGeo.getCenterY();\r
+ // the coordinate (x,y) and the width-height for the parent block\r
+ mxICell parent = port.getParent();\r
+ mxGeometry parentGeo = parent.getGeometry();\r
+ double blockW = parentGeo.getWidth();\r
+ double blockH = parentGeo.getHeight();\r
+ if (portGeo.isRelative()) {\r
+ portX *= blockW;\r
+ portY *= blockH;\r
+ }\r
+ // calculate relative coordinate based on the center of parent block.\r
+ portX -= blockW / 2;\r
+ portY -= blockH / 2;\r
+ Orientation orientation = Orientation.EAST;\r
+ if ((portX) >= blockW * Math.abs(portY) / blockH) { // x>=w*|y|/h\r
+ orientation = Orientation.EAST;\r
+ } else if (portY >= blockH * Math.abs(portX) / blockW) { // y>=h*|x|/w\r
+ orientation = Orientation.SOUTH;\r
+ } else if (portX <= -blockW * Math.abs(portY) / blockH) { // x<=-w*|y|/h\r
+ orientation = Orientation.WEST;\r
+ } else if (portY <= -blockH * Math.abs(portX) / blockW) { // y<=-h*|x|/w\r
+ orientation = Orientation.NORTH;\r
+ }\r
+ return orientation;\r
+ }\r
+\r
+ /**\r
+ * Get the position of the source/target of a link.If dest is true, return source's position.\r
+ * If dest is false, return target's position.\r
+ *\r
+ * @param link\r
+ * @param dest\r
+ * @return the point of the position\r
+ */\r
+ private static mxPoint getLinkPortPosition(BasicLink link, boolean dest) {\r
+ mxPoint point = new mxPoint();\r
+ mxICell port = null;\r
+ if (dest) {\r
+ port = link.getSource();\r
+ } else {\r
+ port = link.getTarget();\r
+ }\r
+ if (port == null) {\r
+ return null;\r
+ }\r
+ if (port.getParent() instanceof SplitBlock) {\r
+ SplitBlock block = (SplitBlock) port.getParent();\r
+ point.setX(block.getGeometry().getCenterX());\r
+ point.setY(block.getGeometry().getCenterY());\r
+ } else if (port.getParent() instanceof BasicBlock) {\r
+ mxGeometry portGeo = port.getGeometry();\r
+ double portX = portGeo.getCenterX();\r
+ double portY = portGeo.getCenterY();\r
+ double portW = portGeo.getWidth();\r
+ double portH = portGeo.getHeight();\r
+ BasicBlock parent = (BasicBlock) port.getParent();\r
+ mxGeometry parentGeo = parent.getGeometry();\r
+ double blockX = parentGeo.getX();\r
+ double blockY = parentGeo.getY();\r
+ double blockW = parentGeo.getWidth();\r
+ double blockH = parentGeo.getHeight();\r
+ if (portGeo.isRelative()) {\r
+ portX *= blockW;\r
+ portY *= blockH;\r
+ }\r
+ point.setX(blockX + portX + portW / 2);\r
+ point.setY(blockY + portY + portH / 2);\r
+ }\r
+ return point;\r
+ }\r
+\r
+ /**\r
+ * Check if two points are coincident.\r
+ *\r
+ * @param x1\r
+ * @param y1\r
+ * @param x2\r
+ * @param y2\r
+ * @param isSplitBlock\r
+ * @return\r
+ */\r
+ protected static boolean isPointCoincident(double x1, double y1, double x2, double y2, boolean isSplitBlock) {\r
+ double error = XcosRouteUtils.ALIGN_STRICT_ERROR;\r
+ if (isSplitBlock) {\r
+ error = XcosRouteUtils.ALIGN_SPLITBLOCK_ERROR;\r
+ }\r
+ if (Math.abs(y1 - y2) <= error && Math.abs(x1 - x2) <= error\r
+ && (Math.abs(y1 - y2) + Math.abs(x1 - x2)) < error * 2) {\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check if two port are parallel.\r
+ *\r
+ * @param orientation1\r
+ * @param orientation2\r
+ * @return\r
+ */\r
+ protected static boolean isOrientationParallel(double x1, double y1, double x2, double y2, Orientation orientation1, Orientation orientation2) {\r
+ if (orientation1 == orientation2) {\r
+ return true;\r
+ }\r
+ double error = XcosRouteUtils.ALIGN_STRICT_ERROR;\r
+ if (Math.abs(y1 - y2) > error) {\r
+ // if they are not aligned horizontally,\r
+ if (orientation1 == Orientation.EAST && orientation2 == Orientation.WEST) {\r
+ return true;\r
+ }\r
+ if (orientation1 == Orientation.WEST && orientation2 == Orientation.EAST) {\r
+ return true;\r
+ }\r
+ }\r
+ if (Math.abs(x1 - x2) > error) {\r
+ // if they are not aligned vertically,\r
+ if (orientation1 == Orientation.SOUTH && orientation2 == Orientation.NORTH) {\r
+ return true;\r
+ }\r
+ if (orientation1 == Orientation.NORTH && orientation2 == Orientation.SOUTH) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Check if two lines are parallel.\r
+ *\r
+ * @param x1\r
+ * the x-coordinate of the first point of the first line\r
+ * @param y1\r
+ * the y-coordinate of the first point of the first line\r
+ * @param x2\r
+ * the x-coordinate of the second point of the first line\r
+ * @param y2\r
+ * the y-coordinate of the second point of the first line\r
+ * @param x3\r
+ * the x-coordinate of the first point of the second line\r
+ * @param y3\r
+ * the y-coordinate of the first point of the second line\r
+ * @param x4\r
+ * the x-coordinate of the second point of the second line\r
+ * @param y4\r
+ * the y-coordinate of the second point of the second line\r
+ * @param includeCoincide\r
+ * true if including coincide situation\r
+ * @return <b>true</b> if two lines are parallel.\r
+ */\r
+ protected static boolean isLineParallel(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, boolean includeCoincide) {\r
+ double i = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);\r
+ // two lines are parallel and not coincide.\r
+ if (i == 0) {\r
+ if (includeCoincide || (!pointInLineSegment(x1, y1, x3, y3, x4, y4)\r
+ && !pointInLineSegment(x2, y2, x3, y3, x4, y4) && !pointInLineSegment(x3, y3, x1, y1, x2, y2)\r
+ && !pointInLineSegment(x4, y4, x1, y1, x2, y2))) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+}\r