1
14 package demo.module;
15
16 import y.base.DataProvider;
17 import y.base.Node;
18 import y.layout.ComponentLayouter;
19 import y.layout.LayoutOrientation;
20 import y.layout.genealogy.FamilyTreeLayouter;
21 import y.layout.hierarchic.IncrementalHierarchicLayouter;
22 import y.layout.hierarchic.incremental.EdgeLayoutDescriptor;
23 import y.layout.hierarchic.incremental.NodeLayoutDescriptor;
24 import y.module.LayoutModule;
25 import y.option.ConstraintManager;
26 import y.option.OptionGroup;
27 import y.option.OptionHandler;
28 import y.util.DataProviderAdapter;
29 import y.view.Graph2D;
30 import y.view.NodeRealizer;
31 import y.view.hierarchy.GroupLayoutConfigurator;
32
33 import java.awt.Color;
34
35
39 public class FamilyTreeLayoutModule extends LayoutModule {
40
41 private static final String FAMILY_TREE_LAYOUTER = "FAMILY_TREE_LAYOUTER";
42
43 private static final String FAMILY_PROPERTIES = "FAMILY_PROPERTIES";
45 private static final String FAMILY_COLOR = "FAMILY_COLOR";
46 private static final String MALE_COLOR = "MALE_COLOR";
47 private static final String FEMALE_COLOR = "FEMALE_COLOR";
48
49 private static final String LAYOUT = "LAYOUT";
51 private static final String ORIENTATION = "ORIENTATION";
52 private static final String RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
53 private static final String BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
54 private static final String LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
55 private static final String TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
56 private static final String SINGLE_DIRECT_BELOW = "SINGLE_DIRECT_BELOW";
57 private static final String FAMILIES_ALWAYS_BELOW = "FAMILIES_ALWAYS_BELOW";
58
59 private static final String DISTANCES = "DISTANCES";
60 private static final String SIBLING_DISTANCES = "SIBLING_DISTANCES";
61 private static final String HORIZONTAL_SPACING = "HORIZONTAL_SPACING";
62 private static final String NODE_TO_NODE_DISTANCE = "NODE_TO_NODE_DISTANCE";
63
64 private static final String GENERATION_DISTANCES = "GENERATION_DISTANCES";
65 private static final String VERTICAL_SPACING = "VERTICAL_SPACING";
66 private static final String MINIMUM_LAYER_DISTANCE = "MINIMUM_LAYER_DISTANCE";
67 private static final String MINIMUM_FIRST_SEGMENT = "MINIMUM_FIRST_SEGMENT";
68 private static final String MINIMUM_LAST_SEGMENT = "MINIMUM_LAST_SEGMENT";
69
70 private static final String ADVANCED_LAYOUT = "ADVANCED_LAYOUT";
72 private static final String COMPONENT_LAYOUTER = "COMPONENT_LAYOUTER";
73 private static final String USE_COMPONENT_LAYOUTER = "USE_COMPONENT_LAYOUTER";
74 private static final String COMPONENT_DISTANCE = "COMPONENT_DISTANCE";
75 private static final String COMPONENT_STYLE = "COMPONENT_STYLE";
76 private static final String COMPONENT_STYLE_NONE = "COMPONENT_STYLE_NONE";
77 private static final String COMPONENT_STYLE_ROWS = "COMPONENT_STYLE_ROWS";
78 private static final String COMPONENT_STYLE_SINGLE_ROW = "COMPONENT_STYLE_SINGLE_ROW";
79 private static final String COMPONENT_STYLE_SINGLE_COLUMN = "COMPONENT_STYLE_SINGLE_COLUMN";
80 private static final String COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE = "COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE";
81 private static final String COMPONENT_STYLE_PACKED_RECTANGLE = "COMPONENT_STYLE_PACKED_RECTANGLE";
82 private static final String COMPONENT_STYLE_PACKED_CIRCLE = "COMPONENT_STYLE_PACKED_CIRCLE";
83 private static final String COMPONENT_STYLE_PACKED_COMPACT_CIRCLE = "COMPONENT_STYLE_PACKED_COMPACT_CIRCLE";
84 private static final String SORT_BY_SEX = "SORT_BY_SEX";
85 private static final String DO_NOT_SORT = "DO_NOT_SORT";
86 private static final String FEMALE_LEFT = "FEMALE_LEFT";
87 private static final String FEMALE_ALWAYS_LEFT = "FEMALE_ALWAYS_LEFT";
88 private static final String MALE_LEFT = "MALE_LEFT";
89 private static final String MALE_ALWAYS_LEFT = "MALE_ALWAYS_LEFT";
90
91 private static final String NODE_ALIGNMENT = "NODE_ALIGNMENT";
92 private static final String NODE_ALIGN_TOP = "NODE_ALIGN_TOP";
93 private static final String NODE_ALIGN_CENTER = "NODE_ALIGN_CENTER";
94 private static final String NODE_ALIGN_BOTTOM = "NODE_ALIGN_BOTTOM";
95
96 private static final Object[] sortBySexEnum = {DO_NOT_SORT, FEMALE_LEFT, FEMALE_ALWAYS_LEFT, MALE_LEFT,
97 MALE_ALWAYS_LEFT};
98 private static final Object[] orientEnum = {TOP_TO_BOTTOM, LEFT_TO_RIGHT, BOTTOM_TO_TOP, RIGHT_TO_LEFT};
99 private static final Object[] nodeAlignmentEnum = {NODE_ALIGN_TOP, NODE_ALIGN_CENTER, NODE_ALIGN_BOTTOM};
100 private static final Object[] componentStyleEnum =
101 {
102 COMPONENT_STYLE_NONE,
103 COMPONENT_STYLE_ROWS,
104 COMPONENT_STYLE_SINGLE_ROW,
105 COMPONENT_STYLE_SINGLE_COLUMN,
106 COMPONENT_STYLE_PACKED_RECTANGLE,
107 COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE,
108 COMPONENT_STYLE_PACKED_CIRCLE,
109 COMPONENT_STYLE_PACKED_COMPACT_CIRCLE,
110 };
111
112 public FamilyTreeLayoutModule() {
113 super(FAMILY_TREE_LAYOUTER, "yFiles Layout Team", "Layouter for genealogic data");
114 }
115
116
121 protected OptionHandler createOptionHandler() {
122 OptionHandler op = new OptionHandler(getModuleName());
123 ConstraintManager cm = new ConstraintManager(op);
124
125 op.useSection(FAMILY_PROPERTIES);
126 op.addColor(FAMILY_COLOR, Color.black, true);
127 op.addColor(MALE_COLOR, new Color(0xCCCCFF), true);
128 op.addColor(FEMALE_COLOR, new Color(0xFF99CC), true);
129
130 op.useSection(LAYOUT);
131 op.addEnum(ORIENTATION, orientEnum, 0);
132 op.addBool(SINGLE_DIRECT_BELOW, true);
133 op.addBool(FAMILIES_ALWAYS_BELOW, false);
134 op.addEnum(NODE_ALIGNMENT, nodeAlignmentEnum, 0);
135 op.addEnum(SORT_BY_SEX, sortBySexEnum, 0);
136
137 op.useSection(DISTANCES);
138 OptionGroup ogSiblings = new OptionGroup();
139 ogSiblings.setAttribute(OptionGroup.ATTRIBUTE_TITLE, SIBLING_DISTANCES);
140 ogSiblings.addItem(op.addDouble(HORIZONTAL_SPACING, 40, 0, 400));
141 ogSiblings.addItem(op.addDouble(NODE_TO_NODE_DISTANCE, 40, 0, 400));
142 OptionGroup ogGenerations = new OptionGroup();
143 ogGenerations.setAttribute(OptionGroup.ATTRIBUTE_TITLE, GENERATION_DISTANCES);
144 ogGenerations.addItem(op.addDouble(VERTICAL_SPACING, 10, 0, 100));
145 ogGenerations.addItem(op.addDouble(MINIMUM_LAYER_DISTANCE, 40, 0, 400));
146 ogGenerations.addItem(op.addDouble(MINIMUM_FIRST_SEGMENT, 40, 0, 400));
147 ogGenerations.addItem(op.addDouble(MINIMUM_LAST_SEGMENT, 20, 0, 400));
148
149 op.useSection(ADVANCED_LAYOUT);
150 op.addBool(USE_COMPONENT_LAYOUTER, true);
151 OptionGroup ogComp = new OptionGroup();
152 ogComp.setAttribute(OptionGroup.ATTRIBUTE_TITLE, COMPONENT_LAYOUTER);
153 ogComp.addItem(op.addInt(COMPONENT_DISTANCE, 40));
154 ogComp.addItem(op.addEnum(COMPONENT_STYLE, componentStyleEnum, 1));
155 cm.setEnabledOnValueEquals(USE_COMPONENT_LAYOUTER, Boolean.TRUE, COMPONENT_DISTANCE);
156
157 return op;
158 }
159
160
161 public void mainrun() {
162
163 final OptionHandler op = getOptionHandler();
164
165 final Graph2D graph = getGraph2D();
166
167
168 FamilyTreeLayouter layouter = new FamilyTreeLayouter();
169 layouter.setSpacingBetweenFamilyMembers(op.getDouble(HORIZONTAL_SPACING));
170 layouter.setOffsetForFamilyNodes(op.getDouble(VERTICAL_SPACING));
171 layouter.setFamilyNodesAlwaysBelow(op.getBool(FAMILIES_ALWAYS_BELOW));
172 layouter.setPartnerlessBelow(op.getBool(SINGLE_DIRECT_BELOW));
173
174
175 if (op.get(ORIENTATION).equals(TOP_TO_BOTTOM)) {
178 layouter.setLayoutOrientation(LayoutOrientation.TOP_TO_BOTTOM);
179 } else if (op.get(ORIENTATION).equals(LEFT_TO_RIGHT)) {
180 layouter.setLayoutOrientation(LayoutOrientation.LEFT_TO_RIGHT);
181 } else if (op.get(ORIENTATION).equals(BOTTOM_TO_TOP)) {
182 layouter.setLayoutOrientation(LayoutOrientation.BOTTOM_TO_TOP);
183 } else if (op.get(ORIENTATION).equals(RIGHT_TO_LEFT)) {
184 layouter.setLayoutOrientation(LayoutOrientation.RIGHT_TO_LEFT);
185 }
186
187
188
189
190 layouter.setComponentLayouterEnabled(op.getBool(USE_COMPONENT_LAYOUTER));
191 ComponentLayouter cl = (ComponentLayouter) layouter.getComponentLayouter();
192 cl.setComponentSpacing(op.getInt(COMPONENT_DISTANCE));
193 if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_NONE)) {
194 cl.setStyle(ComponentLayouter.STYLE_NONE);
195 } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_CIRCLE)) {
196 cl.setStyle(ComponentLayouter.STYLE_PACKED_CIRCLE);
197 } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_COMPACT_CIRCLE)) {
198 cl.setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_CIRCLE);
199 } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_COMPACT_RECTANGLE)) {
200 cl.setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_RECTANGLE);
201 } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_PACKED_RECTANGLE)) {
202 cl.setStyle(ComponentLayouter.STYLE_PACKED_RECTANGLE);
203 } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_ROWS)) {
204 cl.setStyle(ComponentLayouter.STYLE_ROWS);
205 } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_SINGLE_COLUMN)) {
206 cl.setStyle(ComponentLayouter.STYLE_SINGLE_COLUMN);
207 } else if (op.get(COMPONENT_STYLE).equals(COMPONENT_STYLE_SINGLE_ROW)) {
208 cl.setStyle(ComponentLayouter.STYLE_SINGLE_ROW);
209 }
210
211 IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
212 layouter.setTopLayouter(ihl);
213
214
215 NodeLayoutDescriptor nld = ihl.getNodeLayoutDescriptor();
216 if (op.get(NODE_ALIGNMENT).equals(NODE_ALIGN_TOP)) {
217 nld.setLayerAlignment(0.0);
218 layouter.setAlignment(FamilyTreeLayouter.ALIGN_TOP);
219 } else if (op.get(NODE_ALIGNMENT).equals(NODE_ALIGN_CENTER)) {
220 nld.setLayerAlignment(0.5);
221 layouter.setAlignment(FamilyTreeLayouter.ALIGN_CENTER);
222 } else if (op.get(NODE_ALIGNMENT).equals(NODE_ALIGN_BOTTOM)) {
223 nld.setLayerAlignment(1.0);
224 layouter.setAlignment(FamilyTreeLayouter.ALIGN_BOTTOM);
225 }
226
227 ihl.setMinimumLayerDistance(op.getDouble(MINIMUM_LAYER_DISTANCE));
228 ihl.setNodeToNodeDistance(op.getDouble(NODE_TO_NODE_DISTANCE));
229 EdgeLayoutDescriptor eld = ihl.getEdgeLayoutDescriptor();
230 eld.setMinimumFirstSegmentLength(op.getDouble(MINIMUM_FIRST_SEGMENT));
231 eld.setMinimumLastSegmentLength(op.getDouble(MINIMUM_LAST_SEGMENT));
232 eld.setOrthogonallyRouted(true);
233
234 if (op.get(SORT_BY_SEX).equals(DO_NOT_SORT)) {
235 layouter.setSortFamilyMembers(FamilyTreeLayouter.DO_NOT_SORT_BY_SEX);
236 } else if (op.get(SORT_BY_SEX).equals(FEMALE_LEFT)) {
237 layouter.setSortFamilyMembers(FamilyTreeLayouter.FEMALE_FIRST);
238 } else if (op.get(SORT_BY_SEX).equals(FEMALE_ALWAYS_LEFT)) {
239 layouter.setSortFamilyMembers(FamilyTreeLayouter.FEMALE_ALWAYS_FIRST);
240 } else if (op.get(SORT_BY_SEX).equals(MALE_LEFT)) {
241 layouter.setSortFamilyMembers(FamilyTreeLayouter.MALE_FIRST);
242 } else if (op.get(SORT_BY_SEX).equals(MALE_ALWAYS_LEFT)) {
243 layouter.setSortFamilyMembers(FamilyTreeLayouter.MALE_ALWAYS_FIRST);
244 }
245
246
247 DataProvider dpType = null;
248 if(graph.getDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE) == null) {
249
250 dpType = new DataProviderAdapter() {
251 public Object get(Object o) {
252 NodeRealizer nr = graph.getRealizer((Node)o);
253 Color nodeColor = nr.getFillColor();
254 if (nodeColor != null && nodeColor.equals(op.get(MALE_COLOR))) {
255 return FamilyTreeLayouter.TYPE_MALE;
256 }
257 if (nodeColor != null && nodeColor.equals(op.get(FEMALE_COLOR))) {
258 return FamilyTreeLayouter.TYPE_FEMALE;
259 }
260 if (nodeColor != null && nodeColor.equals(op.get(FAMILY_COLOR))) {
261 return FamilyTreeLayouter.TYPE_FAMILY;
262 }
263 return null;
264 }
265 };
266 graph.addDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE, dpType);
267 }
268
269
270 GroupLayoutConfigurator glc = new GroupLayoutConfigurator(graph);
271
272 try {
273 glc.prepareAll();
274 launchLayouter(layouter, true);
275 } finally {
276 glc.restoreAll();
277 if (dpType != null) {
278 graph.removeDataProvider(FamilyTreeLayouter.DP_KEY_FAMILY_TYPE);
279 }
280 }
281
282 }
283 }
284