view dwtx/draw2d/graph/Node.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) 2003, 2008 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.graph.Node;

import dwt.dwthelper.utils;
import dwtx.dwtxhelper.Collection;
import tango.text.convert.Format;

import dwtx.draw2d.geometry.Dimension;
import dwtx.draw2d.geometry.Insets;
import dwtx.draw2d.graph.EdgeList;
import dwtx.draw2d.graph.Subgraph;
import dwtx.draw2d.graph.Edge;

/**
 * A node in a DirectedGraph. A node has 0 or more incoming and outgoing {@link Edge}s. A
 * node is given a width and height by the client. When a layout places the node in the
 * graph, it will determine the node's x and y location.  It may also modify the node's
 * height.
 *
 * A node represents both the <EM>input</EM> and the <EM>output</EM> for a layout
 * algorithm. The following fields are used as input to a graph layout:
 * <UL>
 *   <LI>{@link #width} - the node's width.
 *   <LI>{@link #height} - the node's height.
 *   <LI>{@link #outgoing} - the node's outgoing edges.
 *   <LI>{@link #incoming} - the node's incoming edges.
 *   <LI>padding - the amount of space to be left around the outside of the node.
 *   <LI>{@link #incomingOffset} - the default attachment point for incoming edges.
 *   <LI>{@link #outgoingOffset} - the default attachment point for outgoing edges.
 *   <LI>parent - the parent subgraph containing this node.
 * </UL>
 * <P>
 * The following fields are calculated by a graph layout and comprise the <EM>output</EM>:
 * <UL>
 *   <LI>{@link #x} - the node's x location
 *   <LI>{@link #y} - the node's y location
 *   <LI>{@link #height} - the node's height may be stretched to match the height of other
 *   nodes
 * </UL>
 *
 * @author Randy Hudson
 * @since 2.1.2
 */
public class Node {

Node left, right;

Object[] workingData;
int[] workingInts;

/**
 * Clients may use this field to mark the Node with an arbitrary data object.
 */
public Object data;

//used by various graph visitors
bool flag;

/**
 * The height of this node. This value should be set prior to laying out the directed
 * graph.  Depending on the layout rules, a node's height may be expanded to match the
 * height of other nodes around it.
 */
public int height = 40;

/**
 * @deprecated use {@link #setRowConstraint(int)} and {@link #getRowConstraint()}
 */
public int rowOrder = -1;

/**
 * The edges for which this node is the target.
 */
public EdgeList incoming;

/**
 * The default attachment point for incoming edges.  <code>-1</code> indicates that the
 * node's horizontal center should be used.
 */
public int incomingOffset = -1;

// A non-decreasing number given to consecutive nodes in a Rank.
int index;

//Used in Compound graphs to quickly determine whether a node is inside a subgraph.
int nestingIndex = -1;

/**
 * The edges for which this node is the source.
 */
public EdgeList outgoing;

Insets padding;
private Subgraph parent;
int rank;

/**
 * @deprecated for internal use only
 */
public double sortValue;

/**
 * The node's outgoing offset attachment point.
 */
public int outgoingOffset = -1;

/**
 * The node's width. The default value is 50.
 */
public int width = 50;

/**
 * The node's x coordinate.
 */
public int x;
/**
 * The node's y coordinate.
 */
public int y;

/**
 * Constructs a new node.
 */
public this() { }

/**
 * Constructs a node with the given data object
 * @param data an arbitrary data object
 */
public this(Object data) {
    this(data, null);
}

/**
 * Constructs a node inside the given subgraph.
 * @param parent the parent subgraph
 */
public this(Subgraph parent) {
    this(null, parent);
}

/**
 * Constructs a node with the given data object and parent subgraph. This node is added to
 * the set of members for the parent subgraph
 * @param data an arbitrary data object
 * @param parent the parent subgraph or <code>null</code>
 */
public this(Object data, Subgraph parent) {
    this.data = data;
    this.parent = parent;
    incoming = new EdgeList();
    outgoing = new EdgeList();
    workingData = new Object[3];
    workingInts = new int[4];
    if (parent !is null)
        parent.addMember(this);
}

/**
 * Returns the incoming attachment point.  This is the distance from the left edge to the
 * default incoming attachment point for edges.  Each incoming edge may have it's own
 * attachment setting which takes priority over this default one.
 * @return the incoming offset
 */
public int getOffsetIncoming() {
    if (incomingOffset is -1)
        return width / 2;
    return incomingOffset;
}

/**
 * Returns the outgoing attachment point.  This is the distance from the left edge to the
 * default outgoing attachment point for edges.  Each outgoing edge may have it's own
 * attachment setting which takes priority over this default one.
 * @return the outgoing offset
 */
public int getOffsetOutgoing() {
    if (outgoingOffset is -1)
        return width / 2;
    return outgoingOffset;
}

/**
 * Returns the padding for this node or <code>null</code> if the default padding for the
 * graph should be used.
 * @return the padding or <code>null</code>
 */
public Insets getPadding() {
    return padding;
}

/**
 * Returns the parent Subgraph or <code>null</code> if there is no parent. Subgraphs are
 * only for use in {@link CompoundDirectedGraphLayout}.
 * @return the parent or <code>null</code>
 */
public Subgraph getParent() {
    return parent;
}

/**
 * For internal use only. Returns <code>true</code> if the given node is equal to this
 * node.  This method is implemented for consitency with Subgraph.
 * @param node the node in question
 * @return <code>true</code> if nested
 */
bool isNested(Node node) {
    return node is this;
}

/**
 * Sets the padding. <code>null</code> indicates that the default padding should be used.
 * @param padding an insets or <code>null</code>
 */
public void setPadding(Insets padding) {
    this.padding = padding;
}

/**
 * Sets the parent subgraph.  This method should not be called directly.  The constructor
 * will set the parent accordingly.
 * @param parent the parent
 */
public void setParent(Subgraph parent) {
    this.parent = parent;
}

/**
 * Sets the row sorting constraint for this node. By default, a node's constraint is
 * <code>-1</code>. If two nodes have different values both >= 0, the node with the
 * smaller constraint will be placed to the left of the other node. In all other cases no
 * relative placement is guaranteed.
 * @param value the row constraint
 * @since 3.2
 */
public void setRowConstraint(int value) {
    this.rowOrder = value;
}

/**
 * Returns the row constraint for this node.
 * @return the row constraint
 * @since 3.2
 */
public int getRowConstraint() {
    return rowOrder;
}

/**
 * Sets the size of this node to the given dimension.
 * @param size the new size
 * @since 3.2
 */
public void setSize(Dimension size) {
    width = size.width;
    height = size.height;
}

/**
 * @see Object#toString()
 */
public String toString() {
    return Format("N({})", data ); //$NON-NLS-1$ //$NON-NLS-2$
}

Iterator iteratorNeighbors() {
    return new class(outgoing) Iterator {
        int offset;
        EdgeList list;
        this(EdgeList a){
            list = a;
        }
        public Object next() {
            Edge edge = list.getEdge(offset++);
            if (offset < list.size())
                return edge.opposite(this.outer);
            if (list is outgoing) {
                list = incoming;
                offset = 0;
            } else
                list = null;
            return edge.opposite(this.outer);
        }

        public bool hasNext() {
            if (list is null)
                return false;
            if (offset < list.size())
                return true;
            if (list is outgoing) {
                list = incoming;
                offset = 0;
            }
            return offset < list.size();
        }

        public void remove() {
            throw new RuntimeException("Remove not supported"); //$NON-NLS-1$
        }
    };
}

/**
 * Returns a reference to a node located left from this one
 * @return <code>Node</code> on the left from this one
 * @since 3.4
 */
public Node getLeft() {
    return left;
}

/**
 * Returns a reference to a node located right from this one
 * @return <code>Node</code> on the right from this one
 * @since 3.4
 */
public Node getRight() {
    return right;
}

}