1
28 package demo.layout;
29
30 import demo.view.DemoBase;
31 import y.base.Edge;
32 import y.base.EdgeCursor;
33 import y.view.DefaultGraph2DRenderer;
34 import y.layout.CurveFittingLayoutStage;
35 import y.layout.EdgeBundleDescriptor;
36 import y.layout.EdgeBundling;
37 import y.layout.Layouter;
38 import y.layout.circular.CircularLayouter;
39 import y.layout.radial.RadialLayouter;
40 import y.layout.router.OrganicEdgeRouter;
41 import y.layout.tree.BalloonLayouter;
42 import y.layout.tree.TreeReductionStage;
43 import y.view.Arrow;
44 import y.view.BezierPathCalculator;
45 import y.view.EdgeRealizer;
46 import y.view.EditMode;
47 import y.view.GenericEdgeRealizer;
48 import y.view.Graph2D;
49 import y.view.Graph2DLayoutExecutor;
50 import y.view.PolyLineEdgeRealizer;
51
52 import java.awt.Dimension;
53 import java.awt.EventQueue;
54 import java.awt.FlowLayout;
55 import java.awt.event.ActionEvent;
56 import java.awt.event.ActionListener;
57 import java.awt.event.ItemEvent;
58 import java.awt.event.ItemListener;
59 import java.net.URL;
60 import java.util.Locale;
61 import java.util.Map;
62 import javax.swing.AbstractAction;
63 import javax.swing.JCheckBox;
64 import javax.swing.JComboBox;
65 import javax.swing.JLabel;
66 import javax.swing.JSlider;
67 import javax.swing.JTextField;
68 import javax.swing.JToolBar;
69 import javax.swing.SwingConstants;
70 import javax.swing.border.EtchedBorder;
71 import javax.swing.event.ChangeEvent;
72 import javax.swing.event.ChangeListener;
73
74
92 public class CurveFittingLayoutStageDemo extends DemoBase {
93
94 private static final byte STYLE_BALLOON = 1;
95 private static final byte STYLE_CIRCULAR = 2;
96 private static final byte STYLE_RADIAL = 3;
97 private static final byte STYLE_ORGANIC_ROUTER = 4;
98
99 private static final double NODE_WIDTH = 50;
100 private static final double NODE_HEIGHT = 25;
101
102 private static final String BEZIER_CONFIG = "BEZIER_CONFIGURATION";
103
104 private JSlider errorSlider;
105 private boolean isCurveFitted = false;
106
107 private byte style = STYLE_BALLOON;
108
109 public CurveFittingLayoutStageDemo(){
110 this(null);
111 }
112
113 public CurveFittingLayoutStageDemo( final String helpFilePath ) {
114 loadGraph("resource/curve-fitting-sample.graphml");
116 addHelpPane(helpFilePath);
117 }
118
119 protected void loadGraph(URL resource) {
120 super.loadGraph(resource);
121 }
122
123 protected JToolBar createToolBar() {
124 final JToolBar toolBar = super.createToolBar();
125
126 final int height = 20;
127 final int separatorWidth = 10;
128
129 toolBar.setLayout(new FlowLayout(FlowLayout.LEFT));
130 toolBar.addSeparator(new Dimension(separatorWidth, height));
131
132 toolBar.add(createActionControl(new LayoutAction()));
133
134 final Graph2D graph = view.getGraph2D();
135
136 final JComboBox comboBox = new JComboBox(new Object[]{"Balloon Layout", "Circular Layout", "Radial Layout", "Organic Edge Router"});
137 comboBox.addActionListener(new ActionListener() {
138 public void actionPerformed( ActionEvent e ) {
139 switch (comboBox.getSelectedIndex()) {
140 case 0:
141 style = STYLE_BALLOON;
142 break;
143 case 1:
144 style = STYLE_CIRCULAR;
145 break;
146 case 2:
147 style = STYLE_RADIAL;
148 break;
149 case 3:
150 style = STYLE_ORGANIC_ROUTER;
151 break;
152 default:
153 style = STYLE_BALLOON;
154 }
155 layout(graph, true);
157 }
158 });
159 toolBar.add(comboBox);
160
161 toolBar.addSeparator(new Dimension(separatorWidth, height));
162
163 final JTextField errorTextField = new JTextField();
164
165 final JLabel fittingErrorLabel = new JLabel("Error:", getIconResource("resource/properties.png"), SwingConstants.LEFT);
166
167 final JCheckBox fitCurve = new JCheckBox("Apply Bezier Fitting");
168 fitCurve.addItemListener(new ItemListener() {
169 public void itemStateChanged( final ItemEvent e ) {
170 isCurveFitted = e.getStateChange() == ItemEvent.SELECTED;
171 layout(graph, true);
172 errorSlider.setEnabled(isCurveFitted);
174 errorTextField.setEnabled(isCurveFitted);
175 fittingErrorLabel.setEnabled(isCurveFitted);
176 }
177 });
178
179 toolBar.add(fitCurve);
180 toolBar.add(fittingErrorLabel);
181
182 errorSlider = new JSlider(0, 50, 2);
183 errorSlider.addChangeListener(new ChangeListener() {
184 public void stateChanged( final ChangeEvent e ) {
185 errorTextField.setText(String.valueOf(errorSlider.getValue()));
186 layout(graph, false);
187 }
188 });
189 errorSlider.setEnabled(isCurveFitted);
190 errorTextField.setEnabled(isCurveFitted);
191 fittingErrorLabel.setEnabled(isCurveFitted);
192
193 toolBar.add(errorSlider);
194
195 errorTextField.setText(String.valueOf(errorSlider.getValue()));
196 errorTextField.setHorizontalAlignment(SwingConstants.CENTER);
197 errorTextField.setPreferredSize(new Dimension(height, height));
198 errorTextField.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
199 errorTextField.setEditable(false);
200
201 toolBar.add(errorTextField);
202
203 return toolBar;
204 }
205
206 protected EditMode createEditMode() {
207 EditMode editMode = super.createEditMode();
208 editMode.allowBendCreation(false);
210 editMode.assignNodeLabel(false);
212 return editMode;
213 }
214
215 protected void initialize() {
216 super.initialize();
217 final DefaultGraph2DRenderer graph2DRenderer = (DefaultGraph2DRenderer) view.getGraph2DRenderer();
219 graph2DRenderer.setDrawEdgesFirst(true);
220 graph2DRenderer.setDrawSelectionOnTop(true);
222 }
223
224 protected void configureDefaultRealizers() {
225 super.configureDefaultRealizers();
226 view.getGraph2D().getDefaultNodeRealizer().setSize(NODE_WIDTH, NODE_HEIGHT);
228
229 view.getGraph2D().getDefaultEdgeRealizer().setTargetArrow(Arrow.NONE);
230
231 final GenericEdgeRealizer.Factory f = GenericEdgeRealizer.getFactory();
233 final Map impls = f.createDefaultConfigurationMap();
234 impls.put(GenericEdgeRealizer.PathCalculator.class, new BezierPathCalculator(false));
235 f.addConfiguration(BEZIER_CONFIG, impls);
236 }
237
238
241 private void updateEdgeRealizers(Graph2D graph2D, EdgeCursor ec, boolean isBezier) {
242 for (; ec.ok(); ec.next()) {
243 final Edge edge = ec.edge();
244 final EdgeRealizer oldRealizer = graph2D.getRealizer(edge);
245 EdgeRealizer newRealizer;
246 if (isBezier) {
247 GenericEdgeRealizer ger = new GenericEdgeRealizer(oldRealizer);
249 ger.setConfiguration(BEZIER_CONFIG);
250 newRealizer = ger;
251 } else {
252 PolyLineEdgeRealizer per = new PolyLineEdgeRealizer(oldRealizer);
253 per.setSmoothedBends(true);
254 newRealizer = per;
255 }
256 graph2D.setRealizer(edge, newRealizer);
257 }
258 view.updateView();
259 }
260
261
266 private void layout(Graph2D graph, boolean morphAndFit) {
267 final CurveFittingLayoutStage curveFittingLayoutStage = new CurveFittingLayoutStage();
268 curveFittingLayoutStage.setMaximumError(errorSlider.getValue());
269
270 switch (style) {
271 case STYLE_BALLOON:
272 final BalloonLayouter balloon = new BalloonLayouter();
273 final TreeReductionStage trs = new TreeReductionStage();
275
276 enableBundling(trs.getEdgeBundling());
277
278 if (isCurveFitted) {
279 balloon.appendStage(curveFittingLayoutStage);
281 }
282 balloon.appendStage(trs);
284
285 runLayouter(balloon, graph, morphAndFit);
286 break;
287 case STYLE_CIRCULAR:
288 final CircularLayouter circular = new CircularLayouter();
289 enableBundling(circular.getEdgeBundling());
290
291 if (isCurveFitted) {
292 circular.appendStage(curveFittingLayoutStage);
294 }
295 runLayouter(circular, graph, morphAndFit);
296 break;
297 case STYLE_RADIAL:
298 final RadialLayouter radial = new RadialLayouter();
299 radial.setEdgeRoutingStrategy(RadialLayouter.EDGE_ROUTING_STRATEGY_ARC);
300
301 if (isCurveFitted) {
302 radial.appendStage(curveFittingLayoutStage);
304 }
305 runLayouter(radial, graph, morphAndFit);
306 break;
307 case STYLE_ORGANIC_ROUTER:
308 final OrganicEdgeRouter organicEdgeRouter = new OrganicEdgeRouter();
309
310 runLayouter(organicEdgeRouter, graph, morphAndFit);
313
314 if (isCurveFitted){
315 runLayouter(curveFittingLayoutStage, graph, morphAndFit);
317 }
318 break;
319 }
320 updateEdgeRealizers(graph, graph.edges(), isCurveFitted);
323 }
324
325
328 private void runLayouter(Layouter layouter, Graph2D graph, boolean morphAndFit) {
329 final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor();
330 if (morphAndFit) {
331 layoutExecutor.doLayout(view, layouter);
332 } else {
333 layoutExecutor.doLayout(graph, layouter);
334 }
335 }
336
337 private void enableBundling( final EdgeBundling ebl ) {
339 final EdgeBundleDescriptor descriptor = new EdgeBundleDescriptor();
340 descriptor.setBundled(true);
341 ebl.setDefaultBundleDescriptor(descriptor);
342 ebl.setBundlingStrength(0.99);
343 }
344
345
348 class LayoutAction extends AbstractAction {
349 LayoutAction() {
350 super("Layout", SHARED_LAYOUT_ICON);
351 }
352
353 public void actionPerformed(ActionEvent e ) {
354 layout(view.getGraph2D(), true);
355 }
356 }
357
358 public static void main(String[] args) {
359 EventQueue.invokeLater(new Runnable() {
360 public void run() {
361 Locale.setDefault(Locale.ENGLISH);
362 initLnF();
363 (new CurveFittingLayoutStageDemo("resource/curveFittingDemohelp.html")).start();
364 }
365 });
366 }
367 }
368