1
28 package demo.layout.labeling;
29
30 import demo.view.DemoBase;
31 import demo.view.DemoDefaults;
32 import y.base.Edge;
33 import y.base.EdgeCursor;
34 import y.base.Graph;
35 import y.layout.EdgeLabelLayout;
36 import y.layout.EdgeLabelModel;
37 import y.layout.RotatedDiscreteEdgeLabelModel;
38 import y.layout.RotatedSliderEdgeLabelModel;
39 import y.layout.labeling.AbstractLabelingAlgorithm;
40 import y.layout.labeling.GreedyMISLabeling;
41 import y.layout.labeling.MISLabelingAlgorithm;
42 import y.option.CompoundEditor;
43 import y.option.ConstraintManager;
44 import y.option.DefaultEditorFactory;
45 import y.option.Editor;
46 import y.option.EditorFactory;
47 import y.option.ItemEditor;
48 import y.option.OptionGroup;
49 import y.option.OptionHandler;
50 import y.util.DataProviderAdapter;
51 import y.view.DefaultLabelConfiguration;
52 import y.view.EdgeLabel;
53 import y.view.EditMode;
54 import y.view.Graph2D;
55 import y.view.Graph2DLayoutExecutor;
56 import y.view.Graph2DViewActions;
57 import y.view.PopupMode;
58 import y.view.SmartEdgeLabelModel;
59 import y.view.YLabel;
60
61 import javax.swing.AbstractAction;
62 import javax.swing.JComponent;
63 import javax.swing.JMenu;
64 import javax.swing.JMenuBar;
65 import javax.swing.JPanel;
66 import javax.swing.JPopupMenu;
67 import javax.swing.JToolBar;
68 import java.awt.BorderLayout;
69 import java.awt.Color;
70 import java.awt.Dimension;
71 import java.awt.EventQueue;
72 import java.awt.event.ActionEvent;
73 import java.beans.PropertyChangeEvent;
74 import java.beans.PropertyChangeListener;
75 import java.net.URL;
76 import java.util.ArrayList;
77 import java.util.HashMap;
78 import java.util.Iterator;
79 import java.util.List;
80 import java.util.Locale;
81 import java.util.Map;
82
83
99 public class EdgeLabelingDemo extends DemoBase {
100 private static final String PROPERTIES_GROUP = "Edge Label Properties";
102 private static final String ROTATION_ANGLE_STRING = "Rotation Angle (Degrees)";
103 private static final String LABELING_MODEL_STRING = "Labeling Model";
104 private static final String ALLOW_90_DEGREE_DEVIATION_STRING = "Allow 90 Degree Deviation";
105 private static final String AUTO_FLIPPING_STRING = "Auto Flipping";
106 private static final String AUTO_ROTATE_STRING = "Auto Rotation";
107 private static final String EDGE_TO_LABEL_DISTANCE_STRING = "Edge To Label Distance";
108
109 private static final String MODEL_CENTERED = "Centered";
111 private static final String MODEL_TWO_POS = "2 Pos";
112 private static final String MODEL_SIX_POS = "6 Pos";
113 private static final String MODEL_THREE_POS_CENTER = "3 Pos Center";
114 private static final String MODEL_CENTER_SLIDER = "Center Slider";
115 private static final String MODEL_SIDE_SLIDER = "Side Slider";
116 private static final String[] EDGE_LABEL_MODELS = {
117 MODEL_CENTERED, MODEL_TWO_POS, MODEL_SIX_POS, MODEL_THREE_POS_CENTER, MODEL_CENTER_SLIDER, MODEL_SIDE_SLIDER
118 };
119
120 private static final String CUSTOM_LABELING_CONFIG_NAME = "CUSTOM_LABELING_CONFIG";
121 private static final Color LABEL_LINE_COLOR = new Color(153, 204, 255, 255);
122 static final Color LABEL_BACKGROUND_COLOR = Color.WHITE;
123 private static final int TOOLS_PANEL_WIDTH = 350;
124
125 private DefaultLabelConfiguration customLabelConfig;
126 private Map label2Model;
127 private final OptionHandler optionHandler;
128
129 public EdgeLabelingDemo() {
130 this(null);
131 }
132
133 public EdgeLabelingDemo(final String helpFilePath) {
134 view.setPreferredSize(new Dimension(650, 400));
136 view.setWorldRect(0, 0, 650, 400);
137 view.setFitContentOnResize(true);
138
139 optionHandler = createOptionHandler();
141 contentPane.add(createToolsPanel(helpFilePath), BorderLayout.EAST);
142
143 loadGraph("resource/orthogonal.graphml");
145 }
146
147 protected void initialize() {
148 label2Model = new HashMap();
149 view.getGraph2D().addDataProvider(AbstractLabelingAlgorithm.LABEL_MODEL_DPKEY, new DataProviderAdapter() {
150 public Object get(Object dataHolder) {
151 return label2Model.get(dataHolder);
152 }
153 });
154 }
155
156
159 void doLabelPlacement(final Object selectionKey) {
160 final double rotationAngle = Math.toRadians(optionHandler.getDouble(ROTATION_ANGLE_STRING));
162 final DemoProfitModel profitModel = new DemoProfitModel(rotationAngle, 1.0, 0.5);
163
164 final GreedyMISLabeling labelLayouter = new GreedyMISLabeling();
166 labelLayouter.setOptimizationStrategy(MISLabelingAlgorithm.OPTIMIZATION_BALANCED);
167 labelLayouter.setPlaceEdgeLabels(true);
168 labelLayouter.setSelection(selectionKey);
169 labelLayouter.setPlaceNodeLabels(false);
170 labelLayouter.setProfitModel(profitModel);
171 labelLayouter.setCustomProfitModelRatio(0.1);
172
173 new Graph2DLayoutExecutor().doLayout(view, labelLayouter);
174
175 view.updateView();
176 }
177
178
181 void updateEdgeLabels() {
182 final Graph2D graph = view.getGraph2D();
183 label2Model.clear();
184
185 customLabelConfig.setAutoFlippingEnabled(optionHandler.getBool(AUTO_FLIPPING_STRING));
187
188 final SmartEdgeLabelModel edgeLabelModel = new SmartEdgeLabelModel(); edgeLabelModel.setAutoRotationEnabled(optionHandler.getBool(AUTO_ROTATE_STRING));
191 final CompositeEdgeLabelModel labelingModel = getCurrentEdgeLabelModel();
193 final EdgeLabel defaultLabel = graph.getDefaultEdgeRealizer().getLabel();
195 defaultLabel.setLabelModel(edgeLabelModel, edgeLabelModel.getDefaultParameter());
196
197 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
199 final Edge edge = ec.edge();
200 final EdgeLabelLayout[] labelLayouts = graph.getEdgeLabelLayout(edge);
201 for (int i = 0; i < labelLayouts.length; i++) {
202 final EdgeLabel label = (EdgeLabel) labelLayouts[i];
203 label2Model.put(label, labelingModel);
204
205 final Object parameter = edgeLabelModel.createModelParameter(label.getOrientedBox(),
206 graph.getEdgeLayout(edge), graph.getNodeLayout(edge.source()), graph.getNodeLayout(edge.target())); label.setLabelModel(edgeLabelModel, parameter);
208 }
209 }
210 }
211
212 protected void configureDefaultRealizers() {
213 super.configureDefaultRealizers();
214
215 final YLabel.Factory factory = EdgeLabel.getFactory();
217 final Map defaultConfigImplementationsMap = factory.createDefaultConfigurationMap();
218 customLabelConfig = new DefaultLabelConfiguration();
219 customLabelConfig.setAutoFlippingEnabled(false);
220 defaultConfigImplementationsMap.put(YLabel.Painter.class, customLabelConfig);
221 defaultConfigImplementationsMap.put(YLabel.Layout.class, customLabelConfig);
222 defaultConfigImplementationsMap.put(YLabel.BoundsProvider.class, customLabelConfig);
223 factory.addConfiguration(CUSTOM_LABELING_CONFIG_NAME, defaultConfigImplementationsMap);
224
225 final EdgeLabel label = view.getGraph2D().getDefaultEdgeRealizer().getLabel();
226 label.setConfiguration(CUSTOM_LABELING_CONFIG_NAME);
227 }
228
229
232 private OptionHandler createOptionHandler() {
233 final OptionHandler oh = new OptionHandler("Options");
234 oh.addDouble(ROTATION_ANGLE_STRING, 0.0, 0.0, 360.0);
235 oh.addBool(AUTO_FLIPPING_STRING, true);
236 oh.addEnum(LABELING_MODEL_STRING, EDGE_LABEL_MODELS, 4);
237 oh.addBool(AUTO_ROTATE_STRING, true);
238 oh.addBool(ALLOW_90_DEGREE_DEVIATION_STRING, true);
239 oh.addDouble(EDGE_TO_LABEL_DISTANCE_STRING, 5.0, 1.0, 20.0);
240
241 OptionGroup og = new OptionGroup();
242 og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, PROPERTIES_GROUP);
243 og.addItem(oh.getItem(ROTATION_ANGLE_STRING));
244 og.addItem(oh.getItem(AUTO_FLIPPING_STRING));
245 og.addItem(oh.getItem(EDGE_TO_LABEL_DISTANCE_STRING));
246 og.addItem(oh.getItem(LABELING_MODEL_STRING));
247 og.addItem(oh.getItem(AUTO_ROTATE_STRING));
248 og.addItem(oh.getItem(ALLOW_90_DEGREE_DEVIATION_STRING));
249
250 ConstraintManager cm = new ConstraintManager(oh);
251 final String[] nonCenteredModels = {MODEL_TWO_POS, MODEL_SIX_POS, MODEL_SIDE_SLIDER};
253 cm.setEnabledOnCondition(cm.createConditionValueIs(LABELING_MODEL_STRING, nonCenteredModels),
254 oh.getItem(EDGE_TO_LABEL_DISTANCE_STRING));
255
256 cm.setEnabledOnValueEquals(AUTO_ROTATE_STRING, Boolean.TRUE, ALLOW_90_DEGREE_DEVIATION_STRING);
257
258 oh.addChildPropertyChangeListener(new PropertyChangeListener() {
259 public void propertyChange(PropertyChangeEvent evt) {
260 updateEdgeLabels();
261 doLabelPlacement(null);
262 }
263 });
264
265 return oh;
266 }
267
268
271 private CompositeEdgeLabelModel getCurrentEdgeLabelModel() {
272 final byte modelId = getModel(optionHandler.getEnum(LABELING_MODEL_STRING));
273 final double angle = Math.toRadians(optionHandler.getDouble(ROTATION_ANGLE_STRING));
274
275 final CompositeEdgeLabelModel compositeEdgeLabelModel = new CompositeEdgeLabelModel();
276 compositeEdgeLabelModel.add(getEdgeLabelModel(modelId, optionHandler.getBool(AUTO_ROTATE_STRING),
277 optionHandler.getDouble(EDGE_TO_LABEL_DISTANCE_STRING), angle));
278
279 if (optionHandler.getBool(ALLOW_90_DEGREE_DEVIATION_STRING)) {
280 final double rotatedAngle = (angle + Math.PI * 0.5) % (2.0 * Math.PI);
282 compositeEdgeLabelModel.add(getEdgeLabelModel(modelId, optionHandler.getBool(AUTO_ROTATE_STRING),
283 optionHandler.getDouble(EDGE_TO_LABEL_DISTANCE_STRING), rotatedAngle));
284 }
285
286 return compositeEdgeLabelModel;
287 }
288
289
292 private static byte getModel(int index) {
293 if (index < 0 || index >= EDGE_LABEL_MODELS.length) {
294 return EdgeLabel.SIDE_SLIDER;
295 }
296
297 final String modelString = EDGE_LABEL_MODELS[index];
298 if (MODEL_CENTERED.equals(modelString)) {
299 return EdgeLabel.CENTERED;
300 } else if (MODEL_TWO_POS.equals(modelString)) {
301 return EdgeLabel.TWO_POS;
302 } else if (MODEL_SIX_POS.equals(modelString)) {
303 return EdgeLabel.SIX_POS;
304 } else if (MODEL_THREE_POS_CENTER.equals(modelString)) {
305 return EdgeLabel.THREE_CENTER;
306 } else if (MODEL_CENTER_SLIDER.equals(modelString)) {
307 return EdgeLabel.CENTER_SLIDER;
308 } else {
309 return EdgeLabel.SIDE_SLIDER;
310 }
311 }
312
313
316 private static EdgeLabelModel getEdgeLabelModel(byte modelId, boolean autoRotationEnabled, double distance,
317 double angle) {
318 if (modelId == EdgeLabel.CENTER_SLIDER || modelId == EdgeLabel.SIDE_SLIDER) {
319 final byte mode = (modelId == EdgeLabel.CENTER_SLIDER) ?
320 RotatedSliderEdgeLabelModel.CENTER_SLIDER :
321 RotatedSliderEdgeLabelModel.SIDE_SLIDER;
322 RotatedSliderEdgeLabelModel elm = new RotatedSliderEdgeLabelModel(mode);
323 elm.setAutoRotationEnabled(autoRotationEnabled);
324 if (distance < 1.0 && modelId == EdgeLabel.SIDE_SLIDER) {
325 elm.setDistance(1.0); } else {
327 elm.setDistance(distance);
328 }
329 elm.setAngle(angle);
330 elm.setDistanceRelativeToEdge(true);
331 return elm;
332 } else {
333 final int mode;
334 if (modelId == EdgeLabel.TWO_POS) {
335 mode = RotatedDiscreteEdgeLabelModel.TWO_POS;
336 } else if (modelId == EdgeLabel.CENTERED) {
337 mode = RotatedDiscreteEdgeLabelModel.CENTERED;
338 } else if (modelId == EdgeLabel.THREE_CENTER) {
339 mode = RotatedDiscreteEdgeLabelModel.THREE_CENTER;
340 } else {
341 mode = RotatedDiscreteEdgeLabelModel.SIX_POS; }
343 RotatedDiscreteEdgeLabelModel elm = new RotatedDiscreteEdgeLabelModel(mode);
344 elm.setAutoRotationEnabled(autoRotationEnabled);
345 elm.setAngle(angle);
346 elm.setDistance(distance);
347 elm.setPositionRelativeToSegment(true);
348 return elm;
349 }
350 }
351
352
355 private JPanel createToolsPanel(String helpFilePath) {
356 JPanel toolsPanel = new JPanel(new BorderLayout());
357 toolsPanel.add(createOptionHandlerComponent(optionHandler), BorderLayout.NORTH);
358
359 if (helpFilePath != null) {
360 final URL url = getResource(helpFilePath);
361 if (url == null) {
362 System.err.println("Could not locate help file: " + helpFilePath);
363 } else {
364 JComponent helpPane = createHelpPane(url);
365 if (helpPane != null) {
366 helpPane.setMinimumSize(new Dimension(200, 200));
367 helpPane.setPreferredSize(new Dimension(TOOLS_PANEL_WIDTH, 400));
368 toolsPanel.add(helpPane, BorderLayout.CENTER);
369 }
370 }
371 }
372
373 return toolsPanel;
374 }
375
376
379 protected JMenuBar createMenuBar() {
380 JMenuBar menuBar = new JMenuBar();
381
382 JMenu fileMenu = new JMenu("File");
383 menuBar.add(fileMenu);
384 fileMenu.add(new ExitAction());
385
386 createExamplesMenu(menuBar);
387
388 return menuBar;
389 }
390
391 protected String[] getExampleResources() {
392 return new String[]{
393 "resource/orthogonal.graphml",
394 "resource/organic.graphml"
395 };
396 }
397
398
401 protected void registerViewModes() {
402 EditMode mode = new EditMode();
403 mode.setPopupMode(new DemoPopupMode());
404 view.addViewMode(mode);
405 }
406
407
411 protected void registerViewActions() {
412 super.registerViewActions();
413 view.getCanvasComponent().getActionMap().remove(Graph2DViewActions.EDIT_LABEL);
414 }
415
416
419 protected JToolBar createToolBar() {
420 final JToolBar bar = super.createToolBar();
421 bar.addSeparator();
422
423 bar.add(createActionControl(new AbstractAction(
425 "Place Labels", SHARED_LAYOUT_ICON) {
426 public void actionPerformed(ActionEvent e) {
427 doLabelPlacement(null);
428 }
429 }));
430
431 return bar;
432 }
433
434
437 protected void loadGraph(URL resource) {
438 super.loadGraph(resource);
439
440 DemoDefaults.applyRealizerDefaults(view.getGraph2D());
441 final Graph2D graph = view.getGraph2D();
442 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
443 final EdgeLabelLayout[] labelLayouts = graph.getEdgeLabelLayout(ec.edge());
444 for (int i = 0; i < labelLayouts.length; i++) {
445 final EdgeLabel label = (EdgeLabel) labelLayouts[i];
446 label.setConfiguration(CUSTOM_LABELING_CONFIG_NAME);
447 }
448 }
449
450 updateEdgeLabels();
451 doLabelPlacement(null);
452 }
453
454 class DemoPopupMode extends PopupMode {
455
456 public JPopupMenu getEdgePopup(final Edge edge) {
457 JPopupMenu pm = new JPopupMenu();
458 pm.add(new AbstractAction("Add Label") {
459
460 public void actionPerformed(ActionEvent e) {
461 if (edge == null) {
462 return;
463 }
464
465 final SmartEdgeLabelModel edgeLabelModel = new SmartEdgeLabelModel(); edgeLabelModel.setAutoRotationEnabled(optionHandler.getBool(AUTO_ROTATE_STRING));
467 final EdgeLabel label = new EdgeLabel("Label");
468 label2Model.put(label, getCurrentEdgeLabelModel()); label.setConfiguration(CUSTOM_LABELING_CONFIG_NAME);
470 label.setLineColor(LABEL_LINE_COLOR);
471 label.setBackgroundColor(LABEL_BACKGROUND_COLOR);
472 label.setLabelModel(edgeLabelModel, edgeLabelModel.getDefaultParameter());
473 view.getGraph2D().getRealizer(edge).addLabel(label);
474
475 final Graph graph = edge.getGraph();
477 if (graph != null) {
478 graph.addDataProvider("SELECTED_LABELS", new DataProviderAdapter() {
479 public boolean getBool(Object dataHolder) {
480 return label == dataHolder;
481 }
482 });
483 doLabelPlacement("SELECTED_LABELS");
484 graph.removeDataProvider("SELECTED_LABELS");
485 }
486
487 view.getGraph2D().updateViews();
488 }
489 });
490 return pm;
491 }
492
493 public JPopupMenu getEdgeLabelPopup(final EdgeLabel label) {
494 JPopupMenu pm = new JPopupMenu();
495 pm.add(new AbstractAction("Edit Label") {
496
497 public void actionPerformed(ActionEvent e) {
498 if (label != null) {
499 EdgeLabelPropertyHandler ph = new EdgeLabelPropertyHandler(label, view);
500 ph.showEditor(null, OptionHandler.OK_APPLY_CANCEL_BUTTONS);
501 }
502 }
503 });
504 return pm;
505 }
506 }
507
508
512 private static JComponent createOptionHandlerComponent(OptionHandler oh) {
513 final EditorFactory defaultEditorFactory = new DefaultEditorFactory();
514 final Editor editor = defaultEditorFactory.createEditor(oh);
515
516 final List stack = new ArrayList();
518 stack.add(editor);
519 while (!stack.isEmpty()) {
520 Object editorObj = stack.remove(stack.size() - 1);
521 if (editorObj instanceof ItemEditor) {
522 ((ItemEditor) editorObj).setAutoAdopt(true);
523 ((ItemEditor) editorObj).setAutoCommit(true);
524 }
525 if (editorObj instanceof CompoundEditor) {
526 for (Iterator iter = ((CompoundEditor) editorObj).editors(); iter.hasNext();) {
527 stack.add(iter.next());
528 }
529 }
530 }
531
532 JComponent optionComponent = editor.getComponent();
534 optionComponent.setMinimumSize(new Dimension(200, 50));
535 return optionComponent;
536 }
537
538 public static void main(String[] args) {
539 EventQueue.invokeLater(new Runnable() {
540 public void run() {
541 Locale.setDefault(Locale.ENGLISH);
542 initLnF();
543 (new EdgeLabelingDemo("resource/edgelabelingdemohelp.html")).start("Edge Labeling Demo");
544 }
545 });
546 }
547 }
548