KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > action > layout > graph > BalloonTreeLayout


1 package prefuse.action.layout.graph;
2
3 import java.awt.geom.Point2D JavaDoc;
4 import java.util.Iterator JavaDoc;
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 /**
13  * <p>Layout that computes a circular "balloon-tree" layout of a tree.
14  * This layout places children nodes radially around their parents, and is
15  * equivalent to a top-down flattened view of a ConeTree.</p>
16  *
17  * <p>The algorithm used is that of G. Melançon and I. Herman from their
18  * research paper Circular Drawings of Rooted Trees, Reports of the Centre for
19  * Mathematics and Computer Sciences, Report Number INS–9817, 1998.</p>
20  *
21  * @author <a HREF="http://jheer.org">jeffrey heer</a>
22  */

23 public class BalloonTreeLayout extends TreeLayout {
24
25     private int m_minRadius = 2;
26     
27     /**
28      * Create a new BalloonTreeLayout
29      * @param group the data group to layout. Must resolve to a Graph
30      * or Tree instance.
31      */

32     public BalloonTreeLayout(String JavaDoc group) {
33         this(group, 2);
34     }
35
36     /**
37      * Create a new BalloonTreeLayout
38      * @param group the data group to layout. Must resolve to a Graph
39      * or Tree instance.
40      * @param minRadius the minimum radius to use for a layout circle
41      */

42     public BalloonTreeLayout(String JavaDoc group, int minRadius) {
43         super(group);
44         m_minRadius = minRadius;
45     }
46
47     /**
48      * Get the minimum radius used for a layout circle.
49      * @return the minimum layout radius
50      */

51     public int getMinRadius() {
52         return m_minRadius;
53     }
54     
55     /**
56      * Set the minimum radius used for a layout circle.
57      * @param minRadius the minimum layout radius
58      */

59     public void setMinRadius(int minRadius) {
60         m_minRadius = minRadius;
61     }
62     
63     /**
64      * @see prefuse.action.Action#run(double)
65      */

66     public void run(double frac) {
67         Graph g = (Graph)m_vis.getGroup(m_group);
68         initSchema(g.getNodes());
69         
70         Point2D JavaDoc 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 JavaDoc 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 /*
113     private void setRadius(NodeItem n, ParamBlock np) {
114         int numChildren = n.getChildCount();
115         double p = Math.PI;
116         double fs = (numChildren==0 ? 0 : np.f/numChildren);
117         double pr = 0;
118         double bx = 0, by = 0;
119         Iterator childIter = n.getChildren();
120         while ( childIter.hasNext() ) {
121             NodeItem c = (NodeItem)childIter.next();
122             ParamBlock cp = getParams(c);
123             p += pr + cp.a + fs;
124             bx += (cp.r)*Math.cos(p);
125             by += (cp.r)*Math.sin(p);
126             pr = cp.a;
127         }
128         if ( numChildren != 0 ) {
129             bx /= numChildren;
130             by /= numChildren;
131         }
132         np.rx = -bx;
133         np.ry = -by;
134         
135         p = Math.PI;
136         pr = 0;
137         np.r = 0;
138         childIter = n.getChildren();
139         while ( childIter.hasNext() ) {
140             NodeItem c = (NodeItem)childIter.next();
141             ParamBlock cp = getParams(c);
142             p += pr + cp.a + fs;
143             double x = cp.r*Math.cos(p)-bx;
144             double y = cp.r*Math.sin(p)-by;
145             double d = Math.sqrt(x*x+y*y) + cp.r;
146             np.r = Math.max(np.r, (int)Math.round(d));
147             pr = cp.a;
148         }
149         if ( np.r == 0 )
150             np.r = m_minRadius + 2*np.d;
151     } //
152  
153     private void secondWalk2(NodeItem n, NodeItem r,
154             double x, double y, double l, double t)
155     {
156         ParamBlock np = getParams(n);
157         double cost = Math.cos(t);
158         double sint = Math.sin(t);
159         double nx = x + l*(np.rx*cost-np.ry*sint);
160         double ny = y + l*(np.rx*sint+np.ry*cost);
161         setLocation(n,r,nx,ny);
162         double dd = l*np.d;
163         double p = Math.PI;
164         double fs = np.f / (n.getChildCount()+1);
165         double pr = 0;
166         Iterator childIter = n.getChildren();
167         while ( childIter.hasNext() ) {
168             NodeItem c = (NodeItem)childIter.next();
169             ParamBlock cp = getParams(c);
170             double aa = np.c * cp.a;
171             double rr = np.d * Math.tan(aa)/(1-Math.tan(aa));
172             p += pr + aa + fs;
173             double xx = (l*rr+dd)*Math.cos(p)+np.rx;
174             double yy = (l*rr+dd)*Math.sin(p)+np.ry;
175             double x2 = xx*cost - yy*sint;
176             double y2 = xx*sint + yy*cost;
177             pr = aa;
178             secondWalk2(c, n, x+x2, y+y2, l*rr/cp.r, p);
179         }
180     } //
181 */

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 JavaDoc 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/*l*rr/cp.r*/, p);
212         }
213     }
214     
215     // ------------------------------------------------------------------------
216
// Parameters
217

218     /**
219      * The data field in which the parameters used by this layout are stored.
220      */

221     public static final String JavaDoc PARAMS = "_balloonTreeLayoutParams";
222     /**
223      * The schema for the parameters used by this layout.
224      */

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 JavaDoc 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     /**
246      * Wrapper class holding parameters used for each node in this layout.
247      */

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 } // end of class BalloonTreeLayout
258
Popular Tags