001: /*
002: * Copyright (c) 2004-2005 France Telecom
003: * Copyright (c) 2004 Gaudenz Alder
004: * Copyright (c) 2005 David Benson
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions are met:
009: *
010: * - Redistributions of source code must retain the above copyright notice,
011: * this list of conditions and the following disclaimer.
012: * - Redistributions in binary form must reproduce the above copyright notice,
013: * this list of conditions and the following disclaimer in the documentation
014: * and/or other materials provided with the distribution.
015: * - Neither the name of JGraph nor the names of its contributors may be used
016: * to endorse or promote products derived from this software without specific
017: * prior written permission.
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package com.jgraph.pad.util;
031:
032: import java.awt.geom.Point2D;
033: import java.util.ArrayList;
034: import java.util.List;
035:
036: import org.jgraph.graph.AbstractCellView;
037: import org.jgraph.graph.DefaultEdge;
038: import org.jgraph.graph.DefaultGraphModel;
039: import org.jgraph.graph.Edge;
040: import org.jgraph.graph.EdgeView;
041: import org.jgraph.graph.GraphModel;
042: import org.jgraph.graph.VertexView;
043:
044: /**
045: * Algorithm which create intermediates points for parallel edges
046: *
047: * @author fgendre
048: */
049: public class JGraphpadParallelEdgeRouter extends
050: DefaultEdge.LoopRouting {
051:
052: /**
053: * Singleton to reach parallel edge router
054: */
055: public static final JGraphpadParallelEdgeRouter sharedInstance = new JGraphpadParallelEdgeRouter();
056:
057: /**
058: * Default model
059: */
060: private static final GraphModel emptyModel = new DefaultGraphModel();
061:
062: /**
063: * Distance between each parallel edge
064: */
065: private static double edgeSeparation = 10.;
066:
067: /**
068: * Distance between intermediate and source/target points
069: */
070: private static double edgeDeparture = 10.;
071:
072: /**
073: * Getter for singleton managing parallel edges
074: *
075: * @return JGraphParallelEdgeRouter Routeur for parallel edges
076: */
077: public static JGraphpadParallelEdgeRouter getSharedInstance() {
078: return JGraphpadParallelEdgeRouter.sharedInstance;
079: }
080:
081: /**
082: * Calc of intermediates points
083: *
084: * @param edge
085: * Edge for which routing is demanding
086: * @param points
087: * List of points for this edge
088: */
089: public List routeEdge(EdgeView edge) {
090: List newPoints = new ArrayList();
091:
092: // Check presence of source/target nodes
093: if ((null == edge.getSource()) || (null == edge.getTarget())
094: || (null == edge.getSource().getParentView())
095: || (null == edge.getTarget().getParentView())) {
096: return null;
097: }
098: newPoints.add(edge.getSource());
099:
100: // Check presence of parallel edges
101: Object[] edges = getParallelEdges(edge);
102: if (edges == null) {
103: return null;
104: }
105:
106: // For one edge, no intermediate point
107: if (edges.length >= 2) {
108:
109: // Looking for position of edge
110: int position = 0;
111: for (int i = 0; i < edges.length; i++) {
112: Object e = edges[i];
113: if (e == edge.getCell()) {
114: position = i + 1;
115: }
116: }
117:
118: // Looking for position of source/target nodes (edge=>port=>vertex)
119: VertexView nodeFrom = (VertexView) edge.getSource()
120: .getParentView();
121: VertexView nodeTo = (VertexView) edge.getTarget()
122: .getParentView();
123: Point2D from = AbstractCellView.getCenterPoint(nodeFrom);
124: Point2D to = AbstractCellView.getCenterPoint(nodeTo);
125:
126: if (from != null && to != null) {
127: double dy = from.getY() - to.getY();
128: double dx = from.getX() - to.getX();
129:
130: // Calc of radius
131: double m = dy / dx;
132: double theta = Math.atan(-1 / m);
133: double rx = dx / Math.sqrt(dx * dx + dy * dy);
134: double ry = dy / Math.sqrt(dx * dx + dy * dy);
135:
136: // Memorize size of source/target nodes
137: double sizeFrom = Math.max(nodeFrom.getBounds()
138: .getWidth(), nodeFrom.getBounds().getHeight()) / 2.;
139: double sizeTo = Math.max(nodeTo.getBounds().getWidth(),
140: nodeTo.getBounds().getHeight()) / 2.;
141:
142: // Calc position of central point
143: double edgeMiddleDeparture = (Math.sqrt(dx * dx + dy
144: * dy)
145: - sizeFrom - sizeTo)
146: / 2 + sizeFrom;
147:
148: // Calc position of intermediates points
149: double edgeFromDeparture = edgeDeparture + sizeFrom;
150: double edgeToDeparture = edgeDeparture + sizeTo;
151:
152: // Calc distance between edge and mediane source/target
153: double r = edgeSeparation * Math.floor(position / 2);
154: if (0 == (position % 2)) {
155: r = -r;
156: }
157:
158: // Convert coordinate
159: double ex = r * Math.cos(theta);
160: double ey = r * Math.sin(theta);
161:
162: // Check if is not better to have only one intermediate point
163: if (edgeMiddleDeparture <= edgeFromDeparture) {
164: double midX = from.getX() - rx
165: * edgeMiddleDeparture;
166: double midY = from.getY() - ry
167: * edgeMiddleDeparture;
168:
169: Point2D controlPoint = new Point2D.Double(
170: ex + midX, ey + midY);
171:
172: // Add intermediate point
173: newPoints.add(controlPoint);
174: } else {
175: double midXFrom = from.getX() - rx
176: * edgeFromDeparture;
177: double midYFrom = from.getY() - ry
178: * edgeFromDeparture;
179: double midXTo = to.getX() + rx * edgeToDeparture;
180: double midYTo = to.getY() + ry * edgeToDeparture;
181:
182: Point2D controlPointFrom = new Point2D.Double(ex
183: + midXFrom, ey + midYFrom);
184: Point2D controlPointTo = new Point2D.Double(ex
185: + midXTo, ey + midYTo);
186:
187: // Add intermediates points
188: newPoints.add(controlPointFrom);
189: newPoints.add(controlPointTo);
190: }
191: }
192: }
193: newPoints.add(edge.getTarget());
194: return newPoints;
195: }
196:
197: /**
198: * Getter to obtain the distance between each parallel edge
199: *
200: * @return Distance
201: */
202: public static double getEdgeSeparation() {
203: return JGraphpadParallelEdgeRouter.edgeSeparation;
204: }
205:
206: /**
207: * Setter to define distance between each parallel edge
208: *
209: * @param edgeSeparation
210: * New distance
211: */
212: public static void setEdgeSeparation(double edgeSeparation) {
213: JGraphpadParallelEdgeRouter.edgeSeparation = edgeSeparation;
214: }
215:
216: /**
217: * Getter to obtain the distance between intermediate and source/target
218: * points
219: *
220: * @return Distance
221: */
222: public static double getEdgeDeparture() {
223: return JGraphpadParallelEdgeRouter.edgeDeparture;
224: }
225:
226: /**
227: * Setter to define distance between intermediate and source/target points
228: *
229: * @param edgeDeparture
230: * New distance
231: */
232: public static void setEdgeDeparture(double edgeDeparture) {
233: JGraphpadParallelEdgeRouter.edgeDeparture = edgeDeparture;
234: }
235:
236: /**
237: * Getter to obtain the list of parallel edges
238: *
239: * @param edge
240: * Edge on which one wants to know parallel edges
241: * @return Object[] Array of parallel edges (include edge passed on
242: * argument)
243: */
244: private Object[] getParallelEdges(EdgeView edge) {
245: // FIXME: The model is stored in the cells only in the default
246: // implementations. Otherwise we must use the real model here.
247: return DefaultGraphModel.getEdgesBetween(emptyModel, edge
248: .getSource().getParentView().getCell(), edge
249: .getTarget().getParentView().getCell(), false);
250: }
251:
252: }
|