1
28 package demo.view.orgchart;
29
30 import y.algo.Trees;
31 import y.base.DataProvider;
32 import y.base.EdgeCursor;
33 import y.base.ListCell;
34 import y.base.Node;
35 import y.base.NodeCursor;
36 import y.base.NodeMap;
37 import y.base.YList;
38 import y.geom.LineSegment;
39 import y.geom.YLineSegmentCursor;
40 import y.geom.YPoint;
41 import y.geom.YPointPath;
42 import y.layout.AbstractLayoutStage;
43 import y.layout.EdgeLayout;
44 import y.layout.LayoutGraph;
45 import y.layout.Layouter;
46 import y.layout.tree.AssistantPlacer;
47 import y.layout.tree.DefaultNodePlacer;
48 import y.layout.tree.GenericTreeLayouter;
49 import y.layout.tree.LeftRightPlacer;
50 import y.layout.tree.NodePlacer;
51 import y.util.Maps;
52
53
58 public class OrgChartLayouter implements Layouter {
59
60
63 public static final Object CHILD_LAYOUT_SAME_LAYER = "SAME_LAYER";
64
65
68 public static final Object CHILD_LAYOUT_LEFT_BELOW = "LEFT_BELOW";
69
70
73 public static final Object CHILD_LAYOUT_RIGHT_BELOW = "RIGHT_BELOW";
74
75
79 public static final Object CHILD_LAYOUT_BELOW = "BELOW";
80
81
86 public static final Object CHILD_LAYOUT_DPKEY = "OrgChartLayouter#CHILD_LAYOUT_DPKEY";
87
88
95 public static final Object ASSISTANT_DPKEY = "OrgChartLayouter#ASSISTANT_DPKEY";
96
97 private boolean duplicateBendsOnSharedBus = false;
98
99
100
108 public void setDuplicateBendsOnSharedBus(final boolean duplicateBendsOnSharedBus) {
109 this.duplicateBendsOnSharedBus = duplicateBendsOnSharedBus;
110 }
111
112
120 public boolean isDuplicateBendsOnSharedBus() {
121 return duplicateBendsOnSharedBus;
122 }
123
124
127 public void doLayout(final LayoutGraph graph) {
128 final GenericTreeLayouter gtl = new GenericTreeLayouter();
129 gtl.setGroupingSupported(true);
130 configureNodePlacers(graph);
131 gtl.doLayout(graph);
132 if(isDuplicateBendsOnSharedBus()) {
133 final Layouter bendDuplicator = new BendDuplicatorStage(null);
134 bendDuplicator.doLayout(graph);
135 }
136 }
137
138
142 protected void configureNodePlacers(final LayoutGraph graph) {
143 final DataProvider childLayoutDP = graph.getDataProvider(CHILD_LAYOUT_DPKEY);
144 final NodeMap nodePlacerMap = Maps.createHashedNodeMap();
145 if(childLayoutDP != null) {
146 graph.addDataProvider(GenericTreeLayouter.NODE_PLACER_DPKEY, nodePlacerMap);
147 for(final NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
148 final Node n = nc.node();
149 nodePlacerMap.set(n, createNodePlacer(childLayoutDP.get(n)));
150 }
151 graph.addDataProvider(GenericTreeLayouter.NODE_PLACER_DPKEY, nodePlacerMap);
152 }
153
154 final DataProvider assistDP = graph.getDataProvider(ASSISTANT_DPKEY);
155 for(NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
156 final Node n = nc.node();
157 if(assistDP != null && assistDP.getBool(n)) {
158 if(n.inDegree() > 0 && n.firstInEdge().source().outDegree() > 1) {
159 final AssistantPlacer placer = new AssistantPlacer();
160 final NodePlacer parentPlacer = (NodePlacer) nodePlacerMap.get(n.firstInEdge().source());
161 placer.setChildNodePlacer(parentPlacer);
162 nodePlacerMap.set(n.firstInEdge().source(), placer);
163 }
164 }
165 }
166 graph.addDataProvider(AssistantPlacer.ASSISTANT_DPKEY, assistDP);
167 }
168
169
172 protected NodePlacer createNodePlacer(final Object childLayout) {
173 if(childLayout == CHILD_LAYOUT_LEFT_BELOW) {
174 final DefaultNodePlacer placer = new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, DefaultNodePlacer.ALIGNMENT_CENTER, DefaultNodePlacer.ROUTING_FORK, 20.0d, 80.d);
175 placer.setChildPlacement(DefaultNodePlacer.PLACEMENT_VERTICAL_TO_LEFT);
176 placer.setRootAlignment(DefaultNodePlacer.ALIGNMENT_LEADING_ON_BUS);
177 placer.setRoutingStyle(DefaultNodePlacer.ROUTING_FORK_AT_ROOT);
178 return placer;
179 }
180 else if(childLayout == CHILD_LAYOUT_RIGHT_BELOW) {
181 final DefaultNodePlacer placer = new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, DefaultNodePlacer.ALIGNMENT_CENTER, DefaultNodePlacer.ROUTING_FORK, 20.0d, 80.d);
182 placer.setChildPlacement(DefaultNodePlacer.PLACEMENT_VERTICAL_TO_RIGHT);
183 placer.setRootAlignment(DefaultNodePlacer.ALIGNMENT_LEADING_ON_BUS);
184 placer.setRoutingStyle(DefaultNodePlacer.ROUTING_FORK_AT_ROOT);
185 return placer;
186 }
187 else if(childLayout == CHILD_LAYOUT_BELOW) {
188 final LeftRightPlacer placer = new LeftRightPlacer();
189 placer.setPlaceLastOnBottom(false);
190 return placer;
191 }
192 else { final DefaultNodePlacer placer = new DefaultNodePlacer();
194 placer.setChildPlacement(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD);
195 placer.setRootAlignment(DefaultNodePlacer.ALIGNMENT_MEDIAN);
196 return placer;
197 }
198 }
199
200
203 public boolean canLayout(final LayoutGraph graph) {
204 return Trees.isForest(graph); }
206
207
210 static class BendDuplicatorStage extends AbstractLayoutStage {
211
212 public BendDuplicatorStage(final Layouter coreLayouter) {
213 super(coreLayouter);
214 }
215
216 public boolean canLayout(final LayoutGraph graph) {
217 return true;
218 }
219
220 public void doLayout(final LayoutGraph graph) {
221 doLayoutCore(graph);
222
223 for(final NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
224 final Node n = nc.node();
225 for(final EdgeCursor ec = n.outEdges(); ec.ok(); ec.next()) {
226 boolean lastSegmentOverlap = false;
227 final EdgeLayout er = graph.getEdgeLayout(ec.edge());
228
229 if(er.pointCount() > 0) {
230 final YPoint bendPoint = er.getPoint(er.pointCount()-1);
232
233 loop:for(final EdgeCursor ecc = n.outEdges(); ecc.ok(); ecc.next()) {
234
235 if(ecc.edge() != ec.edge()) {
236 final YPointPath path = graph.getPath(ecc.edge());
237 for(final YLineSegmentCursor lc = path.lineSegments(); lc.ok(); lc.next()) {
238 final LineSegment seg = lc.lineSegment();
239 if(seg.contains(bendPoint)) {
240 lastSegmentOverlap = true;
241 break loop;
242 }
243 }
244 }
245 }
246 }
247
248 final YList points = graph.getPointList(ec.edge());
249 for(ListCell c = points.firstCell(); c != null; c = c.succ()) {
250 final YPoint p = (YPoint) c.getInfo();
251 if(c.succ() == null && !lastSegmentOverlap) {
252 break;
253 }
254 points.insertBefore(new YPoint(p.x,p.y), c);
255 }
256 graph.setPoints(ec.edge(), points);
257 }
258 }
259 }
260 }
261 }
262