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;
15  
16  import demo.view.DemoBase;
17  import y.base.Edge;
18  import y.base.Node;
19  import y.layout.CanonicMultiStageLayouter;
20  import y.layout.LabelLayoutConstants;
21  import y.layout.LayoutOrientation;
22  import y.layout.Layouter;
23  import y.layout.OrientationLayouter;
24  import y.layout.circular.CircularLayouter;
25  import y.layout.hierarchic.IncrementalHierarchicLayouter;
26  import y.layout.labeling.AbstractLabelingAlgorithm;
27  import y.layout.organic.SmartOrganicLayouter;
28  import y.layout.orthogonal.OrthogonalLayouter;
29  import y.option.OptionHandler;
30  import y.util.D;
31  import y.view.EdgeLabel;
32  import y.view.Graph2D;
33  import y.view.Graph2DLayoutExecutor;
34  import y.view.SmartEdgeLabelModel;
35  import y.view.SmartNodeLabelModel;
36  
37  import javax.swing.AbstractAction;
38  import javax.swing.JToolBar;
39  import javax.swing.JRootPane;
40  import java.awt.EventQueue;
41  import java.awt.event.ActionEvent;
42  import java.awt.event.ActionListener;
43  import java.util.Arrays;
44  import java.util.List;
45  import java.util.Locale;
46  import java.util.Random;
47  
48  /**
49   * Demonstrates how layout and labeling algorithms can be applied to a
50   * graph being displayed within a viewer component.
51   * <br>
52   * The view actions provided with ViewActionDemo are accessible as well.
53   * <br>
54   * The layout options can be either given as command line arguments or
55   * within a settings dialog that will automatically pop up if no
56   * command line arguments are provided.
57   * <br>
58   * Command line usage:
59   * <pre>
60   * java demo.view.layout.LayoutDemo {hierarchic,orthogonal,organic,circular} [anim] [label]
61   * </pre>
62   * <br>
63   * The first command line argument is the name of the layout algorithm
64   * to be applied. 
65   * If the optional argument "label" is given, a generic labeling algorithm will 
66   * be used to place all edge labels.
67   * If the optional argument "anim" is given, then the
68   * layout will be applied to the graph in an animated fashion.
69   */
70  public class LayoutDemo extends DemoBase {
71    OptionHandler layoutOptions;
72  
73    public LayoutDemo() {
74      this(new String[]{"Hierarchical", "anim", "label"});
75    }
76  
77    public LayoutDemo(String[] args) {
78      initLayoutOptions(args);
79  
80      //build sample graph
81      buildGraph(view.getGraph2D());
82    }
83  
84    /**
85     * Initializes layout options from command line arguments.
86     * If no command line arguments are given, a settings dialog will
87     * automatically be displayed.
88     */
89    void initLayoutOptions(String[] args) {
90      //create layout options
91      boolean anim = true;
92      boolean label = true;
93      String layout = "Hierarchical";
94      if (args.length > 0) {
95        layout = args[0];
96        List list = Arrays.asList(args);
97        anim = list.contains("anim");
98        label = list.contains("label");
99      }
100 
101     layoutOptions = new OptionHandler("Settings");
102     final String[] algoEnum = {"Hierarchical", "Orthogonal", "Organic", "Circular"};
103     layoutOptions.addEnum("Layout Style", algoEnum, layout, null);
104     layoutOptions.addBool("Generic Labeling", label);
105     layoutOptions.addBool("Layout Morphing", anim);
106 
107     if (args.length == 0) {
108       layoutOptions.showEditor();
109     }
110   }
111 
112 
113   /** Creates a small random graph with labelled edges */
114   void buildGraph(Graph2D graph) {
115     graph.clear();
116     Node[] nodes = new Node[10];
117     for (int i = 0; i < nodes.length; i++) {
118       nodes[i] = graph.createNode();
119       graph.getRealizer(nodes[i]).setLabelText(String.valueOf(i));
120       SmartNodeLabelModel model = new SmartNodeLabelModel();
121       graph.getRealizer(nodes[i]).getLabel().setLabelModel(model);
122       graph.getRealizer(nodes[i]).getLabel().setModelParameter(model.getDefaultParameter());
123     }
124 
125     Random random = new Random(0L);
126     for (int i = 0; i < nodes.length; i++) {
127       for (int j = i + 1; j < nodes.length; j++) {
128         if (random.nextDouble() > 0.75) {
129           Edge edge = graph.createEdge(nodes[i], nodes[j]);
130           EdgeLabel edgeLabel = new EdgeLabel(i + " -> " + j);
131           // For generic edge labeling, edge label models "smart" or "free" yield the best
132           // results.
133           SmartEdgeLabelModel model = new SmartEdgeLabelModel();
134           edgeLabel.setLabelModel(model);
135           edgeLabel.setModelParameter(model.getDefaultParameter());
136           edgeLabel.setPreferredPlacement((byte)(LabelLayoutConstants.PLACE_RIGHT_OF_EDGE |
137                                                  LabelLayoutConstants.PLACE_AT_CENTER));
138           graph.getRealizer(edge).addLabel(edgeLabel);
139         }
140       }
141     }
142   }
143 
144   /**
145    * Adds an extra layout action to the toolbar
146    */
147   protected JToolBar createToolBar() {
148     JToolBar toolBar = super.createToolBar();
149     toolBar.addSeparator();
150     toolBar.add(createActionControl(new LayoutAction()));
151     return toolBar;
152   }
153 
154   /**
155    * Layout action that configures and launches a layout algorithm.
156    */
157   class LayoutAction extends AbstractAction {
158     LayoutAction() {
159       super("Layout", SHARED_LAYOUT_ICON);
160     }
161 
162     public void actionPerformed(ActionEvent e) {
163       final ActionListener applyLayoutListener = new ActionListener() {
164         public void actionPerformed(ActionEvent e) {
165           applyLayout();
166         }
167       };
168 
169       OptionSupport.showDialog(layoutOptions, applyLayoutListener, true, view.getFrame());
170     }
171   }
172 
173   /**
174    * Configures and invokes a layout algorithm
175    */
176   void applyLayout() {
177     Layouter layouter = createLayouter(layoutOptions);
178     applyLayout(layouter, layoutOptions.getBool("Layout Morphing"));
179   }
180 
181   /**
182    * Creates and returns a Layouter instance according to the given layout options.
183    */
184   Layouter createLayouter(OptionHandler layoutOptions) {
185 
186     String layout = layoutOptions.getString("Layout Style");
187     boolean label = layoutOptions.getBool("Generic Labeling");
188 
189     CanonicMultiStageLayouter layouter = null;
190 
191     if ("Circular".equals(layout)) {
192       CircularLayouter cl = new CircularLayouter();
193       cl.getSingleCycleLayouter().setMinimalNodeDistance(100);
194       layouter = cl;
195     } else if ("Hierarchical".equals(layout)) {
196       IncrementalHierarchicLayouter hl = new IncrementalHierarchicLayouter();
197       //set some options
198       hl.getNodeLayoutDescriptor().setMinimumLayerHeight(60.0);
199       hl.getNodeLayoutDescriptor().setMinimumDistance(20.0);
200 
201       //use left-to-right layout orientation
202       OrientationLayouter ol = new OrientationLayouter();
203       ol.setOrientation(LayoutOrientation.LEFT_TO_RIGHT);
204       hl.setOrientationLayouter(ol);
205 
206       layouter = hl;
207     } else if ("Organic".equals(layout)) {
208       SmartOrganicLayouter ol = new SmartOrganicLayouter();
209       //set some options
210       ol.setPreferredEdgeLength(80.0);
211       ol.setQualityTimeRatio(1.0);
212       ol.setNodeOverlapsAllowed(false);
213       layouter = ol;
214     } else if ("Orthogonal".equals(layout)) {
215       //set some options
216       layouter = new OrthogonalLayouter();
217     }
218 
219     if (layouter == null) {
220       usage();
221     }
222 
223     if (label) {
224       // Automatic placement of edge labels using the generic labeling feature.
225       AbstractLabelingAlgorithm la = 
226           (AbstractLabelingAlgorithm)layouter.getLabelLayouter();
227       // Node labels already have a good position.
228       la.setPlaceNodeLabels(false);
229       layouter.setLabelLayouterEnabled(true);
230     }
231 
232     return layouter;
233   }
234 
235   /**
236    * Applies the given layout algorithm to the graph
237    * residing in the view. depending on the parameter
238    * the layout will be applied in an animated fashion
239    * to the graph or not.
240    */
241   void applyLayout(Layouter layouter, boolean animated) {
242     if (animated) {
243       final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor();
244       layoutExecutor.getLayoutMorpher().setPreferredDuration(800L);
245       layoutExecutor.getLayoutMorpher().setEasedExecution(true);
246       layoutExecutor.getLayoutMorpher().setSmoothViewTransform(true);
247       layoutExecutor.doLayout(view, layouter);
248     } else {
249       view.applyLayout(layouter);
250 
251       //adjusts the zoom and origin of the view to make the
252       //whole graph visible
253       view.fitContent();
254 
255       //an ALTERNATIVE to fitContent is updateWorldRect
256       //adjusts view scrollbars, so that the whole graph is visible
257       //does not change zoom on the view
258 
259       //view.updateWorldRect();
260 
261       view.updateView();
262     }
263   }
264 
265 
266   void usage() {
267     D.bug("USAGE: java demo.view.layout.LayoutDemoTmp " +
268         "{organic,circular,random,hierarchic,orthogonal} [label] [anim]");
269     System.exit(0);
270   }
271 
272   public void addContentTo(final JRootPane rootPane) {
273     super.addContentTo(rootPane);
274     EventQueue.invokeLater(new Runnable() {
275       public void run() {
276         applyLayout();
277       }
278     });
279   }
280 
281   public static void main( final String[] args ) {
282     EventQueue.invokeLater(new Runnable() {
283       public void run() {
284         Locale.setDefault(Locale.ENGLISH);
285         initLnF();
286         new LayoutDemo(args).start();
287       }
288     });
289   }
290 }
291