diff dwtx/draw2d/AutomaticRouter.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/AutomaticRouter.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.AutomaticRouter;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.internal.MultiValueMap;
+import dwtx.draw2d.AbstractRouter;
+import dwtx.draw2d.ConnectionRouter;
+import dwtx.draw2d.ConnectionAnchor;
+import dwtx.draw2d.Connection;
+
+/**
+ * An abstract router implementation which detects when multiple connections are
+ * overlapping. Two connections overlap if the combination of source and target
+ * anchors are equal. Subclasses must implement {@link #handleCollision(PointList, int)}
+ * to determine how to avoid the overlap.
+ * <p>
+ * This router can delegate to another connection router. The wrappered router will route
+ * the connections first, after which overlapping will be determined.
+ */
+public abstract class AutomaticRouter
+    : AbstractRouter
+{
+
+private ConnectionRouter nextRouter;
+private MultiValueMap connections;
+
+public this(){
+    connections = new MultiValueMap();
+}
+
+private class HashKey {
+
+    private ConnectionAnchor anchor1, anchor2;
+
+    this(Connection conn) {
+        anchor1 = conn.getSourceAnchor();
+        anchor2 = conn.getTargetAnchor();
+    }
+
+    public override int opEquals(Object object) {
+        bool isEqual = false;
+        HashKey hashKey;
+
+        if (auto hashKey = cast(HashKey)object ) {
+            ConnectionAnchor hkA1 = hashKey.getFirstAnchor();
+            ConnectionAnchor hkA2 = hashKey.getSecondAnchor();
+
+            isEqual = ((cast(Object)hkA1).opEquals(cast(Object)anchor1) && (cast(Object)hkA2).opEquals(cast(Object)anchor2))
+                || ((cast(Object)hkA1).opEquals(cast(Object)anchor2) && (cast(Object)hkA2).opEquals(cast(Object)anchor1));
+        }
+        return isEqual;
+    }
+
+    public ConnectionAnchor getFirstAnchor() {
+        return anchor1;
+    }
+
+    public ConnectionAnchor getSecondAnchor() {
+        return anchor2;
+    }
+
+    public override hash_t toHash() {
+        return (cast(Object)anchor1).toHash() ^ (cast(Object)anchor2).toHash();
+    }
+}
+
+/**
+ * @see dwtx.draw2d.ConnectionRouter#getConstraint(Connection)
+ */
+public Object getConstraint(Connection connection) {
+    if (next() !is null)
+        return next().getConstraint(connection);
+    return null;
+}
+
+/**
+ * Handles collisions between 2 or more Connections. Collisions are currently defined as 2
+ * connections with no bendpoints and whose start and end points coincide.  In other
+ * words, the 2 connections are the exact same line.
+ *
+ * @param list The PointList of a connection that collides with another connection
+ * @param index The index of the current connection in the list of colliding connections
+ */
+protected abstract void handleCollision(PointList list, int index);
+
+/**
+ * @see dwtx.draw2d.ConnectionRouter#invalidate(Connection)
+ */
+public void invalidate(Connection conn) {
+    if (next() !is null)
+        next().invalidate(conn);
+    if (conn.getSourceAnchor() is null || conn.getTargetAnchor() is null)
+        return;
+    HashKey connectionKey = new HashKey(conn);
+    ArrayList connectionList = connections.get(connectionKey);
+    int affected = connections.remove(connectionKey, cast(Object)conn);
+    if (affected !is -1) {
+        for (int i = affected; i < connectionList.size(); i++)
+            (cast(Connection)connectionList.get(i)).revalidate();
+    } else
+        connections.removeValue(cast(Object)conn);
+
+}
+
+/**
+ * Returns the next router in the chain.
+ * @return The next router
+ * @since 2.0
+ */
+protected ConnectionRouter next() {
+    return nextRouter;
+}
+
+
+
+/**
+ * @see dwtx.draw2d.ConnectionRouter#remove(Connection)
+ */
+public void remove(Connection conn) {
+    if (conn.getSourceAnchor() is null || conn.getTargetAnchor() is null)
+        return;
+    HashKey connectionKey = new HashKey(conn);
+    ArrayList connectionList = connections.get(connectionKey);
+    if (connectionList !is null) {
+        int index = connections.remove(connectionKey,cast(Object) conn);
+        for (int i = index + 1; i < connectionList.size(); i++)
+            (cast(Connection)connectionList.get(i)).revalidate();
+    }
+    if (next() !is null)
+        next().remove(conn);
+}
+
+/**
+ * Routes the given connection.  Calls the 'next' router first (if one exists) and if no
+ * bendpoints were added by the next router, collisions are dealt with by calling
+ * {@link #handleCollision(PointList, int)}.
+ * @param conn The connection to route
+ */
+public void route(Connection conn) {
+    if (next() !is null)
+        next().route(conn);
+    else {
+        conn.getPoints().removeAllPoints();
+        setEndPoints(conn);
+    }
+
+    if (conn.getPoints().size() is 2) {
+        PointList points = conn.getPoints();
+        HashKey connectionKey = new HashKey(conn);
+        ArrayList connectionList = connections.get(connectionKey);
+
+        if (connectionList !is null) {
+
+            int index;
+
+            if (connectionList.contains(cast(Object)conn)) {
+                index = connectionList.indexOf(cast(Object)conn) + 1;
+            } else {
+                index = connectionList.size() + 1;
+                connections.put(connectionKey, cast(Object)conn);
+            }
+
+            handleCollision(points, index);
+            conn.setPoints(points);
+        } else {
+            connections.put(connectionKey, cast(Object)conn);
+        }
+    }
+}
+
+/**
+ * An AutomaticRouter needs no constraints for the connections it routes.  This method
+ * invalidates the connections and calls {@link #setConstraint(Connection, Object)} on the
+ * {@link #next()} router.
+ * @see dwtx.draw2d.ConnectionRouter#setConstraint(Connection, Object)
+ */
+public void setConstraint(Connection connection, Object constraint) {
+    invalidate(connection);
+    if (next() !is null)
+        next().setConstraint(connection, constraint);
+}
+
+/**
+ * Sets the start and end points for the given connection.
+ * @param conn The connection
+ */
+protected void setEndPoints(Connection conn) {
+    PointList points = conn.getPoints();
+    points.removeAllPoints();
+    Point start = getStartPoint(conn);
+    Point end = getEndPoint(conn);
+    conn.translateToRelative(start);
+    conn.translateToRelative(end);
+    points.addPoint(start);
+    points.addPoint(end);
+    conn.setPoints(points);
+}
+
+/**
+ * Sets the next router.
+ * @param router The ConnectionRouter
+ * @since 2.0
+ */
+public void setNextRouter(ConnectionRouter router) {
+    nextRouter = router;
+}
+
+}