1
28 package demo.layout.hierarchic;
29
30 import demo.view.DemoBase;
31 import y.base.DataProvider;
32 import y.base.Edge;
33 import y.base.EdgeCursor;
34 import y.base.EdgeMap;
35 import y.base.Node;
36 import y.base.NodeCursor;
37 import y.geom.YPoint;
38 import y.layout.AbstractLayoutStage;
39 import y.layout.LayoutGraph;
40 import y.layout.LayoutOrientation;
41 import y.layout.Layouter;
42 import y.layout.PortConstraint;
43 import y.layout.hierarchic.AsIsLayerer;
44 import y.layout.hierarchic.IncrementalHierarchicLayouter;
45 import y.layout.hierarchic.incremental.AsIsSequencer;
46 import y.layout.labeling.SALabeling;
47 import y.util.Maps;
48 import y.view.Graph2D;
49 import y.view.Graph2DLayoutExecutor;
50
51 import javax.swing.AbstractAction;
52 import javax.swing.JCheckBox;
53 import javax.swing.JToolBar;
54 import javax.swing.event.ChangeEvent;
55 import javax.swing.event.ChangeListener;
56 import java.awt.EventQueue;
57 import java.awt.event.ActionEvent;
58 import java.util.Locale;
59
60
78 public class SankeyDemo extends DemoBase {
79
80 private boolean fromSketchEnabled = true;
81
82 public SankeyDemo(){
83 this(null);
84 }
85
86 public SankeyDemo(final String helpFilePath) {
87 addHelpPane(helpFilePath);
88 loadGraph("resource/voter-migration.graphml");
89 doLayout();
90 }
91
92 protected JToolBar createToolBar() {
93 final JToolBar toolBar = super.createToolBar();
94 toolBar.addSeparator();
95
96 final JCheckBox fromSketch = new JCheckBox("Use drawing as sketch");
97 fromSketch.setSelected(true);
98 fromSketch.addChangeListener(new ChangeListener() {
99 public void stateChanged(ChangeEvent e) {
100 fromSketchEnabled = !fromSketchEnabled;
101 }
102 });
103 toolBar.add(fromSketch);
104
105 toolBar.add(createActionControl(new AbstractAction(
106 "Layout", SHARED_LAYOUT_ICON) {
107 public void actionPerformed(ActionEvent e) {
108 doLayout();
109 }
110 }));
111
112 return toolBar;
113 }
114
115
118 private void doLayout() {
119 final Graph2D graph = view.getGraph2D();
120 final EdgeMap thicknessMap = Maps.createHashedEdgeMap();
121 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
122 final Edge edge = ec.edge();
123 final double thickness = graph.getRealizer(edge).getLineType().getLineWidth();
125 thicknessMap.setDouble(edge, thickness);
126 }
127 graph.addDataProvider(IncrementalHierarchicLayouter.EDGE_THICKNESS_DPKEY, thicknessMap);
128
129 final IncrementalHierarchicLayouter layouter = new IncrementalHierarchicLayouter();
131 final byte layoutOrientation = LayoutOrientation.LEFT_TO_RIGHT;
132 layouter.setLayoutOrientation(layoutOrientation);
133 layouter.setNodeToNodeDistance(50);
134 layouter.getEdgeLayoutDescriptor().setMinimumFirstSegmentLength(150);
135 layouter.getEdgeLayoutDescriptor().setMinimumLastSegmentLength(150);
136 if (fromSketchEnabled) {
137 layouter.setFromScratchLayerer(new AsIsLayerer());
138 layouter.setFromScratchSequencer(new AsIsSequencer());
139 }
140
141 final double portBorderRatio = 0;
143 layouter.getNodeLayoutDescriptor().setPortBorderGapRatios(portBorderRatio);
144
145 final SALabeling labelLayouter = (SALabeling) layouter.getLabelLayouter();
147 labelLayouter.setPlaceNodeLabels(false);
148 labelLayouter.setRemoveNodeOverlaps(true);
149 layouter.setLabelLayouterEnabled(true);
150
151 final NodeResizingStage nodeResizingStage = new NodeResizingStage(layouter);
154 nodeResizingStage.setLayoutOrientation(layoutOrientation);
155 nodeResizingStage.setPortBorderGapRatio(portBorderRatio);
156
157 final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
159 executor.doLayout(view, nodeResizingStage);
160
161 graph.removeDataProvider(IncrementalHierarchicLayouter.EDGE_THICKNESS_DPKEY);
162 }
163
164
168 private static class NodeResizingStage extends AbstractLayoutStage {
169 private double minimumPortDistance;
170 private double portBorderGapRatio;
171 private byte layoutOrientation;
172
173 public NodeResizingStage(Layouter coreLayouter) {
174 super(coreLayouter);
175 }
176
177
181 public byte getLayoutOrientation() {
182 return layoutOrientation;
183 }
184
185
191 public void setLayoutOrientation(byte layoutOrientation) {
192 this.layoutOrientation = layoutOrientation;
193 }
194
195
199 public double getPortBorderGapRatio() {
200 return portBorderGapRatio;
201 }
202
203
209 public void setPortBorderGapRatio(double portBorderGapRatio) {
210 this.portBorderGapRatio = portBorderGapRatio;
211 }
212
213
216 public double getMinimumPortDistance() {
217 return minimumPortDistance;
218 }
219
220
225 public void setMinimumPortDistance(double minimumPortDistance) {
226 this.minimumPortDistance = minimumPortDistance;
227 }
228
229 public boolean canLayout(LayoutGraph graph) {
230 return canLayoutCore(graph);
231 }
232
233 public void doLayout(LayoutGraph graph) {
234 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
235 adjustNodeSizes(nc.node(), graph);
237 }
238
239 doLayoutCore(graph);
240 }
241
242 private void adjustNodeSizes(Node node, LayoutGraph graph) {
243 double width = graph.getWidth(node);
244 double height = graph.getHeight(node);
245
246 final double inEdgeSpace = calcRequiredSpace(node.inEdges(), graph);
247 final double outEdgeSpace = calcRequiredSpace(node.outEdges(), graph);
248 if (layoutOrientation == LayoutOrientation.TOP_TO_BOTTOM
249 || layoutOrientation == LayoutOrientation.BOTTOM_TO_TOP) {
250 width = Math.max(width, inEdgeSpace);
252 width = Math.max(width, outEdgeSpace);
253 } else {
254 height = Math.max(height, inEdgeSpace);
256 height = Math.max(height, outEdgeSpace);
257 }
258
259 final DataProvider edgeThicknessDP = graph.getDataProvider(IncrementalHierarchicLayouter.EDGE_THICKNESS_DPKEY);
261 if (edgeThicknessDP != null) {
262 for (EdgeCursor ec = node.edges(); ec.ok(); ec.next()) {
263 final Edge edge = ec.edge();
264 final double thickness = edgeThicknessDP.getDouble(edge);
265
266 final PortConstraint spc = PortConstraint.getSPC(graph, edge);
267 if (edge.source() == node && spc != null && spc.isStrong()) {
268 final YPoint sourcePoint = graph.getSourcePointRel(edge);
269 width = Math.max(width, Math.abs(sourcePoint.getX()) * 2 + thickness);
270 height = Math.max(height, Math.abs(sourcePoint.getY()) * 2 + thickness);
271 }
272
273 final PortConstraint tpc = PortConstraint.getTPC(graph, edge);
274 if (edge.target() == node && tpc != null && tpc.isStrong()) {
275 final YPoint targetPoint = graph.getTargetPointRel(edge);
276 width = Math.max(width, Math.abs(targetPoint.getX()) * 2 + thickness);
277 height = Math.max(height, Math.abs(targetPoint.getY()) * 2 + thickness);
278 }
279 }
280 }
281
282 graph.setSize(node, width, height);
283 }
284
285
289 private double calcRequiredSpace(EdgeCursor edgesOnSideCur, LayoutGraph graph) {
290 double requiredSpace = 0;
291 final DataProvider edgeThicknessDP = graph.getDataProvider(IncrementalHierarchicLayouter.EDGE_THICKNESS_DPKEY);
292 for (EdgeCursor ec = edgesOnSideCur; ec.ok(); ec.next()) {
293 final Edge edge = ec.edge();
294 final double thickness = (edgeThicknessDP == null) ? 0 : edgeThicknessDP.getDouble(edge);
295 requiredSpace += Math.max(thickness, 1);
296 }
297 requiredSpace += (edgesOnSideCur.size() - 1) * getMinimumPortDistance();
298 requiredSpace += 2 * getPortBorderGapRatio() * getMinimumPortDistance();
299 return requiredSpace;
300 }
301 }
302
303
306 public static void main(String[] args) {
307 EventQueue.invokeLater(new Runnable() {
308 public void run() {
309 Locale.setDefault(Locale.ENGLISH);
310 initLnF();
311 (new SankeyDemo("resource/sankeydemohelp.html")).start("Sankey Demo");
312 }
313 });
314 }
315 }
316