1 package prefuse.action.layout.graph; 2 3 import java.awt.geom.Point2D ; 4 import java.util.Iterator ; 5 6 import prefuse.data.Graph; 7 import prefuse.data.Schema; 8 import prefuse.data.tuple.TupleSet; 9 import prefuse.visual.NodeItem; 10 11 12 23 public class BalloonTreeLayout extends TreeLayout { 24 25 private int m_minRadius = 2; 26 27 32 public BalloonTreeLayout(String group) { 33 this(group, 2); 34 } 35 36 42 public BalloonTreeLayout(String group, int minRadius) { 43 super(group); 44 m_minRadius = minRadius; 45 } 46 47 51 public int getMinRadius() { 52 return m_minRadius; 53 } 54 55 59 public void setMinRadius(int minRadius) { 60 m_minRadius = minRadius; 61 } 62 63 66 public void run(double frac) { 67 Graph g = (Graph)m_vis.getGroup(m_group); 68 initSchema(g.getNodes()); 69 70 Point2D anchor = getLayoutAnchor(); 71 NodeItem n = getLayoutRoot(); 72 layout(n,anchor.getX(),anchor.getY()); 73 } 74 75 private void layout(NodeItem n, double x, double y) { 76 firstWalk(n); 77 secondWalk(n,null,x,y,1,0); 78 } 79 80 private void firstWalk(NodeItem n) { 81 Params np = getParams(n); 82 np.d = 0; 83 double s = 0; 84 Iterator childIter = n.children(); 85 while ( childIter.hasNext() ) { 86 NodeItem c = (NodeItem)childIter.next(); 87 if ( !c.isVisible() ) continue; 88 firstWalk(c); 89 Params cp = getParams(c); 90 np.d = Math.max(np.d,cp.r); 91 cp.a = Math.atan(((double)cp.r)/(np.d+cp.r)); 92 s += cp.a; 93 } 94 adjustChildren(np, s); 95 setRadius(np); 96 } 97 98 private void adjustChildren(Params np, double s) { 99 if ( s > Math.PI ) { 100 np.c = Math.PI/s; 101 np.f = 0; 102 } else { 103 np.c = 1; 104 np.f = Math.PI - s; 105 } 106 } 107 108 private void setRadius(Params np) { 109 np.r = Math.max(np.d,m_minRadius) + 2*np.d; 110 } 111 112 182 183 private void secondWalk(NodeItem n, NodeItem r, 184 double x, double y, double l, double t) 185 { 186 setX(n, r, x); 187 setY(n, r, y); 188 189 Params np = getParams(n); 190 int numChildren = 0; 191 Iterator childIter = n.children(); 192 while ( childIter.hasNext() ) { 193 NodeItem c = (NodeItem)childIter.next(); 194 if ( c.isVisible() ) ++numChildren; 195 } 196 double dd = l*np.d; 197 double p = t + Math.PI; 198 double fs = (numChildren==0 ? 0 : np.f/numChildren); 199 double pr = 0; 200 childIter = n.children(); 201 while ( childIter.hasNext() ) { 202 NodeItem c = (NodeItem)childIter.next(); 203 if ( !c.isVisible() ) continue; 204 Params cp = getParams(c); 205 double aa = np.c * cp.a; 206 double rr = np.d * Math.tan(aa)/(1-Math.tan(aa)); 207 p += pr + aa + fs; 208 double xx = (l*rr+dd)*Math.cos(p); 209 double yy = (l*rr+dd)*Math.sin(p); 210 pr = aa; 211 secondWalk(c, n, x+xx, y+yy, l*np.c, p); 212 } 213 } 214 215 218 221 public static final String PARAMS = "_balloonTreeLayoutParams"; 222 225 public static final Schema PARAMS_SCHEMA = new Schema(); 226 static { 227 PARAMS_SCHEMA.addColumn(PARAMS, Params.class); 228 } 229 230 private void initSchema(TupleSet ts) { 231 try { 232 ts.addColumns(PARAMS_SCHEMA); 233 } catch ( IllegalArgumentException iae ) {} 234 } 235 236 private Params getParams(NodeItem n) { 237 Params np = (Params)n.get(PARAMS); 238 if ( np == null ) { 239 np = new Params(); 240 n.set(PARAMS, np); 241 } 242 return np; 243 } 244 245 248 public static class Params { 249 public int d; 250 public int r; 251 public double rx, ry; 252 public double a; 253 public double c; 254 public double f; 255 } 256 257 } | Popular Tags |