1
28 package demo.layout.module;
29
30 import y.module.LayoutModule;
31 import y.module.YModule;
32
33 import y.base.DataProvider;
34 import y.base.Edge;
35 import y.base.EdgeCursor;
36 import y.geom.OrientedRectangle;
37 import y.layout.EdgeLabelModel;
38 import y.layout.LabelRanking;
39 import y.layout.RotatedDiscreteEdgeLabelModel;
40 import y.layout.RotatedSliderEdgeLabelModel;
41 import y.layout.labeling.GreedyMISLabeling;
42 import y.layout.labeling.MISLabelingAlgorithm;
43 import y.layout.labeling.SALabeling;
44 import y.option.OptionHandler;
45 import y.option.ConstraintManager;
46 import y.option.ConstraintManager.Condition;
47 import y.option.EnumOptionItem;
48 import y.util.DataProviderAdapter;
49 import y.view.EdgeLabel;
50 import y.view.EdgeRealizer;
51 import y.view.Graph2D;
52 import y.view.NodeLabel;
53 import y.view.YLabel;
54
55
60 public class LabelingModule extends LayoutModule {
61
62 protected static final String MODULE_DIVERSE_LABELING = "DIVERSE_LABELING";
64
65 protected static final String SECTION_SCOPE = "SCOPE";
67 protected static final String ITEM_PLACE_NODE_LABELS = "PLACE_NODE_LABELS";
69 protected static final String ITEM_PLACE_EDGE_LABELS = "PLACE_EDGE_LABELS";
70 protected static final String ITEM_CONSIDER_SELECTED_FEATURES_ONLY = "CONSIDER_SELECTED_FEATURES_ONLY";
71 protected static final String ITEM_CONSIDER_INVISIBLE_LABELS = "CONSIDER_INVISIBLE_LABELS";
72
73 protected static final String SECTION_QUALITY = "QUALITY";
75 protected static final String ITEM_USE_OPTIMIZATION = "USE_OPTIMIZATION";
77 protected static final String ITEM_OPTIMIZATION_STRATEGY = "OPTIMIZATION_STRATEGY";
78 protected static final String VALUE_OPTIMIZATION_BALANCED = "OPTIMIZATION_BALANCED";
79 protected static final String VALUE_OPTIMIZATION_NONE = "OPTIMIZATION_NONE";
80 protected static final String VALUE_OPTIMIZATION_EDGE_OVERLAP = "OPTIMIZATION_EDGE_OVERLAP";
81 protected static final String VALUE_OPTIMIZATION_LABEL_OVERLAP = "OPTIMIZATION_LABEL_OVERLAP";
82 protected static final String VALUE_OPTIMIZATION_NODE_OVERLAP = "OPTIMIZATION_NODE_OVERLAP";
83 protected static final String ITEM_ALLOW_NODE_OVERLAPS = "ALLOW_NODE_OVERLAPS";
84 protected static final String ITEM_ALLOW_EDGE_OVERLAPS = "ALLOW_EDGE_OVERLAPS";
85 protected static final String ITEM_USE_POSTPROCESSING = "USE_POSTPROCESSING";
86
87 protected static final String SECTION_MODEL = "MODEL";
89 protected static final String ITEM_EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
91 protected static final String VALUE_CENTERED = "CENTERED";
92 protected static final String VALUE_TWO_POS = "TWO_POS";
93 protected static final String VALUE_SIX_POS = "SIX_POS";
94 protected static final String VALUE_THREE_CENTER = "THREE_CENTER";
95 protected static final String VALUE_FREE = "FREE";
96 protected static final String VALUE_CENTER_SLIDER = "CENTER_SLIDER";
97 protected static final String VALUE_SIDE_SLIDER = "SIDE_SLIDER";
98 protected static final String VALUE_AS_IS = "AS_IS";
99 protected static final String VALUE_BEST = "BEST";
100 protected static final String ITEM_AUTO_ROTATE = "AUTO_ROTATE";
101
102 protected static final String LABEL_SELECTION_DP_KEY = "LABEL_SELECTION";
104
105
108 public LabelingModule() {
109 super(MODULE_DIVERSE_LABELING);
110 }
111
112
116 protected OptionHandler createOptionHandler() {
117 final OptionHandler options = new OptionHandler(getModuleName());
118 final ConstraintManager optionConstraint = new ConstraintManager(options);
119
120 options.useSection(SECTION_SCOPE);
122 options.addBool(ITEM_PLACE_NODE_LABELS, true);
124 options.addBool(ITEM_PLACE_EDGE_LABELS, true);
125 options.addBool(ITEM_CONSIDER_SELECTED_FEATURES_ONLY, false);
126 options.addBool(ITEM_CONSIDER_INVISIBLE_LABELS, false);
127
128 options.useSection(SECTION_QUALITY);
130 options.addBool(ITEM_USE_OPTIMIZATION, false);
132 options.addEnum(ITEM_OPTIMIZATION_STRATEGY, new String[]{
133 VALUE_OPTIMIZATION_BALANCED,
134 VALUE_OPTIMIZATION_NONE,
135 VALUE_OPTIMIZATION_EDGE_OVERLAP,
136 VALUE_OPTIMIZATION_LABEL_OVERLAP,
137 VALUE_OPTIMIZATION_NODE_OVERLAP
138 }, 0);
139 options.addBool(ITEM_ALLOW_NODE_OVERLAPS, false);
140 options.addBool(ITEM_ALLOW_EDGE_OVERLAPS, true);
141 options.addBool(ITEM_USE_POSTPROCESSING, false);
142
143 options.useSection(SECTION_MODEL);
145 final EnumOptionItem itemEdgeLabelModel = options.addEnum(ITEM_EDGE_LABEL_MODEL, new String[]{
147 VALUE_CENTERED,
148 VALUE_TWO_POS,
149 VALUE_SIX_POS,
150 VALUE_THREE_CENTER,
151 VALUE_FREE,
152 VALUE_CENTER_SLIDER,
153 VALUE_SIDE_SLIDER,
154 VALUE_AS_IS,
155 VALUE_BEST
156 }, 8);
157 options.addBool(ITEM_AUTO_ROTATE, false);
158 final Condition condition =
161 optionConstraint.createConditionValueIs(itemEdgeLabelModel, new String[]{
162 VALUE_AS_IS,
163 VALUE_BEST,
164 VALUE_FREE
165 });
166 optionConstraint.setEnabledOnCondition(condition.inverse(), options.getItem(ITEM_AUTO_ROTATE));
167
168 return options;
169 }
170
171
175 private static byte translateOptimizationStrategy(final String optimizationStrategy) {
176 if (VALUE_OPTIMIZATION_LABEL_OVERLAP.equals(optimizationStrategy)) {
177 return MISLabelingAlgorithm.OPTIMIZATION_LABEL_OVERLAP;
178 } else if (VALUE_OPTIMIZATION_BALANCED.equals(optimizationStrategy)) {
179 return MISLabelingAlgorithm.OPTIMIZATION_BALANCED;
180 } else if (VALUE_OPTIMIZATION_EDGE_OVERLAP.equals(optimizationStrategy)) {
181 return MISLabelingAlgorithm.OPTIMIZATION_EDGE_OVERLAP;
182 } else if (VALUE_OPTIMIZATION_NODE_OVERLAP.equals(optimizationStrategy)) {
183 return MISLabelingAlgorithm.OPTIMIZATION_NODE_OVERLAP;
184 } else {
185 return MISLabelingAlgorithm.OPTIMIZATION_NONE;
186 }
187 }
188
189
193 protected void mainrun() {
194 final OptionHandler options = getOptionHandler();
195 final MISLabelingAlgorithm al = options.getBool(ITEM_USE_OPTIMIZATION)
196 ? (MISLabelingAlgorithm) new SALabeling()
197 : new GreedyMISLabeling();
198
199 configure(al, options);
200
201 final Graph2D graph = getGraph2D();
202 prepareGraph(graph, options);
203 try {
204 launchLayouter(al);
205 } finally {
206 restoreGraph(graph, options);
207 }
208 }
209
210
220 protected void prepareGraph(final Graph2D graph, final OptionHandler options) {
221 final DataProvider labelSet = new LabelSetDP(
222 graph,
223 options.getBool(ITEM_CONSIDER_SELECTED_FEATURES_ONLY),
224 options.getBool(ITEM_PLACE_NODE_LABELS),
225 options.getBool(ITEM_PLACE_EDGE_LABELS),
226 options.getBool(ITEM_CONSIDER_INVISIBLE_LABELS));
227 graph.addDataProvider(LABEL_SELECTION_DP_KEY, labelSet);
228
229 setupEdgeLabelModels(graph, options.getString(ITEM_EDGE_LABEL_MODEL), labelSet, options.getBool(ITEM_AUTO_ROTATE));
230 }
231
232
238 protected void restoreGraph(final Graph2D graph, final OptionHandler options) {
239 graph.removeDataProvider(LABEL_SELECTION_DP_KEY);
240 }
241
242
247 protected void configure(final MISLabelingAlgorithm al, final OptionHandler options) {
248 al.setAutoFlippingEnabled(true);
249 al.setOptimizationStrategy(translateOptimizationStrategy(options.getString(ITEM_OPTIMIZATION_STRATEGY)));
250 if (al.getOptimizationStrategy() == MISLabelingAlgorithm.OPTIMIZATION_NONE) {
251 al.setProfitModel(new LabelRanking());
252 }
253 al.setRemoveNodeOverlaps(!options.getBool(ITEM_ALLOW_NODE_OVERLAPS));
254 al.setRemoveEdgeOverlaps(!options.getBool(ITEM_ALLOW_EDGE_OVERLAPS));
255 al.setApplyPostprocessing(options.getBool(ITEM_USE_POSTPROCESSING));
256
257 al.setSelection(LABEL_SELECTION_DP_KEY);
258 }
259
260 private static void setupEdgeLabelModels(
261 final Graph2D graph,
262 final String modelValue, final DataProvider labelFilter, final boolean autoRotate
263 ) {
264 if (VALUE_AS_IS.equals(modelValue)) {
265 return;
266 }
267
268 final byte model;
269 if (VALUE_CENTERED.equals(modelValue)) {
270 model = EdgeLabel.CENTERED;
271 } else if (VALUE_TWO_POS.equals(modelValue)) {
272 model = EdgeLabel.TWO_POS;
273 } else if (VALUE_SIX_POS.equals(modelValue)) {
274 model = EdgeLabel.SIX_POS;
275 } else if (VALUE_THREE_CENTER.equals(modelValue)) {
276 model = EdgeLabel.THREE_CENTER;
277 } else if (VALUE_CENTER_SLIDER.equals(modelValue)) {
278 model = EdgeLabel.CENTER_SLIDER;
279 } else if (VALUE_SIDE_SLIDER.equals(modelValue)) {
280 model = EdgeLabel.SIDE_SLIDER;
281 } else {
282 model = EdgeLabel.FREE;
284 }
285
286 EdgeLabelModel labelModel = null;
287 if (autoRotate) {
288 labelModel = getEdgeLabelModel(model);
289 }
290
291 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
292 final Edge e = ec.edge();
293 EdgeRealizer er = graph.getRealizer(e);
294 for (int i = 0; i < er.labelCount(); i++) {
295 final EdgeLabel label = er.getLabel(i);
296 if (labelFilter.getBool(label)) {
297 final OrientedRectangle lb = label.getOrientedBox();
298 if (labelModel != null) {
299 label.setLabelModel(labelModel, labelModel.getDefaultParameter());
300 label.setModelParameter(label.getBestModelParameterForBounds(lb));
301 } else {
302 label.setModel(model);
303 label.setModelParameter(label.getBestModelParameterForBounds(lb));
304 }
305 }
306 }
307 }
308 }
309
310 private static EdgeLabelModel getEdgeLabelModel(byte modelValue) {
311 final EdgeLabelModel labelModel;
312 if (EdgeLabel.CENTERED == modelValue) {
313 labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.CENTERED);
314 ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
315 } else if (EdgeLabel.TWO_POS == modelValue) {
316 labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.TWO_POS);
317 ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
318 } else if (EdgeLabel.SIX_POS == modelValue) {
319 labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.SIX_POS);
320 ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
321 } else if (EdgeLabel.THREE_CENTER == modelValue) {
322 labelModel = new RotatedDiscreteEdgeLabelModel(RotatedDiscreteEdgeLabelModel.THREE_CENTER);
323 ((RotatedDiscreteEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
324 } else if (EdgeLabel.CENTER_SLIDER == modelValue) {
325 labelModel = new RotatedSliderEdgeLabelModel(RotatedSliderEdgeLabelModel.CENTER_SLIDER);
326 ((RotatedSliderEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
327 } else if (EdgeLabel.SIDE_SLIDER == modelValue) {
328 labelModel = new RotatedSliderEdgeLabelModel(RotatedSliderEdgeLabelModel.SIDE_SLIDER);
329 ((RotatedSliderEdgeLabelModel) labelModel).setAutoRotationEnabled(true);
330 } else {
331 labelModel = null;
332 }
333 return labelModel;
334 }
335
336
339 static class LabelSetDP extends DataProviderAdapter {
340 private final boolean considerOnlySelected;
341 private final Graph2D graph;
342 private final boolean nodes;
343 private final boolean edges;
344 private final boolean invisible;
345
346 LabelSetDP(Graph2D g,boolean sel,boolean n,boolean e,boolean uv) {
347 considerOnlySelected = sel;
348 graph = g;
349 nodes = n;
350 edges = e;
351 invisible = uv;
352 }
353
354 public boolean getBool(Object o) {
355 YLabel ylabel = (YLabel) o;
356 if (!ylabel.isVisible() && !invisible) {
357 return false;
358 }
359 if (o instanceof NodeLabel) {
360 final NodeLabel l = (NodeLabel) o;
361 if (l.getModel() == NodeLabel.INTERNAL) {
362 return false;
363 }
364 }
365 if (considerOnlySelected) {
366 if ((o instanceof NodeLabel) && nodes) {
367 final NodeLabel l = (NodeLabel) o;
368 return graph.isSelected(l.getNode());
369 }
370 if ((o instanceof EdgeLabel) && edges) {
371 final EdgeLabel l = (EdgeLabel) o;
372 return graph.isSelected(l.getEdge());
373 }
374 return false;
375 } else {
376 if ((o instanceof NodeLabel) && nodes) {
377 return true;
378 }
379 return (o instanceof EdgeLabel) && edges;
380 }
381 }
382 }
383 }
384