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.module;
15  
16  import y.module.LayoutModule;
17  import y.module.YModule;
18  
19  import y.base.DataProvider;
20  import y.base.Edge;
21  import y.base.EdgeCursor;
22  
23  import y.layout.LabelRanking;
24  import y.layout.EdgeLabelModel;
25  import y.layout.RotatedDiscreteEdgeLabelModel;
26  import y.layout.RotatedSliderEdgeLabelModel;
27  import y.layout.labeling.GreedyMISLabeling;
28  import y.layout.labeling.MISLabelingAlgorithm;
29  import y.layout.labeling.SALabeling;
30  import y.option.MappedListCellRenderer;
31  import y.option.OptionHandler;
32  import y.option.ConstraintManager;
33  import y.util.DataProviderAdapter;
34  import y.view.EdgeLabel;
35  import y.view.EdgeRealizer;
36  import y.view.Graph2D;
37  import y.view.Graph2DView;
38  import y.view.NodeLabel;
39  import y.view.YLabel;
40  import y.view.Graph2DLayoutExecutor;
41  import java.util.Map;
42  
43  /**
44   * This module represents an interactive configurator and launcher for the
45   * yFiles labeling algorithms.
46   *
47   */
48  public class LabelingModule extends YModule {
49  
50    private static final String ALLOW_NODE_OVERLAPS = "ALLOW_NODE_OVERLAPS";
51    private static final String INPUT = "INPUT";
52    private static final String CONSIDER_INVISIBLE_LABELS = "CONSIDER_INVISIBLE_LABELS";
53    private static final String ALLOW_EDGE_OVERLAPS = "ALLOW_EDGE_OVERLAPS";
54    private static final String DIVERSE_LABELING = "DIVERSE_LABELING";
55    private static final String QUALITY = "QUALITY";
56    private static final String USE_OPTIMIZATION = "USE_OPTIMIZATION";
57    private static final String USE_POSTPROCESSING = "USE_POSTPROCESSING";
58    private static final String CONSIDER_SELECTED_FEATURES_ONLY = "CONSIDER_SELECTED_FEATURES_ONLY";
59    private static final String SCOPE = "SCOPE";
60    private static final String PLACE_EDGE_LABELS = "PLACE_EDGE_LABELS";
61    private static final String MODEL = "MODEL";
62    private static final String EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
63    private static final String AUTO_ROTATE = "AUTO_ROTATE";
64    private static final String UNKNOWN_MODEL_VALUE = "UNKNOWN_MODEL_VALUE";
65    private static final String PLACE_NODE_LABELS = "PLACE_NODE_LABELS";
66    private static final String OPTIMIZATION_BALANCED = "OPTIMIZATION_BALANCED";
67    private static final String OPTIMIZATION_NODE_OVERLAP = "OPTIMIZATION_NODE_OVERLAP";
68    private static final String OPTIMIZATION_LABEL_OVERLAP = "OPTIMIZATION_LABEL_OVERLAP";
69    private static final String OPTIMIZATION_EDGE_OVERLAP = "OPTIMIZATION_EDGE_OVERLAP";
70    private static final String OPTIMIZATION_NONE = "OPTIMIZATION_NONE";
71    private static final String OPTIMIZATION_STRATEGY = "OPTIMIZATION_STRATEGY";
72  
73    // the following are not keys for i18n
74    private static final String AS_IS = "As Is";
75    private static final String BEST = "Best";
76  
77    private static final String[] optimizationStrategy = {
78        OPTIMIZATION_BALANCED, OPTIMIZATION_NONE, OPTIMIZATION_EDGE_OVERLAP, OPTIMIZATION_LABEL_OVERLAP,
79        OPTIMIZATION_NODE_OVERLAP
80    };
81  
82    public LabelingModule() {
83      super(DIVERSE_LABELING, "yFiles Layout Team", "Places Labels");
84    }
85  
86    /** Creates an option handler for this layouter */
87    protected OptionHandler createOptionHandler() {
88      OptionHandler op = new OptionHandler(getModuleName());
89      op.useSection(SCOPE);
90      op.addBool(PLACE_NODE_LABELS, true);
91      op.addBool(PLACE_EDGE_LABELS, true);
92      op.addBool(CONSIDER_SELECTED_FEATURES_ONLY, false);
93      op.addBool(CONSIDER_INVISIBLE_LABELS, false);
94      op.useSection(QUALITY);
95      op.addBool(USE_OPTIMIZATION, false);
96      op.addEnum(OPTIMIZATION_STRATEGY, optimizationStrategy, 0);
97      op.addBool(ALLOW_NODE_OVERLAPS, false);
98      op.addBool(ALLOW_EDGE_OVERLAPS, true);
99      op.addBool(USE_POSTPROCESSING, false);
100 
101     op.useSection(MODEL);
102     Map map = EdgeLabel.modelToStringMap();
103     Object asIs = AS_IS;
104     map.put(asIs, asIs);
105     Object best = BEST;
106     map.put(best, best);    
107     op.addEnum(EDGE_LABEL_MODEL, map.keySet().toArray(), best, new MappedListCellRenderer(map));
108     op.addBool(AUTO_ROTATE, false);
109 
110     // enable the auto rotate item for applicably label models only
111     final ConstraintManager cm = new ConstraintManager(op);
112     final Object[] nonAutoRotatableModels = {AS_IS, BEST, new Byte(EdgeLabel.FREE)};
113     final ConstraintManager.Condition condition = cm.createConditionValueIs(EDGE_LABEL_MODEL, nonAutoRotatableModels);    
114     cm.setEnabledOnCondition(condition.inverse(), op.getItem(AUTO_ROTATE));
115 
116     return op;
117   }
118 
119   protected void init() {
120     final OptionHandler op = getOptionHandler();
121     DataProvider labelSet = new LabelSetDP(
122         getGraph2D(),
123         op.getBool(CONSIDER_SELECTED_FEATURES_ONLY),
124         op.getBool(PLACE_NODE_LABELS),
125         op.getBool(PLACE_EDGE_LABELS),
126         op.getBool(CONSIDER_INVISIBLE_LABELS));
127     getGraph2D().addDataProvider(INPUT, labelSet);
128 
129     setupEdgeLabelModels(op.get(EDGE_LABEL_MODEL), labelSet, op.getBool(AUTO_ROTATE));
130   }
131 
132   protected void mainrun() {
133     final OptionHandler op = getOptionHandler();
134     final MISLabelingAlgorithm al = op.getBool(USE_OPTIMIZATION) ?
135         (MISLabelingAlgorithm) new SALabeling() :
136         new GreedyMISLabeling();
137 
138     al.setOptimizationStrategy((byte) op.getEnum(OPTIMIZATION_STRATEGY));
139     if (al.getOptimizationStrategy() == MISLabelingAlgorithm.OPTIMIZATION_NONE) {
140       al.setProfitModel(new LabelRanking());
141     }
142     al.setRemoveNodeOverlaps(!op.getBool(ALLOW_NODE_OVERLAPS));
143     al.setRemoveEdgeOverlaps(!op.getBool(ALLOW_EDGE_OVERLAPS));
144     al.setApplyPostprocessing(op.getBool(USE_POSTPROCESSING));
145 
146     al.setSelection(INPUT);
147 
148     final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.UNBUFFERED);
149     final Graph2DView view = getGraph2DView();
150     if (view == null) {
151       layoutExecutor.doLayout(getGraph2D(), al);
152     } else {
153       layoutExecutor.doLayout(view, al);
154     }
155 
156     getGraph2D().removeDataProvider(INPUT);
157     getGraph2D().updateViews();
158   }
159 
160   static EdgeLabelModel getEdgeLabelModel(byte modelValue) {
161     final EdgeLabelModel labelModel;
162     if(EdgeLabel.CENTERED == modelValue) {
163       labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.CENTERED);
164       ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
165     } else if(EdgeLabel.TWO_POS == modelValue) {
166       labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.TWO_POS);
167       ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
168     } else if(EdgeLabel.SIX_POS == modelValue) {
169       labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.SIX_POS);
170       ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
171     } else if(EdgeLabel.THREE_CENTER == modelValue) {
172       labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.THREE_CENTER);
173       ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
174     } else if(EdgeLabel.CENTER_SLIDER == modelValue) {
175       labelModel = new RotatedSliderEdgeLabelModel(RotatedSliderEdgeLabelModel.CENTER_SLIDER);
176       ((RotatedSliderEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
177     } else if(EdgeLabel.SIDE_SLIDER == modelValue) {
178       labelModel = new RotatedSliderEdgeLabelModel(RotatedSliderEdgeLabelModel.SIDE_SLIDER);
179       ((RotatedSliderEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
180     } else {
181       labelModel = null;
182     }
183     return labelModel;
184   }
185 
186   /**
187    * Selects the labels we want to set.
188    */
189   static class LabelSetDP extends DataProviderAdapter
190   {
191     private final boolean considerOnlySelected;
192     private final Graph2D graph;
193     private final boolean nodes;
194     private final boolean edges;
195     private final boolean invisible;
196 
197     LabelSetDP(Graph2D g,boolean sel,boolean n,boolean e,boolean uv)
198     {
199       considerOnlySelected = sel;
200       graph = g;
201       nodes = n;
202       edges = e;
203       invisible = uv;
204     }
205 
206     public boolean getBool(Object o)
207     {
208       YLabel ylabel = (YLabel) o;
209       if (!ylabel.isVisible() && !invisible) {
210         return false;
211       }
212       if (o instanceof NodeLabel) {
213         NodeLabel l = (NodeLabel) o;
214         if (l.getModel() == NodeLabel.INTERNAL) return false;
215       }
216       if (considerOnlySelected)
217       {
218         if ((o instanceof NodeLabel) && nodes)
219         {
220           NodeLabel l = (NodeLabel) o;
221           if (graph.isSelected(l.getNode())) {
222             return true;
223           } else {
224             return false;
225           }
226         }
227         if ((o instanceof EdgeLabel) && edges) {
228           EdgeLabel l = (EdgeLabel) o;
229           if (graph.isSelected(l.getEdge())) {
230             return true;
231           } else {
232             return false;
233           }
234         }
235         return false;
236       } else {
237         if ((o instanceof NodeLabel) && nodes) return true;
238         if ((o instanceof EdgeLabel) && edges) return true;
239         return false;
240       }
241     }
242   }
243 
244   void setupEdgeLabelModels(Object modelValue, DataProvider labelFilter, boolean autoRotate) {
245     if (AS_IS.equals(modelValue)) {
246       return;
247     }
248 
249     final byte model;
250     EdgeLabelModel labelModel = null;
251     if (BEST.equals(modelValue)) {
252       model = EdgeLabel.FREE;
253     } else if (modelValue instanceof Byte) {
254       model = ((Byte) modelValue).byteValue();
255       if (autoRotate) {
256         labelModel = getEdgeLabelModel(model);
257       }
258     } else {
259       throw new IllegalArgumentException(UNKNOWN_MODEL_VALUE + modelValue);
260     }
261 
262     final Graph2D graph = getGraph2D();
263     for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
264       Edge e = ec.edge();
265       EdgeRealizer er = graph.getRealizer(e);
266       for (int i = 0; i < er.labelCount(); i++) {
267         EdgeLabel label = er.getLabel(i);
268         if (labelFilter.getBool(label)) {
269           if (labelModel != null) {
270             label.setLabelModel(labelModel);
271           } else {
272             label.setModel(model);
273           }
274           label.setModelParameter(label.getLabelModel().getDefaultParameter());
275         }
276       }
277     }
278   }
279 }
280 
281