1 package prefuse.action.layout.graph; 2 3 import java.awt.geom.Rectangle2D ; 4 import java.util.ArrayList ; 5 import java.util.Collections ; 6 import java.util.Comparator ; 7 import java.util.Iterator ; 8 import java.util.List ; 9 10 import prefuse.data.Graph; 11 import prefuse.data.Schema; 12 import prefuse.data.tuple.TupleSet; 13 import prefuse.data.util.TreeNodeIterator; 14 import prefuse.visual.NodeItem; 15 import prefuse.visual.VisualItem; 16 17 18 42 public class SquarifiedTreeMapLayout extends TreeLayout { 43 44 public static final String AREA = "_area"; 46 public static final Schema AREA_SCHEMA = new Schema(); 47 static { 48 AREA_SCHEMA.addColumn(AREA, double.class); 49 } 50 51 private static Comparator s_cmp = new Comparator () { 52 public int compare(Object o1, Object o2) { 53 double s1 = ((VisualItem)o1).getDouble(AREA); 54 double s2 = ((VisualItem)o2).getDouble(AREA); 55 return ( s1>s2 ? 1 : (s1<s2 ? -1 : 0)); 56 } 57 }; 58 private ArrayList m_kids = new ArrayList (); 59 private ArrayList m_row = new ArrayList (); 60 private Rectangle2D m_r = new Rectangle2D.Double (); 61 62 private double m_frame; 64 69 public SquarifiedTreeMapLayout(String group) { 70 this(group, 0); 71 } 72 73 80 public SquarifiedTreeMapLayout(String group, double frame) { 81 super(group); 82 setFrameWidth(frame); 83 } 84 85 93 public void setFrameWidth(double frame) { 94 if ( frame < 0 ) 95 throw new IllegalArgumentException ( 96 "Frame value must be greater than or equal to 0."); 97 m_frame = frame; 98 } 99 100 105 public double getFrameWidth() { 106 return m_frame; 107 } 108 109 112 public void run(double frac) { 113 NodeItem root = getLayoutRoot(); 115 Rectangle2D b = getLayoutBounds(); 116 m_r.setRect(b.getX(), b.getY(), b.getWidth()-1, b.getHeight()-1); 117 118 computeAreas(root); 120 121 setX(root, null, 0); 123 setY(root, null, 0); 124 root.setBounds(0, 0, m_r.getWidth(), m_r.getHeight()); 125 126 updateArea(root, m_r); 128 layout(root, m_r); 129 } 130 131 134 private void computeAreas(NodeItem root) { 135 int leafCount = 0; 136 137 Graph g = (Graph)m_vis.getGroup(m_group); 139 TupleSet nodes = g.getNodes(); 140 nodes.addColumns(AREA_SCHEMA); 141 142 Iterator iter = new TreeNodeIterator(root); 144 while ( iter.hasNext() ) { 145 NodeItem n = (NodeItem)iter.next(); 146 n.setDouble(AREA, 0); 147 } 148 149 iter = new TreeNodeIterator(root); 151 while ( iter.hasNext() ) { 152 NodeItem n = (NodeItem)iter.next(); 153 if ( n.getChildCount() == 0 ) { 154 double sz = n.getSize(); 155 n.setDouble(AREA, sz); 156 NodeItem p = (NodeItem)n.getParent(); 157 for (; p!=null; p=(NodeItem)p.getParent()) 158 p.setDouble(AREA, sz + p.getDouble(AREA)); 159 ++leafCount; 160 } 161 } 162 163 Rectangle2D b = getLayoutBounds(); 165 double area = (b.getWidth()-1)*(b.getHeight()-1); 166 double scale = area/root.getDouble(AREA); 167 iter = new TreeNodeIterator(root); 168 while ( iter.hasNext() ) { 169 NodeItem n = (NodeItem)iter.next(); 170 n.setDouble(AREA, n.getDouble(AREA)*scale); 171 } 172 } 173 174 177 private void layout(NodeItem p, Rectangle2D r) { 178 Iterator childIter = p.children(); 180 while ( childIter.hasNext() ) 181 m_kids.add(childIter.next()); 182 Collections.sort(m_kids, s_cmp); 183 184 double w = Math.min(r.getWidth(),r.getHeight()); 186 squarify(m_kids, m_row, w, r); 187 m_kids.clear(); 189 childIter = p.children(); 191 while ( childIter.hasNext() ) { 192 NodeItem c = (NodeItem)childIter.next(); 193 if ( c.getChildCount() > 0 ) { 194 updateArea(c,r); 195 layout(c, r); 196 } 197 } 198 } 199 200 private void updateArea(NodeItem n, Rectangle2D r) { 201 Rectangle2D b = n.getBounds(); 202 if ( m_frame == 0.0 ) { 203 r.setRect(b); 205 return; 206 } 207 208 double dA = 2*m_frame*(b.getWidth()+b.getHeight()-2*m_frame); 210 double A = n.getDouble(AREA) - dA; 211 212 double s = 0; 214 Iterator childIter = n.children(); 215 while ( childIter.hasNext() ) 216 s += ((NodeItem)childIter.next()).getDouble(AREA); 217 double t = A/s; 218 219 childIter = n.children(); 221 while ( childIter.hasNext() ) { 222 NodeItem c = (NodeItem)childIter.next(); 223 c.setDouble(AREA, c.getDouble(AREA)*t); 224 } 225 226 r.setRect(b.getX()+m_frame, b.getY()+m_frame, 228 b.getWidth()-2*m_frame, b.getHeight()-2*m_frame); 229 return; 230 } 231 232 private void squarify(List c, List row, double w, Rectangle2D r) { 233 double worst = Double.MAX_VALUE, nworst; 234 int len; 235 236 while ( (len=c.size()) > 0 ) { 237 VisualItem item = (VisualItem) c.get(len-1); 239 double a = item.getDouble(AREA); 240 if (a <= 0.0) { 241 c.remove(len-1); 242 continue; 243 } 244 row.add(item); 245 246 nworst = worst(row, w); 247 if ( nworst <= worst ) { 248 c.remove(len-1); 249 worst = nworst; 250 } else { 251 row.remove(row.size()-1); r = layoutRow(row, w, r); w = Math.min(r.getWidth(),r.getHeight()); row.clear(); worst = Double.MAX_VALUE; 256 } 257 } 258 if ( row.size() > 0 ) { 259 r = layoutRow(row, w, r); row.clear(); } 262 } 263 264 private double worst(List rlist, double w) { 265 double rmax = Double.MIN_VALUE, rmin = Double.MAX_VALUE, s = 0.0; 266 Iterator iter = rlist.iterator(); 267 while ( iter.hasNext() ) { 268 double r = ((VisualItem)iter.next()).getDouble(AREA); 269 rmin = Math.min(rmin, r); 270 rmax = Math.max(rmax, r); 271 s += r; 272 } 273 s = s*s; w = w*w; 274 return Math.max(w*rmax/s, s/(w*rmin)); 275 } 276 277 private Rectangle2D layoutRow(List row, double w, Rectangle2D r) { 278 double s = 0; Iterator rowIter = row.iterator(); 280 while ( rowIter.hasNext() ) 281 s += ((VisualItem)rowIter.next()).getDouble(AREA); 282 double x = r.getX(), y = r.getY(), d = 0; 283 double h = w==0 ? 0 : s/w; 284 boolean horiz = (w == r.getWidth()); 285 286 rowIter = row.iterator(); 288 while ( rowIter.hasNext() ) { 289 NodeItem n = (NodeItem)rowIter.next(); 290 NodeItem p = (NodeItem)n.getParent(); 291 if ( horiz ) { 292 setX(n, p, x+d); 293 setY(n, p, y); 294 } else { 295 setX(n, p, x); 296 setY(n, p, y+d); 297 } 298 double nw = n.getDouble(AREA)/h; 299 if ( horiz ) { 300 setNodeDimensions(n,nw,h); 301 d += nw; 302 } else { 303 setNodeDimensions(n,h,nw); 304 d += nw; 305 } 306 } 307 if ( horiz ) 309 r.setRect(x,y+h,r.getWidth(),r.getHeight()-h); 310 else 311 r.setRect(x+h,y,r.getWidth()-h,r.getHeight()); 312 return r; 313 } 314 315 private void setNodeDimensions(NodeItem n, double w, double h) { 316 n.setBounds(n.getX(), n.getY(), w, h); 317 } 318 319 } | Popular Tags |