1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.layout.hierarchic;
29  
30  import y.base.DataProvider;
31  import y.base.Node;
32  import y.base.NodeCursor;
33  import y.layout.LayoutOrientation;
34  import y.layout.NodeLayout;
35  import y.layout.hierarchic.IncrementalHierarchicLayouter;
36  import y.layout.hierarchic.incremental.SwimLaneDescriptor;
37  import y.view.Drawable;
38  import y.view.Graph2D;
39  import y.view.Graph2DView;
40  
41  import java.awt.BasicStroke;
42  import java.awt.Color;
43  import java.awt.Graphics2D;
44  import java.awt.Rectangle;
45  import java.awt.Stroke;
46  import java.awt.geom.Line2D;
47  import java.util.ArrayList;
48  import java.util.List;
49  
50  /**
51   * Drawable implementation that displays swim lanes
52   */
53  final class SwimlaneDrawable implements Drawable {
54    private static final int X = 0;
55    private static final int Y = 1;
56  
57  
58    private final Line2D.Double line;
59    private final Stroke stroke;
60  
61    private List lanes;
62    private Rectangle bounds;
63    private double spacing;
64    private Color[] colors;
65    private byte orientation;
66    private boolean lastUpdateWasVertical;
67  
68    private Graph2D graph;
69    private Graph2DView view;
70    private boolean accessGraph;
71  
72    SwimlaneDrawable( final Graph2D graph ) {
73      this(graph, null, true);
74    }
75  
76    SwimlaneDrawable( final Graph2DView view ) {
77      this(null, view, false);
78    }
79  
80    SwimlaneDrawable(
81            final Graph2D graph, final DataProvider swimLaneDescriptors
82    ) {
83      this(graph, null, true);
84    }
85  
86    private SwimlaneDrawable(
87            final Graph2D graph, final Graph2DView view, final boolean accessGraph
88    ) {
89      this.graph = graph;
90      this.view = view;
91      this.accessGraph = accessGraph;
92  
93      this.line = new Line2D.Double();
94      this.stroke = new BasicStroke(1.25f);
95      this.spacing = 20.0d;
96      this.bounds = new Rectangle(20,20,200,200);
97      this.lanes = new ArrayList(20);
98      this.colors = new Color[]{new Color(150, 150, 255), new Color(0 , 0, 150)};
99      this.orientation = LayoutOrientation.TOP_TO_BOTTOM;
100     this.lastUpdateWasVertical = true;
101   }
102 
103   public void setEvenLaneColor( final Color color ) {
104     colors[0] = color;
105   }
106 
107   public void setOddLaneColor( final Color color ) {
108     colors[1] = color;
109   }
110 
111   public byte getOrientation() {
112     return orientation;
113   }
114 
115   public void setOrientation( final byte orientation ) {
116     this.orientation = orientation;
117   }
118 
119   public Rectangle getBounds() {
120     return bounds;
121   }
122 
123   public void updateLanes() {
124     lanes.clear();
125 
126     final Graph2D g = getGraph();
127 
128     if (g.N() < 1) {
129       return;
130     }
131 
132     final DataProvider slds = getSwimLaneDescriptors(g);
133     if (slds == null) {
134       return;
135     }
136 
137     double minY;
138     double maxY;
139     double minX;
140     double maxX;
141     if (LayoutOrientation.TOP_TO_BOTTOM == orientation ||
142         LayoutOrientation.BOTTOM_TO_TOP == orientation) {
143       minY = Double.MAX_VALUE;
144       maxY = -Double.MAX_VALUE;
145       for (NodeCursor nc = g.nodes(); nc.ok(); nc.next()){
146         final Node node = nc.node();
147         final NodeLayout nl = g.getNodeLayout(node);
148         minY = Math.min(minY, nl.getY());
149         maxY = Math.max(maxY, nl.getY() + nl.getHeight());
150 
151         final SwimLaneDescriptor sld = (SwimLaneDescriptor) slds.get(node);
152         if (sld == null) {
153           continue;
154         }
155         while (lanes.size() - 1 < sld.getComputedLaneIndex()){
156           lanes.add(new double[][]{{0, 0, 0, 0}, {0, 0}});
157         }
158         double[][] laneData = (double[][]) lanes.get(sld.getComputedLaneIndex());
159         laneData[X][0] = sld.getComputedLanePosition();
160         laneData[X][1] = laneData[X][0] + sld.getLeftLaneInset();
161         laneData[X][3] = laneData[X][0] + sld.getComputedLaneWidth();
162         laneData[X][2] = laneData[X][3] - sld.getRightLaneInset();
163       }
164 
165       minX = Double.MAX_VALUE;
166       maxX = -Double.MAX_VALUE;
167       for (int i = 0; i < lanes.size(); i++){
168         double[][] laneData = (double[][]) lanes.get(i);
169         laneData[Y][0] = minY - spacing;
170         laneData[Y][1] = maxY + spacing;
171         minX = Math.min(laneData[X][0], minX);
172         maxX = Math.max(laneData[X][3], maxX);
173       }
174 
175       lastUpdateWasVertical = true;
176     } else {
177       minX = Double.MAX_VALUE;
178       maxX = -Double.MAX_VALUE;
179       for (NodeCursor nc = g.nodes(); nc.ok(); nc.next()){
180         final Node node = nc.node();
181         final NodeLayout nl = g.getNodeLayout(node);
182         minX = Math.min(minX, nl.getX());
183         maxX = Math.max(maxX, nl.getX() + nl.getWidth());
184 
185         final SwimLaneDescriptor sld = (SwimLaneDescriptor) slds.get(node);
186         if (sld == null) {
187           continue;
188         }
189         while (lanes.size() - 1 < sld.getComputedLaneIndex()){
190           lanes.add(new double[][]{{0, 0, 0, 0}, {0, 0}});
191         }
192         double[][] laneData = (double[][]) lanes.get(sld.getComputedLaneIndex());
193         final double sign = LayoutOrientation.LEFT_TO_RIGHT == orientation ? -1 : 1;
194         laneData[X][0] = sign * sld.getComputedLanePosition();
195         laneData[X][1] = laneData[X][0] + sign * sld.getLeftLaneInset();
196         laneData[X][3] = laneData[X][0] + sign * sld.getComputedLaneWidth();
197         laneData[X][2] = laneData[X][3] - sign * sld.getRightLaneInset();
198       }
199 
200       minY = Double.MAX_VALUE;
201       maxY = -Double.MAX_VALUE;
202       for (int i = 0; i < lanes.size(); i++){
203         double[][] laneData = (double[][]) lanes.get(i);
204         laneData[Y][0] = minX - spacing;
205         laneData[Y][1] = maxX + spacing;
206         minY = Math.min(laneData[X][0], minY);
207         maxY = Math.max(laneData[X][3], maxY);
208       }
209 
210       lastUpdateWasVertical = false;
211     }
212 
213     bounds.setFrame(minX, minY, maxX - minX, maxY - minY);
214     g.updateViews();
215   }
216 
217   public void paint( final Graphics2D g ) {
218     if (lanes.isEmpty()) {
219       return;
220     }
221 
222     final Color oldColor = g.getColor();
223     final Stroke oldStroke = g.getStroke();
224     g.setStroke(stroke);
225     if (lastUpdateWasVertical) {
226       for (int i = 0; i < lanes.size(); i++) {
227         double[][] lane = (double[][]) lanes.get(i);
228 
229         line.y1 = lane[Y][0];
230         line.y2 = lane[Y][1];
231 
232         g.setColor(colors[i % colors.length]);
233         line.x1 = line.x2 = lane[X][1];
234         g.draw(line);
235         line.x1 = line.x2 = lane[X][2];
236         g.draw(line);
237       }
238     } else {
239       for (int i = 0; i < lanes.size(); i++) {
240         double[][] lane = (double[][]) lanes.get(i);
241 
242         line.x1 = lane[Y][0];
243         line.x2 = lane[Y][1];
244 
245         g.setColor(colors[i % colors.length]);
246         line.y1 = line.y2 = lane[X][1];
247         g.draw(line);
248         line.y1 = line.y2 = lane[X][2];
249         g.draw(line);
250       }
251     }
252     g.setStroke(oldStroke);
253     g.setColor(oldColor);
254   }
255 
256   private DataProvider getSwimLaneDescriptors( final Graph2D graph ) {
257     return graph.getDataProvider(IncrementalHierarchicLayouter.SWIMLANE_DESCRIPTOR_DPKEY);
258   }
259 
260   private Graph2D getGraph() {
261     if (accessGraph) {
262       return graph;
263     } else {
264       return view.getGraph2D();
265     }
266   }
267 }
268