1
28 package demo.layout.module;
29
30 import y.module.LayoutModule;
31 import y.module.YModule;
32
33 import y.base.Node;
34 import y.layout.ComponentLayouter;
35 import y.layout.LayoutOrientation;
36 import y.layout.genealogy.FamilyTreeLayouter;
37 import y.layout.hierarchic.IncrementalHierarchicLayouter;
38 import y.layout.hierarchic.incremental.EdgeLayoutDescriptor;
39 import y.layout.hierarchic.incremental.NodeLayoutDescriptor;
40 import y.layout.hierarchic.incremental.RoutingStyle;
41 import y.layout.hierarchic.incremental.SimplexNodePlacer;
42 import y.option.ConstraintManager;
43 import y.option.OptionGroup;
44 import y.option.OptionHandler;
45 import y.option.OptionItem;
46 import y.util.DataProviderAdapter;
47 import y.view.Graph2D;
48 import y.view.NodeRealizer;
49
50 import java.awt.Color;
51
52
56 public class FamilyTreeLayoutModule extends LayoutModule {
57
58 protected static final String MODULE_FAMILY_TREE_LAYOUTER = "FAMILY_TREE_LAYOUTER";
60
61 protected static final String SECTION_FAMILY_PROPERTIES = "FAMILY_PROPERTIES";
64 protected static final String ITEM_FAMILY_COLOR = "FAMILY_COLOR";
66 protected static final String ITEM_MALE_COLOR = "MALE_COLOR";
67 protected static final String ITEM_FEMALE_COLOR = "FEMALE_COLOR";
68
69 protected static final String SECTION_LAYOUT = "LAYOUT";
72 protected static final String ITEM_ORIENTATION = "ORIENTATION";
74 protected static final String VALUE_TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
75 protected static final String VALUE_LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
76 protected static final String VALUE_BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
77 protected static final String VALUE_RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
78 protected static final String ITEM_SINGLE_DIRECT_BELOW = "SINGLE_DIRECT_BELOW";
79 protected static final String ITEM_FAMILIES_ALWAYS_BELOW = "FAMILIES_ALWAYS_BELOW";
80 protected static final String ITEM_NODE_ALIGNMENT = "NODE_ALIGNMENT";
81 protected static final String VALUE_NODE_ALIGN_TOP = "NODE_ALIGN_TOP";
82 protected static final String VALUE_NODE_ALIGN_CENTER = "NODE_ALIGN_CENTER";
83 protected static final String VALUE_NODE_ALIGN_BOTTOM = "NODE_ALIGN_BOTTOM";
84 protected static final String ITEM_SORT_BY_SEX = "SORT_BY_SEX";
85 protected static final String VALUE_DO_NOT_SORT = "DO_NOT_SORT";
86 protected static final String VALUE_FEMALE_LEFT = "FEMALE_LEFT";
87 protected static final String VALUE_FEMALE_ALWAYS_LEFT = "FEMALE_ALWAYS_LEFT";
88 protected static final String VALUE_MALE_LEFT = "MALE_LEFT";
89 protected static final String VALUE_MALE_ALWAYS_LEFT = "MALE_ALWAYS_LEFT";
90
91 protected static final String SECTION_DISTANCES = "DISTANCES";
93 protected static final String TITLE_SIBLING_DISTANCES = "SIBLING_DISTANCES";
95 protected static final String ITEM_HORIZONTAL_SPACING = "HORIZONTAL_SPACING";
96 protected static final String ITEM_NODE_TO_NODE_DISTANCE = "NODE_TO_NODE_DISTANCE";
97 protected static final String TITLE_GENERATION_DISTANCES = "GENERATION_DISTANCES";
98 protected static final String ITEM_VERTICAL_SPACING = "VERTICAL_SPACING";
99 protected static final String ITEM_MINIMUM_LAYER_DISTANCE = "MINIMUM_LAYER_DISTANCE";
100 protected static final String ITEM_MINIMUM_FIRST_SEGMENT = "MINIMUM_FIRST_SEGMENT";
101 protected static final String ITEM_MINIMUM_LAST_SEGMENT = "MINIMUM_LAST_SEGMENT";
102
103 protected static final String SECTION_ADVANCED_LAYOUT = "ADVANCED_LAYOUT";
106 protected static final String ITEM_USE_COMPONENT_LAYOUTER = "USE_COMPONENT_LAYOUTER";
108 protected static final String TITLE_COMPONENT_LAYOUTER = "COMPONENT_LAYOUTER";
109 protected static final String ITEM_COMPONENT_DISTANCE = "COMPONENT_DISTANCE";
110 protected static final String ITEM_COMPONENT_STYLE = "COMPONENT_STYLE";
111 protected static final String VALUE_COMPONENT_STYLE_NONE = "COMPONENT_STYLE_NONE";
112 protected static final String VALUE_COMPONENT_STYLE_ROWS = "COMPONENT_STYLE_ROWS";
113 protected static final String VALUE_COMPONENT_STYLE_SINGLE_ROW = "COMPONENT_STYLE_SINGLE_ROW";
114 protected static final String VALUE_COMPONENT_STYLE_SINGLE_COLUMN = "COMPONENT_STYLE_SINGLE_COLUMN";
115 protected static final String VALUE_COMPONENT_STYLE_PACKED_RECTANGLE = "COMPONENT_STYLE_PACKED_RECTANGLE";
116 protected static final String VALUE_COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE = "COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE";
117 protected static final String VALUE_COMPONENT_STYLE_PACKED_CIRCLE = "COMPONENT_STYLE_PACKED_CIRCLE";
118 protected static final String VALUE_COMPONENT_STYLE_PACKED_COMPACT_CIRCLE = "COMPONENT_STYLE_PACKED_COMPACT_CIRCLE";
119
120 private boolean isFamilyDPAddedByModule = false;
122
123
126 public FamilyTreeLayoutModule() {
127 super(MODULE_FAMILY_TREE_LAYOUTER);
128 }
129
130
134 protected OptionHandler createOptionHandler() {
135 final OptionHandler options = new OptionHandler(getModuleName());
136 final ConstraintManager optionConstraints = new ConstraintManager(options);
137
138 options.useSection(SECTION_FAMILY_PROPERTIES);
140 options.addColor(ITEM_FAMILY_COLOR, Color.black, true);
142 options.addColor(ITEM_MALE_COLOR, new Color(0xFFCC99), true);
143 options.addColor(ITEM_FEMALE_COLOR, new Color(0xCCCCFF), true);
144
145 options.useSection(SECTION_LAYOUT);
147 options.addEnum(ITEM_ORIENTATION, new String[]{
149 VALUE_TOP_TO_BOTTOM,
150 VALUE_LEFT_TO_RIGHT,
151 VALUE_BOTTOM_TO_TOP,
152 VALUE_RIGHT_TO_LEFT
153 }, 0);
154 options.addBool(ITEM_SINGLE_DIRECT_BELOW, true);
155 options.addBool(ITEM_FAMILIES_ALWAYS_BELOW, false);
156 options.addEnum(ITEM_NODE_ALIGNMENT, new String[]{
157 VALUE_NODE_ALIGN_TOP,
158 VALUE_NODE_ALIGN_CENTER,
159 VALUE_NODE_ALIGN_BOTTOM
160 }, 0);
161 options.addEnum(ITEM_SORT_BY_SEX, new String[]{
162 VALUE_DO_NOT_SORT,
163 VALUE_FEMALE_LEFT,
164 VALUE_FEMALE_ALWAYS_LEFT,
165 VALUE_MALE_LEFT,
166 VALUE_MALE_ALWAYS_LEFT
167 }, 0);
168
169 options.useSection(SECTION_DISTANCES);
171 final OptionGroup siblingsGroup = new OptionGroup();
173 siblingsGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_SIBLING_DISTANCES);
174 siblingsGroup.addItem(options.addDouble(ITEM_HORIZONTAL_SPACING, 40, 0, 400));
176 siblingsGroup.addItem(options.addDouble(ITEM_NODE_TO_NODE_DISTANCE, 40, 0, 400));
177 final OptionGroup generationsGroup = new OptionGroup();
179 generationsGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_GENERATION_DISTANCES);
180 generationsGroup.addItem(options.addDouble(ITEM_VERTICAL_SPACING, 10, 0, 100));
182 generationsGroup.addItem(options.addDouble(ITEM_MINIMUM_LAYER_DISTANCE, 40, 0, 400));
183 generationsGroup.addItem(options.addDouble(ITEM_MINIMUM_FIRST_SEGMENT, 40, 0, 400));
184 generationsGroup.addItem(options.addDouble(ITEM_MINIMUM_LAST_SEGMENT, 20, 0, 400));
185
186 options.useSection(SECTION_ADVANCED_LAYOUT);
188 final OptionItem itemUseComponentLayouter = options.addBool(ITEM_USE_COMPONENT_LAYOUTER, true);
190 final OptionGroup componentsGroup = new OptionGroup();
192 componentsGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_COMPONENT_LAYOUTER);
193 final OptionItem itemComponentDistance = componentsGroup.addItem(options.addInt(ITEM_COMPONENT_DISTANCE, 40));
195 componentsGroup.addItem(
196 options.addEnum(ITEM_COMPONENT_STYLE, new String[]{
197 VALUE_COMPONENT_STYLE_NONE,
198 VALUE_COMPONENT_STYLE_ROWS,
199 VALUE_COMPONENT_STYLE_SINGLE_ROW,
200 VALUE_COMPONENT_STYLE_SINGLE_COLUMN,
201 VALUE_COMPONENT_STYLE_PACKED_RECTANGLE,
202 VALUE_COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE,
203 VALUE_COMPONENT_STYLE_PACKED_CIRCLE,
204 VALUE_COMPONENT_STYLE_PACKED_COMPACT_CIRCLE,
205 }, 1));
206 optionConstraints.setEnabledOnValueEquals(itemUseComponentLayouter, Boolean.TRUE, itemComponentDistance);
208
209 return options;
210 }
211
212
216 protected void mainrun() {
217 final FamilyTreeLayouter familyTree = new FamilyTreeLayouter();
218
219 final OptionHandler options = getOptionHandler();
220 configure(familyTree, options);
221
222 final Graph2D graph = getGraph2D();
223 prepareGraph(graph, options);
224 try {
225 launchLayouter(familyTree);
226 } finally {
227 restoreGraph(graph, options);
228 }
229 }
230
231
241 protected void prepareGraph(final Graph2D graph, final OptionHandler options) {
242 isFamilyDPAddedByModule = graph.getDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE) == null;
246 if (isFamilyDPAddedByModule) {
247
248 graph.addDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE, new DataProviderAdapter() {
249 public Object get(Object o) {
250 final NodeRealizer nr = graph.getRealizer((Node) o);
251 final Color nodeColor = nr.getFillColor();
252 if (nodeColor != null && nodeColor.equals(options.get(ITEM_MALE_COLOR))) {
253 return FamilyTreeLayouter.TYPE_MALE;
254 }
255 if (nodeColor != null && nodeColor.equals(options.get(ITEM_FEMALE_COLOR))) {
256 return FamilyTreeLayouter.TYPE_FEMALE;
257 }
258 if (nodeColor != null && nodeColor.equals(options.get(ITEM_FAMILY_COLOR))) {
259 return FamilyTreeLayouter.TYPE_FAMILY;
260 }
261 return null;
262 }
263 });
264 }
265 }
266
267
273 protected void restoreGraph(final Graph2D graph, final OptionHandler options) {
274 if (isFamilyDPAddedByModule) {
276 isFamilyDPAddedByModule = false;
277 graph.removeDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE);
278 }
279 }
280
281
286 protected void configure(final FamilyTreeLayouter familyTree, final OptionHandler options) {
287 familyTree.setSpacingBetweenFamilyMembers(options.getDouble(ITEM_HORIZONTAL_SPACING));
288 familyTree.setOffsetForFamilyNodes(options.getDouble(ITEM_VERTICAL_SPACING));
289 familyTree.setFamilyNodesAlwaysBelow(options.getBool(ITEM_FAMILIES_ALWAYS_BELOW));
290 familyTree.setPartnerlessBelow(options.getBool(ITEM_SINGLE_DIRECT_BELOW));
291
292
293 if (options.get(ITEM_ORIENTATION).equals(VALUE_TOP_TO_BOTTOM)) {
296 familyTree.setLayoutOrientation(LayoutOrientation.TOP_TO_BOTTOM);
297 } else if (options.get(ITEM_ORIENTATION).equals(VALUE_LEFT_TO_RIGHT)) {
298 familyTree.setLayoutOrientation(LayoutOrientation.LEFT_TO_RIGHT);
299 } else if (options.get(ITEM_ORIENTATION).equals(VALUE_BOTTOM_TO_TOP)) {
300 familyTree.setLayoutOrientation(LayoutOrientation.BOTTOM_TO_TOP);
301 } else if (options.get(ITEM_ORIENTATION).equals(VALUE_RIGHT_TO_LEFT)) {
302 familyTree.setLayoutOrientation(LayoutOrientation.RIGHT_TO_LEFT);
303 }
304
305
306
307
308 familyTree.setComponentLayouterEnabled(options.getBool(ITEM_USE_COMPONENT_LAYOUTER));
309 final ComponentLayouter cl = (ComponentLayouter) familyTree.getComponentLayouter();
310 cl.setComponentSpacing(options.getInt(ITEM_COMPONENT_DISTANCE));
311 if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_NONE)) {
312 cl.setStyle(ComponentLayouter.STYLE_NONE);
313 } else if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_PACKED_CIRCLE)) {
314 cl.setStyle(ComponentLayouter.STYLE_PACKED_CIRCLE);
315 } else if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_PACKED_COMPACT_CIRCLE)) {
316 cl.setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_CIRCLE);
317 } else if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE)) {
318 cl.setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_RECTANGLE);
319 } else if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_PACKED_RECTANGLE)) {
320 cl.setStyle(ComponentLayouter.STYLE_PACKED_RECTANGLE);
321 } else if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_ROWS)) {
322 cl.setStyle(ComponentLayouter.STYLE_MULTI_ROWS);
323 } else if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_SINGLE_COLUMN)) {
324 cl.setStyle(ComponentLayouter.STYLE_SINGLE_COLUMN);
325 } else if (options.get(ITEM_COMPONENT_STYLE).equals(VALUE_COMPONENT_STYLE_SINGLE_ROW)) {
326 cl.setStyle(ComponentLayouter.STYLE_SINGLE_ROW);
327 }
328
329 final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
330 familyTree.setTopLayouter(ihl);
331
332 ((SimplexNodePlacer) ihl.getNodePlacer()).setBendReductionEnabled(false);
334
335
336 final NodeLayoutDescriptor nld = ihl.getNodeLayoutDescriptor();
337 if (options.get(ITEM_NODE_ALIGNMENT).equals(VALUE_NODE_ALIGN_TOP)) {
338 nld.setLayerAlignment(0.0);
339 familyTree.setAlignment(FamilyTreeLayouter.ALIGN_TOP);
340 } else if (options.get(ITEM_NODE_ALIGNMENT).equals(VALUE_NODE_ALIGN_CENTER)) {
341 nld.setLayerAlignment(0.5);
342 familyTree.setAlignment(FamilyTreeLayouter.ALIGN_CENTER);
343 } else if (options.get(ITEM_NODE_ALIGNMENT).equals(VALUE_NODE_ALIGN_BOTTOM)) {
344 nld.setLayerAlignment(1.0);
345 familyTree.setAlignment(FamilyTreeLayouter.ALIGN_BOTTOM);
346 }
347
348 ihl.setMinimumLayerDistance(options.getDouble(ITEM_MINIMUM_LAYER_DISTANCE));
349 ihl.setNodeToNodeDistance(options.getDouble(ITEM_NODE_TO_NODE_DISTANCE));
350 final EdgeLayoutDescriptor eld = ihl.getEdgeLayoutDescriptor();
351 eld.setMinimumFirstSegmentLength(options.getDouble(ITEM_MINIMUM_FIRST_SEGMENT));
352 eld.setMinimumLastSegmentLength(options.getDouble(ITEM_MINIMUM_LAST_SEGMENT));
353 eld.setRoutingStyle(new RoutingStyle(RoutingStyle.EDGE_STYLE_ORTHOGONAL));
354
355 if (options.get(ITEM_SORT_BY_SEX).equals(VALUE_DO_NOT_SORT)) {
356 familyTree.setSortFamilyMembers(FamilyTreeLayouter.DO_NOT_SORT_BY_SEX);
357 } else if (options.get(ITEM_SORT_BY_SEX).equals(VALUE_FEMALE_LEFT)) {
358 familyTree.setSortFamilyMembers(FamilyTreeLayouter.FEMALE_FIRST);
359 } else if (options.get(ITEM_SORT_BY_SEX).equals(VALUE_FEMALE_ALWAYS_LEFT)) {
360 familyTree.setSortFamilyMembers(FamilyTreeLayouter.FEMALE_ALWAYS_FIRST);
361 } else if (options.get(ITEM_SORT_BY_SEX).equals(VALUE_MALE_LEFT)) {
362 familyTree.setSortFamilyMembers(FamilyTreeLayouter.MALE_FIRST);
363 } else if (options.get(ITEM_SORT_BY_SEX).equals(VALUE_MALE_ALWAYS_LEFT)) {
364 familyTree.setSortFamilyMembers(FamilyTreeLayouter.MALE_ALWAYS_FIRST);
365 }
366 }
367 }
368