1
14 package demo.view.flowchart;
15
16 import demo.view.flowchart.layout.FlowchartLayouter;
17 import demo.view.flowchart.painters.FlowchartLayoutConfigurator;
18 import y.base.Edge;
19 import y.base.EdgeCursor;
20 import y.base.EdgeMap;
21 import y.base.Node;
22 import y.base.NodeCursor;
23 import y.layout.LayoutOrientation;
24 import y.layout.PortConstraintKeys;
25 import y.module.LayoutModule;
26 import y.option.OptionGroup;
27 import y.option.OptionHandler;
28 import y.util.Maps;
29 import y.view.Graph2D;
30 import y.view.Graph2DLayoutExecutor;
31 import y.view.tabular.TableLayoutConfigurator;
32
33
36 public class FlowchartLayoutModule extends LayoutModule {
37 private static final String FLOWCHART_LAYOUT = "FLOWCHART_LAYOUT";
38 private static final String GROUP_NEGATIVE_BRANCH = "GROUP_NEGATIVE_BRANCH";
39 private static final String GROUP_POSITIVE_BRANCH = "GROUP_POSITIVE_BRANCH";
40
41 private static final String ALLOW_FLATWISE_EDGES = "ALLOW_FLATWISE_EDGES";
42 private static final String LANE_INSETS = "LANE_INSETS";
43 private static final String MINIMUM_EDGE_DISTANCE = "MINIMUM_EDGE_DISTANCE";
44 private static final String MINIMUM_NODE_DISTANCE = "MINIMUM_NODE_DISTANCE";
45 private static final String MINIMUM_POOL_DISTANCE = "MINIMUM_POOL_DISTANCE";
46
47 private static final String IN_EDGE_GROUPING = "IN_EDGE_GROUPING";
48 private static final String GROUPING_ALL = "GROUPING_ALL";
49 private static final String GROUPING_OPTIMIZED = "GROUPING_OPTIMIZED";
50 private static final String GROUPING_NONE = "GROUPING_NONE";
51
52 private static final String ORIENTATION = "ORIENTATION";
53 private static final String LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
54 private static final String TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
55
56 private static final String NEGATIVE_BRANCH_LABEL = "NEGATIVE_BRANCH_LABEL";
57 private static final String NEGATIVE_BRANCH_DEFAULT = "No";
58 private static final String NEGATIVE_BRANCH_DIRECTION = "NEGATIVE_BRANCH_DIRECTION";
59
60 private static final String POSITIVE_BRANCH_LABEL = "POSITIVE_BRANCH_LABEL";
61 private static final String POSITIVE_BRANCH_DEFAULT = "Yes";
62 private static final String POSITIVE_BRANCH_DIRECTION = "POSITIVE_BRANCH_DIRECTION";
63
64 private static final String DIRECTION_WITH_THE_FLOW = "DIRECTION_WITH_THE_FLOW";
65 private static final String DIRECTION_FLATWISE = "DIRECTION_FLATWISE";
66 private static final String DIRECTION_LEFT_IN_FLOW = "DIRECTION_LEFT_IN_FLOW";
67 private static final String DIRECTION_RIGHT_IN_FLOW = "DIRECTION_RIGHT_IN_FLOW";
68 private static final String DIRECTION_UNDEFINED = "DIRECTION_UNDEFINED";
69
70 private static final String[] DIRECTION_ENUM = {
71 DIRECTION_WITH_THE_FLOW, DIRECTION_FLATWISE,
72 DIRECTION_LEFT_IN_FLOW, DIRECTION_RIGHT_IN_FLOW,
73 DIRECTION_UNDEFINED
74 };
75
76 private static final String[] GROUPING_ENUM = {
77 GROUPING_ALL, GROUPING_OPTIMIZED, GROUPING_NONE
78 };
79
80 private static final String[] ORIENTATION_ENUM = {
81 TOP_TO_BOTTOM, LEFT_TO_RIGHT
82 };
83
84
87 public FlowchartLayoutModule() {
88 super(FLOWCHART_LAYOUT, "yFiles Layout Team", "Flowchart Layout");
89
90 setPortIntersectionCalculatorEnabled(true);
92 }
93
94
95
98 protected OptionHandler createOptionHandler() {
99 final OptionHandler op = new OptionHandler(getModuleName());
100
101 op.addEnum(ORIENTATION, ORIENTATION_ENUM, 0);
102
103 op.addString(POSITIVE_BRANCH_LABEL, POSITIVE_BRANCH_DEFAULT);
104 op.addEnum(POSITIVE_BRANCH_DIRECTION, DIRECTION_ENUM, 0);
105 op.addString(NEGATIVE_BRANCH_LABEL, NEGATIVE_BRANCH_DEFAULT);
106 op.addEnum(NEGATIVE_BRANCH_DIRECTION, DIRECTION_ENUM, 1);
107
108 op.addEnum(IN_EDGE_GROUPING, GROUPING_ENUM, 1);
109 op.addBool(ALLOW_FLATWISE_EDGES, false);
110
111 op.addDouble(MINIMUM_NODE_DISTANCE, 30.0);
112 op.addDouble(MINIMUM_EDGE_DISTANCE, 15.0);
113 op.addDouble(MINIMUM_POOL_DISTANCE, 30.0);
114 op.addDouble(LANE_INSETS, 10.0);
115
116
117 OptionGroup og = new OptionGroup();
118 og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, GROUP_POSITIVE_BRANCH);
119 og.addItem(op.getItem(POSITIVE_BRANCH_LABEL));
120 og.addItem(op.getItem(POSITIVE_BRANCH_DIRECTION));
121
122 og = new OptionGroup();
123 og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, GROUP_NEGATIVE_BRANCH);
124 og.addItem(op.getItem(NEGATIVE_BRANCH_LABEL));
125 og.addItem(op.getItem(NEGATIVE_BRANCH_DIRECTION));
126
127 adoptSettings(op, new FlowchartLayoutConfigurator());
129 adoptSettings(op, new FlowchartLayouter());
131
132 return op;
133 }
134
135
138 protected void mainrun() {
139 final OptionHandler op = getOptionHandler();
140
141 final FlowchartLayouter layouter = new FlowchartLayouter();
142 final FlowchartLayoutConfigurator layoutConfigurator = new FlowchartLayoutConfigurator();
143
144 configureLayouter(op, layouter);
145 configureLayoutConfigurator(op, layoutConfigurator);
146 configureLayoutExecutor(layouter);
147
148 final Graph2D graph = getGraph2D();
149
150 try {
151 configureInEdgeGrouping();
152 layoutConfigurator.prepareAll(graph);
153
154 launchLayouter(layouter, true);
155
156 } finally {
157 layoutConfigurator.restoreAll(graph);
158 graph.removeDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY);
159 }
160 }
161
162
165 static void configureLayouter(OptionHandler op, FlowchartLayouter layouter) {
166 layouter.setAllowFlatwiseEdges(op.getBool(ALLOW_FLATWISE_EDGES));
167 layouter.setLayoutOrientation(convertEnumToLayoutOrientation(op.getEnum(ORIENTATION)));
168
169 layouter.setLaneInsets(op.getDouble(LANE_INSETS));
170 layouter.setMinimumEdgeLength(op.getDouble(MINIMUM_EDGE_DISTANCE));
171 layouter.setMinimumEdgeDistance(op.getDouble(MINIMUM_EDGE_DISTANCE));
172 layouter.setMinimumNodeDistance(op.getDouble(MINIMUM_NODE_DISTANCE));
173 layouter.setMinimumPoolDistance(op.getDouble(MINIMUM_POOL_DISTANCE));
174 }
175
176
179 static void adoptSettings(OptionHandler oh, FlowchartLayouter layouter) {
180 oh.set(ORIENTATION, isHorizontalOrientation(layouter) ? ORIENTATION_ENUM[1] : ORIENTATION_ENUM[0]);
181 oh.set(ALLOW_FLATWISE_EDGES, Boolean.valueOf(layouter.isAllowFlatwiseEdges()));
182
183 oh.set(LANE_INSETS, new Double(layouter.getLaneInsets()));
184 oh.set(MINIMUM_EDGE_DISTANCE, new Double(layouter.getMinimumEdgeDistance()));
185 oh.set(MINIMUM_NODE_DISTANCE, new Double(layouter.getMinimumNodeDistance()));
186 oh.set(MINIMUM_POOL_DISTANCE, new Double(layouter.getMinimumPoolDistance()));
187 }
188
189
192 static void configureLayoutConfigurator(OptionHandler op, FlowchartLayoutConfigurator configurator) {
193 configurator.setPositiveBranchLabel(op.getString(POSITIVE_BRANCH_LABEL));
194 configurator.setNegativeBranchLabel(op.getString(NEGATIVE_BRANCH_LABEL));
195 configurator.setPreferredNegativeBranchDirection(
196 convertEnumToBranchDirection(op.getEnum(NEGATIVE_BRANCH_DIRECTION)));
197 configurator.setPreferredPositiveBranchDirection(
198 convertEnumToBranchDirection(op.getEnum(POSITIVE_BRANCH_DIRECTION)));
199 }
200
201
204 static void adoptSettings(OptionHandler oh, FlowchartLayoutConfigurator configurator) {
205 oh.set(POSITIVE_BRANCH_LABEL, configurator.getPositiveBranchLabel());
206 oh.set(NEGATIVE_BRANCH_LABEL, configurator.getNegativeBranchLabel());
207 oh.set(NEGATIVE_BRANCH_DIRECTION,
208 DIRECTION_ENUM[convertBranchDirectionToEnum(configurator.getPreferredNegativeBranchDirection())]);
209 oh.set(POSITIVE_BRANCH_DIRECTION,
210 DIRECTION_ENUM[convertBranchDirectionToEnum(configurator.getPreferredPositiveBranchDirection())]);
211 }
212
213
216 private void configureLayoutExecutor(FlowchartLayouter layouter) {
217 final Graph2DLayoutExecutor layoutExecutor = getLayoutExecutor();
218 layoutExecutor.getLayoutMorpher().setPreferredDuration(500L);
219
220 layoutExecutor.setConfiguringTableNodeRealizers(true);
221 final TableLayoutConfigurator tableLayoutConfigurator = getLayoutExecutor().getTableLayoutConfigurator();
222 tableLayoutConfigurator.setHorizontalLayoutConfiguration(isHorizontalOrientation(layouter));
223 tableLayoutConfigurator.setTableToTableDistance(layouter.getMinimumPoolDistance());
224 tableLayoutConfigurator.setFromSketchModeEnabled(true);
225 }
226
227 private void configureInEdgeGrouping() {
228 final OptionHandler oh = getOptionHandler();
229
230 if (oh.get(IN_EDGE_GROUPING).equals(GROUPING_NONE)) {
231 return;
232 }
233
234 final int inDegreeThreshold;
235 final int degreeThreshold;
236 if (oh.get(IN_EDGE_GROUPING).equals(GROUPING_ALL)) {
237 inDegreeThreshold = 0;
238 degreeThreshold = 0;
239 } else {
240 inDegreeThreshold = 3;
241 degreeThreshold = 4;
242 }
243
244 final Graph2D graph = getGraph2D();
245 final EdgeMap map = Maps.createHashedEdgeMap();
246 graph.addDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, map);
247
248 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
249 final Node node = nc.node();
250 if (node.inDegree() < 2 || node.inDegree() < inDegreeThreshold || node.degree() < degreeThreshold) {
251 continue;
252 }
253
254 for (EdgeCursor edgeCursor = node.inEdges(); edgeCursor.ok(); edgeCursor.next()) {
255 final Edge edge = edgeCursor.edge();
256 map.set(edge, node);
257 }
258 }
259 }
260
261
264 private static int convertEnumToBranchDirection(int enumIndex) {
265 switch (enumIndex) {
266 case 0:
267 return FlowchartLayouter.DIRECTION_WITH_THE_FLOW;
268 case 1:
269 return FlowchartLayouter.DIRECTION_FLATWISE;
270 case 2:
271 return FlowchartLayouter.DIRECTION_LEFT_IN_FLOW;
272 case 3:
273 return FlowchartLayouter.DIRECTION_RIGHT_IN_FLOW;
274 case 4:
275 default:
276 return FlowchartLayouter.DIRECTION_UNDEFINED;
277 }
278 }
279
280
283 private static int convertBranchDirectionToEnum(int direction) {
284 switch (direction) {
285 case FlowchartLayouter.DIRECTION_WITH_THE_FLOW:
286 return 0;
287 case FlowchartLayouter.DIRECTION_FLATWISE:
288 return 1;
289 case FlowchartLayouter.DIRECTION_LEFT_IN_FLOW:
290 return 2;
291 case FlowchartLayouter.DIRECTION_RIGHT_IN_FLOW:
292 return 3;
293 case FlowchartLayouter.DIRECTION_UNDEFINED:
294 default:
295 return 4;
296 }
297 }
298
299
302 private static byte convertEnumToLayoutOrientation(int enumIndex) {
303 return enumIndex == 0 ? LayoutOrientation.TOP_TO_BOTTOM : LayoutOrientation.LEFT_TO_RIGHT;
304 }
305
306 private static boolean isHorizontalOrientation(FlowchartLayouter layouter) {
307 final int layoutOrientation = (int) layouter.getLayoutOrientation();
308 return layoutOrientation == (int) LayoutOrientation.LEFT_TO_RIGHT
309 || layoutOrientation == (int) LayoutOrientation.RIGHT_TO_LEFT;
310 }
311
312 }
313