view 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 source

/*******************************************************************************
 * 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;
}

}