1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.layout.hierarchic;
15  
16  import y.base.DataProvider;
17  import y.base.Node;
18  import y.base.NodeCursor;
19  import y.layout.LayoutOrientation;
20  import y.layout.NodeLayout;
21  import y.layout.hierarchic.IncrementalHierarchicLayouter;
22  import y.layout.hierarchic.incremental.SwimLaneDescriptor;
23  import y.view.Drawable;
24  import y.view.Graph2D;
25  import y.view.Graph2DView;
26  
27  import java.awt.BasicStroke;
28  import java.awt.Color;
29  import java.awt.Graphics2D;
30  import java.awt.Rectangle;
31  import java.awt.Stroke;
32  import java.awt.geom.Line2D;
33  import java.util.ArrayList;
34  import java.util.List;
35  
36  /**
37   * Drawable implementation that displays swim lanes
38   */
39  final class SwimlaneDrawable implements Drawable {
40    private static final int X = 0;
41    private static final int Y = 1;
42  
43  
44    private final Line2D.Double line;
45    private final Stroke stroke;
46  
47    private List lanes;
48    private Rectangle bounds;
49    private double spacing;
50    private Color[] colors;
51    private byte orientation;
52    private boolean lastUpdateWasVertical;
53  
54    private Graph2D graph;
55    private Graph2DView view;
56    private boolean accessGraph;
57  
58    SwimlaneDrawable( final Graph2D graph ) {
59      this(graph, null, true);
60    }
61  
62    SwimlaneDrawable( final Graph2DView view ) {
63      this(null, view, false);
64    }
65  
66    SwimlaneDrawable(
67            final Graph2D graph, final DataProvider swimLaneDescriptors
68    ) {
69      this(graph, null, true);
70    }
71  
72    private SwimlaneDrawable(
73            final Graph2D graph, final Graph2DView view, final boolean accessGraph
74    ) {
75      this.graph = graph;
76      this.view = view;
77      this.accessGraph = accessGraph;
78  
79      this.line = new Line2D.Double();
80      this.stroke = new BasicStroke(1.25f);
81      this.spacing = 20.0d;
82      this.bounds = new Rectangle(20,20,200,200);
83      this.lanes = new ArrayList(20);
84      this.colors = new Color[]{new Color(150, 150, 255), new Color(0 , 0, 150)};
85      this.orientation = LayoutOrientation.TOP_TO_BOTTOM;
86      this.lastUpdateWasVertical = true;
87    }
88  
89    public void setEvenLaneColor( final Color color ) {
90      colors[0] = color;
91    }
92  
93    public void setOddLaneColor( final Color color ) {
94      colors[1] = color;
95    }
96  
97    public byte getOrientation() {
98      return orientation;
99    }
100 
101   public void setOrientation( final byte orientation ) {
102     this.orientation = orientation;
103   }
104 
105   public Rectangle getBounds() {
106     return bounds;
107   }
108 
109   public void updateLanes() {
110     lanes.clear();
111 
112     final Graph2D g = getGraph();
113 
114     if (g.N() < 1) {
115       return;
116     }
117 
118     final DataProvider slds = getSwimLaneDescriptors(g);
119     if (slds == null) {
120       return;
121     }
122 
123     double minY;
124     double maxY;
125     double minX;
126     double maxX;
127     if (LayoutOrientation.TOP_TO_BOTTOM == orientation ||
128         LayoutOrientation.BOTTOM_TO_TOP == orientation) {
129       minY = Double.MAX_VALUE;
130       maxY = -Double.MAX_VALUE;
131       for (NodeCursor nc = g.nodes(); nc.ok(); nc.next()){
132         final Node node = nc.node();
133         final NodeLayout nl = g.getNodeLayout(node);
134         minY = Math.min(minY, nl.getY());
135         maxY = Math.max(maxY, nl.getY() + nl.getHeight());
136 
137         final SwimLaneDescriptor sld = (SwimLaneDescriptor) slds.get(node);
138         if (sld == null) {
139           continue;
140         }
141         while (lanes.size() - 1 < sld.getComputedLaneIndex()){
142           lanes.add(new double[][]{{0, 0, 0, 0}, {0, 0}});
143         }
144         double[][] laneData = (double[][]) lanes.get(sld.getComputedLaneIndex());
145         laneData[X][0] = sld.getComputedLanePosition();
146         laneData[X][1] = laneData[X][0] + sld.getLeftLaneInset();
147         laneData[X][3] = laneData[X][0] + sld.getComputedLaneWidth();
148         laneData[X][2] = laneData[X][3] - sld.getRightLaneInset();
149       }
150 
151       minX = Double.MAX_VALUE;
152       maxX = -Double.MAX_VALUE;
153       for (int i = 0; i < lanes.size(); i++){
154         double[][] laneData = (double[][]) lanes.get(i);
155         laneData[Y][0] = minY - spacing;
156         laneData[Y][1] = maxY + spacing;
157         minX = Math.min(laneData[X][0], minX);
158         maxX = Math.max(laneData[X][3], maxX);
159       }
160 
161       lastUpdateWasVertical = true;
162     } else {
163       minX = Double.MAX_VALUE;
164       maxX = -Double.MAX_VALUE;
165       for (NodeCursor nc = g.nodes(); nc.ok(); nc.next()){
166         final Node node = nc.node();
167         final NodeLayout nl = g.getNodeLayout(node);
168         minX = Math.min(minX, nl.getX());
169         maxX = Math.max(maxX, nl.getX() + nl.getWidth());
170 
171         final SwimLaneDescriptor sld = (SwimLaneDescriptor) slds.get(node);
172         if (sld == null) {
173           continue;
174         }
175         while (lanes.size() - 1 < sld.getComputedLaneIndex()){
176           lanes.add(new double[][]{{0, 0, 0, 0}, {0, 0}});
177         }
178         double[][] laneData = (double[][]) lanes.get(sld.getComputedLaneIndex());
179         final double sign = LayoutOrientation.LEFT_TO_RIGHT == orientation ? -1 : 1;
180         laneData[X][0] = sign * sld.getComputedLanePosition();
181         laneData[X][1] = laneData[X][0] + sign * sld.getLeftLaneInset();
182         laneData[X][3] = laneData[X][0] + sign * sld.getComputedLaneWidth();
183         laneData[X][2] = laneData[X][3] - sign * sld.getRightLaneInset();
184       }
185 
186       minY = Double.MAX_VALUE;
187       maxY = -Double.MAX_VALUE;
188       for (int i = 0; i < lanes.size(); i++){
189         double[][] laneData = (double[][]) lanes.get(i);
190         laneData[Y][0] = minX - spacing;
191         laneData[Y][1] = maxX + spacing;
192         minY = Math.min(laneData[X][0], minY);
193         maxY = Math.max(laneData[X][3], maxY);
194       }
195 
196       lastUpdateWasVertical = false;
197     }
198 
199     bounds.setFrame(minX, minY, maxX - minX, maxY - minY);
200     g.updateViews();
201   }
202 
203   public void paint( final Graphics2D g ) {
204     if (lanes.isEmpty()) {
205       return;
206     }
207 
208     final Color oldColor = g.getColor();
209     final Stroke oldStroke = g.getStroke();
210     g.setStroke(stroke);
211     if (lastUpdateWasVertical) {
212       for (int i = 0; i < lanes.size(); i++) {
213         double[][] lane = (double[][]) lanes.get(i);
214 
215         line.y1 = lane[Y][0];
216         line.y2 = lane[Y][1];
217 
218         g.setColor(colors[i % colors.length]);
219         line.x1 = line.x2 = lane[X][1];
220         g.draw(line);
221         line.x1 = line.x2 = lane[X][2];
222         g.draw(line);
223       }
224     } else {
225       for (int i = 0; i < lanes.size(); i++) {
226         double[][] lane = (double[][]) lanes.get(i);
227 
228         line.x1 = lane[Y][0];
229         line.x2 = lane[Y][1];
230 
231         g.setColor(colors[i % colors.length]);
232         line.y1 = line.y2 = lane[X][1];
233         g.draw(line);
234         line.y1 = line.y2 = lane[X][2];
235         g.draw(line);
236       }
237     }
238     g.setStroke(oldStroke);
239     g.setColor(oldColor);
240   }
241 
242   private DataProvider getSwimLaneDescriptors( final Graph2D graph ) {
243     return graph.getDataProvider(IncrementalHierarchicLayouter.SWIMLANE_DESCRIPTOR_DPKEY);
244   }
245 
246   private Graph2D getGraph() {
247     if (accessGraph) {
248       return graph;
249     } else {
250       return view.getGraph2D();
251     }
252   }
253 }
254