1
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
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