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