1
28 package demo.layout.hierarchic;
29
30 import demo.view.DemoBase;
31 import y.view.Graph2DLayoutExecutor;
32 import y.view.Graph2D;
33 import y.view.EdgeRealizer;
34 import y.view.ViewMode;
35 import y.view.LineType;
36 import y.layout.hierarchic.IncrementalHierarchicLayouter;
37 import y.layout.hierarchic.incremental.SimplexNodePlacer;
38 import y.layout.Layouter;
39 import y.layout.LayoutGraph;
40 import y.base.EdgeCursor;
41 import y.base.Edge;
42 import y.base.EdgeMap;
43 import y.base.Node;
44 import y.base.NodeList;
45 import y.base.EdgeList;
46 import y.option.OptionHandler;
47 import y.option.OptionItem;
48 import y.option.TableEditorFactory;
49 import y.option.Editor;
50 import y.util.Maps;
51 import y.util.GraphHider;
52 import y.algo.ShortestPaths;
53 import y.algo.Paths;
54 import y.algo.Cycles;
55
56 import javax.swing.BorderFactory;
57 import javax.swing.JToolBar;
58 import javax.swing.AbstractAction;
59 import javax.swing.JComponent;
60 import javax.swing.JSplitPane;
61 import java.awt.event.ActionEvent;
62 import java.awt.event.MouseEvent;
63 import java.awt.EventQueue;
64 import java.awt.Color;
65 import java.awt.Dimension;
66 import java.awt.BorderLayout;
67 import java.beans.PropertyChangeListener;
68 import java.beans.PropertyChangeEvent;
69 import java.util.Locale;
70
71
88 public class CriticalPathDemo extends DemoBase {
89 private static final Color COLOR_CRITICAL_EDGE = Color.RED;
90 private static final Color COLOR_COMMON_EDGE = Color.BLACK;
91
92 private boolean backloopRoutingEnabled;
93 private boolean edgeStraighteningOptimizationEnabled;
94 private boolean useOrthogonalEdgeRoutes;
95 private int minimalNodeDistance;
96 private int minimalLayerDistance;
97
98 public CriticalPathDemo() {
99 this(null);
100 }
101
102 public CriticalPathDemo(final String helpFilePath) {
103 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, createOptionTable(createOptionHandler()),
104 view);
105 splitPane.setBorder(BorderFactory.createEmptyBorder());
106 contentPane.add(splitPane, BorderLayout.CENTER);
107 addHelpPane(helpFilePath);
108 loadInitialGraph();
109 }
110
111
112 protected JToolBar createToolBar() {
113 JToolBar bar = super.createToolBar();
114 bar.addSeparator();
115 bar.add(createActionControl(new LayoutAction()));
116 bar.addSeparator();
117 bar.add(new MarkSelectionAction());
118 bar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
119 bar.add(new UnmarkSelectionAction());
120 bar.addSeparator();
121 bar.add(new MarkLongestPath());
122 bar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
123 bar.add(new MarkShortestPathBetweenNodes());
124 return bar;
125 }
126
127 private void loadInitialGraph() {
128 loadGraph("resource/critical_path.graphml");
129 }
130
131
132 protected OptionHandler createOptionHandler() {
133 final OptionHandler layoutOptionHandler = new OptionHandler("Option Table");
134
135 minimalLayerDistance = 60;
136 OptionItem minimalLayerDistanceItem = layoutOptionHandler.addInt("Minimal Layer Distance", minimalLayerDistance);
137 minimalLayerDistanceItem.addPropertyChangeListener(new PropertyChangeListener() {
138 public void propertyChange(PropertyChangeEvent evt) {
139 minimalLayerDistance = layoutOptionHandler.getInt("Minimal Layer Distance");
140 }
141 });
142
143 minimalNodeDistance = 30;
144 OptionItem minimalNodeDistanceItem = layoutOptionHandler.addInt("Minimal Node Distance", minimalNodeDistance);
145 minimalNodeDistanceItem.addPropertyChangeListener(new PropertyChangeListener() {
146 public void propertyChange(PropertyChangeEvent evt) {
147 minimalNodeDistance = layoutOptionHandler.getInt("Minimal Node Distance");
148 }
149 });
150
151 useOrthogonalEdgeRoutes = true;
152 OptionItem useOrthogonalEdgeRoutesItem = layoutOptionHandler.addBool("Use Orthogonal Edge Routes", useOrthogonalEdgeRoutes);
153 useOrthogonalEdgeRoutesItem.addPropertyChangeListener(new PropertyChangeListener() {
154 public void propertyChange(PropertyChangeEvent evt) {
155 useOrthogonalEdgeRoutes = layoutOptionHandler.getBool("Use Orthogonal Edge Routes");
156 }
157 });
158
159 backloopRoutingEnabled = true;
160 OptionItem backloopRoutingEnabledItem = layoutOptionHandler.addBool("Enable Backloop Routing", backloopRoutingEnabled);
161 backloopRoutingEnabledItem.addPropertyChangeListener(new PropertyChangeListener() {
162 public void propertyChange(PropertyChangeEvent evt) {
163 backloopRoutingEnabled = layoutOptionHandler.getBool("Enable Backloop Routing");
164 }
165 });
166
167 edgeStraighteningOptimizationEnabled = true;
168 OptionItem edgeStraighteningOptimizationEnabledItem = layoutOptionHandler.addBool("Enable Edge Straightening", edgeStraighteningOptimizationEnabled);
169 edgeStraighteningOptimizationEnabledItem.addPropertyChangeListener(new PropertyChangeListener() {
170 public void propertyChange(PropertyChangeEvent evt) {
171 edgeStraighteningOptimizationEnabled = layoutOptionHandler.getBool("Enable Edge Straightening");
172 }
173 });
174
175 return layoutOptionHandler;
176 }
177
178 class LayoutAction extends AbstractAction {
179 LayoutAction() {
180 super("Layout", SHARED_LAYOUT_ICON);
181 }
182
183 public void actionPerformed(ActionEvent e) {
184 Graph2D g = view.getGraph2D();
186 EdgeMap edge2CriticalValue = Maps.createHashedEdgeMap();
187 for (EdgeCursor ec = g.edges(); ec.ok(); ec.next()) {
188 Edge edge = ec.edge();
189 if (isCritical(edge, g)) {
190 edge2CriticalValue.setDouble(edge, 1.0);
191 } else {
192 edge2CriticalValue.setDouble(edge, 0.0);
193 }
194 }
195
196 g.addDataProvider(IncrementalHierarchicLayouter.CRITICAL_EDGE_DPKEY, edge2CriticalValue);
198
199 try {
200 Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
201 executor.getLayoutMorpher().setSmoothViewTransform(true);
202 executor.getLayoutMorpher().setEasedExecution(true);
203 executor.doLayout(view, getHierarchicLayouter());
204 } finally {
205
206 g.removeDataProvider(IncrementalHierarchicLayouter.CRITICAL_EDGE_DPKEY);
207 }
208 }
209 }
210
211 private void markAsCriticalEdge(Edge e, Graph2D g) {
212 EdgeRealizer eRealizer = g.getRealizer(e);
213 eRealizer.setLineColor(COLOR_CRITICAL_EDGE);
214 eRealizer.setLineType(LineType.LINE_2);
215 }
216
217 private void unmarkEdge(Edge e,Graph2D g) {
218 EdgeRealizer eRealizer = g.getRealizer(e);
219 eRealizer.setLineColor(COLOR_COMMON_EDGE);
220 eRealizer.setLineType(LineType.LINE_1);
221 }
222
223 private boolean isCritical(Edge e, Graph2D g) {
224 EdgeRealizer eRealizer = g.getRealizer(e);
225 return (eRealizer.getLineColor() == COLOR_CRITICAL_EDGE);
226 }
227
228 class MarkSelectionAction extends AbstractAction {
229 MarkSelectionAction() {
230 super(" Mark Selected Edges ");
231 }
232
233 public void actionPerformed(ActionEvent e) {
234 Graph2D g = view.getGraph2D();
235 g.firePreEvent();
236 g.backupRealizers();
237 try {
238 for (EdgeCursor ec = g.edges(); ec.ok(); ec.next()) {
239 Edge edge = ec.edge();
240 if (g.isSelected(edge)) {
241 markAsCriticalEdge(edge, g);
242 }
243 }
244 g.updateViews();
245 } finally {
246 g.firePostEvent();
247 }
248 }
249 }
250
251 class UnmarkSelectionAction extends AbstractAction {
252 UnmarkSelectionAction() {
253 super(" Unmark Selected Edges ");
254 }
255
256 public void actionPerformed(ActionEvent e) {
257 Graph2D g = view.getGraph2D();
258 g.firePreEvent();
259 g.backupRealizers();
260 try {
261 for (EdgeCursor ec = g.edges(); ec.ok(); ec.next()) {
262 Edge edge = ec.edge();
263 if (g.isSelected(edge)) {
264 unmarkEdge(edge, g);
265 }
266 }
267 g.updateViews();
268 } finally {
269 g.firePostEvent();
270 }
271 }
272 }
273
274 class MarkShortestPathBetweenNodes extends AbstractAction {
275 MarkShortestPathBetweenNodes() {
276 super(" Mark Path Between Two Nodes ");
277 }
278
279 public void actionPerformed(ActionEvent ae) {
280 Graph2D g = view.getGraph2D();
281 g.firePreEvent();
282 g.backupRealizers();
283 try {
284 NodeList selectedNodes = new NodeList(g.selectedNodes());
285 if (!selectedNodes.isEmpty()) {
286 EdgeMap path = Maps.createHashedEdgeMap();
287 Node n1 = selectedNodes.firstNode();
288 Node n2 = selectedNodes.lastNode();
289 ShortestPaths.findShortestUniformPaths(g, n1, n2, true, path);
290 if (!foundPath(g, path)) {
291 ShortestPaths.findShortestUniformPaths(g, n2, n1, true, path);
292 }
293 for (EdgeCursor ec = g.edges(); ec.ok(); ec.next()) {
294 Edge e = ec.edge();
295 if (path.getBool(e)) {
296 markAsCriticalEdge(e, g);
297 } else {
298 unmarkEdge(e, g);
299 }
300 }
301 g.updateViews();
302 }
303 } finally {
304 g.firePostEvent();
305 }
306 }
307 }
308
309 class MarkLongestPath extends AbstractAction {
310 MarkLongestPath() {
311 super(" Mark Longest Path ");
312 }
313
314 public void actionPerformed(ActionEvent ae) {
315 Graph2D g = view.getGraph2D();
316 g.firePreEvent();
317 g.backupRealizers();
318 try {
319 for (EdgeCursor ec = g.edges(); ec.ok(); ec.next()) {
321 unmarkEdge(ec.edge(), g);
322 }
323
324 final EdgeMap cycleEdges = Maps.createIndexEdgeMap(new boolean[g.E()]);
326 Cycles.findCycleEdges(g, cycleEdges);
327 GraphHider hider = new GraphHider(g);
328 hider.hide(new EdgeList(g.edges(), cycleEdges));
329
330 for (EdgeCursor ec = Paths.findLongestPath(g).edges(); ec.ok(); ec.next()) {
332 markAsCriticalEdge(ec.edge(), g);
333 }
334 hider.unhideAll();
335 g.updateViews();
336 } finally {
337 g.firePostEvent();
338 }
339 }
340 }
341
342 private boolean foundPath(LayoutGraph g, EdgeMap path) {
343 for(EdgeCursor ec = g.edges(); ec.ok(); ec.next()) {
344 if(path.getBool(ec.edge())) {
345 return true;
346 }
347 }
348 return false;
349 }
350
351 private JComponent createOptionTable(OptionHandler oh) {
352 TableEditorFactory tef = new TableEditorFactory();
354 oh.setAttribute(TableEditorFactory.ATTRIBUTE_INFO_POSITION, TableEditorFactory.InfoPosition.NONE);
355 final Editor editor = tef.createEditor(oh);
356
357 JComponent optionPane = editor.getComponent();
358 optionPane.setPreferredSize(new Dimension(200, 100));
359 optionPane.setMinimumSize(new Dimension(200, 100));
360 optionPane.setMaximumSize(new Dimension(250, 100));
361 return optionPane;
362 }
363
364 private Layouter getHierarchicLayouter() {
365 IncrementalHierarchicLayouter layouter = new IncrementalHierarchicLayouter();
366 layouter.setBackloopRoutingEnabled(backloopRoutingEnabled);
367 if(layouter.getNodePlacer() instanceof SimplexNodePlacer) {
368 ((SimplexNodePlacer) layouter.getNodePlacer()).setEdgeStraighteningOptimizationEnabled(edgeStraighteningOptimizationEnabled);
369 }
370 layouter.setOrthogonallyRouted(useOrthogonalEdgeRoutes);
371 layouter.setMinimumLayerDistance(minimalLayerDistance);
372 layouter.setNodeToNodeDistance(minimalNodeDistance);
373 return layouter;
374 }
375
376 protected void registerViewModes() {
377 super.registerViewModes();
378 view.addViewMode(new ViewMode() {
379
380 public void mouseClicked(MouseEvent e) {
381 super.mouseClicked(e);
382 Graph2D g = view.getGraph2D();
383 if (e.getClickCount() == 2) {
384 final EdgeCursor selectedEdges = g.selectedEdges();
385 if (selectedEdges != null && selectedEdges.size() > 0) {
386 for (; selectedEdges.ok(); selectedEdges.next()) {
388 if(isCritical(selectedEdges.edge(), g)) {
389 unmarkEdge(selectedEdges.edge(), g);
390 } else {
391 markAsCriticalEdge(selectedEdges.edge(), g);
392 }
393 }
394 }
395 view.updateView();
396 }
397 }
398 });
399 }
400
401
402 public static void main(String[] args) {
403 EventQueue.invokeLater(new Runnable() {
404 public void run() {
405 Locale.setDefault(Locale.ENGLISH);
406 initLnF();
407 (new CriticalPathDemo("resource/criticalpathhelp.html")).start("Critical Path Demo");
408 }
409 });
410 }
411 }
412