1 package prefuse.util.force; 2 3 import java.util.ArrayList ; 4 import java.util.Arrays ; 5 import java.util.Iterator ; 6 import java.util.Random ; 7 8 27 public class NBodyForce extends AbstractForce { 28 29 35 36 private static String [] pnames = new String [] { "GravitationalConstant", 37 "Distance", "BarnesHutTheta" }; 38 39 public static final float DEFAULT_GRAV_CONSTANT = -1.0f; 40 public static final float DEFAULT_MIN_GRAV_CONSTANT = -10f; 41 public static final float DEFAULT_MAX_GRAV_CONSTANT = 10f; 42 43 public static final float DEFAULT_DISTANCE = -1f; 44 public static final float DEFAULT_MIN_DISTANCE = -1f; 45 public static final float DEFAULT_MAX_DISTANCE = 500f; 46 47 public static final float DEFAULT_THETA = 0.9f; 48 public static final float DEFAULT_MIN_THETA = 0.0f; 49 public static final float DEFAULT_MAX_THETA = 1.0f; 50 51 public static final int GRAVITATIONAL_CONST = 0; 52 public static final int MIN_DISTANCE = 1; 53 public static final int BARNES_HUT_THETA = 2; 54 55 private float xMin, xMax, yMin, yMax; 56 private QuadTreeNodeFactory factory = new QuadTreeNodeFactory(); 57 private QuadTreeNode root; 58 59 private Random rand = new Random (12345678L); 61 64 public NBodyForce() { 65 this(DEFAULT_GRAV_CONSTANT, DEFAULT_DISTANCE, DEFAULT_THETA); 66 } 67 68 79 public NBodyForce(float gravConstant, float minDistance, float theta) { 80 params = new float[] { gravConstant, minDistance, theta }; 81 minValues = new float[] { DEFAULT_MIN_GRAV_CONSTANT, 82 DEFAULT_MIN_DISTANCE, DEFAULT_MIN_THETA }; 83 maxValues = new float[] { DEFAULT_MAX_GRAV_CONSTANT, 84 DEFAULT_MAX_DISTANCE, DEFAULT_MAX_THETA }; 85 root = factory.getQuadTreeNode(); 86 } 87 88 92 public boolean isItemForce() { 93 return true; 94 } 95 96 99 protected String [] getParameterNames() { 100 return pnames; 101 } 102 103 110 private void setBounds(float xMin, float yMin, float xMax, float yMax) { 111 this.xMin = xMin; 112 this.yMin = yMin; 113 this.xMax = xMax; 114 this.yMax = yMax; 115 } 116 117 120 public void clear() { 121 clearHelper(root); 122 root = factory.getQuadTreeNode(); 123 } 124 125 private void clearHelper(QuadTreeNode n) { 126 for ( int i = 0; i < n.children.length; i++ ) { 127 if ( n.children[i] != null ) 128 clearHelper(n.children[i]); 129 } 130 factory.reclaim(n); 131 } 132 133 139 public void init(ForceSimulator fsim) { 140 clear(); 142 float x1 = Float.MAX_VALUE, y1 = Float.MAX_VALUE; 144 float x2 = Float.MIN_VALUE, y2 = Float.MIN_VALUE; 145 Iterator itemIter = fsim.getItems(); 146 while ( itemIter.hasNext() ) { 147 ForceItem item = (ForceItem)itemIter.next(); 148 float x = item.location[0]; 149 float y = item.location[1]; 150 if ( x < x1 ) x1 = x; 151 if ( y < y1 ) y1 = y; 152 if ( x > x2 ) x2 = x; 153 if ( y > y2 ) y2 = y; 154 } 155 float dx = x2-x1, dy = y2-y1; 156 if ( dx > dy ) { y2 = y1 + dx; } else { x2 = x1 + dy; } 157 setBounds(x1,y1,x2,y2); 158 159 itemIter = fsim.getItems(); 161 while ( itemIter.hasNext() ) { 162 ForceItem item = (ForceItem)itemIter.next(); 163 insert(item); 164 } 165 166 calcMass(root); 168 } 169 170 176 public void insert(ForceItem item) { 177 try { 179 insert(item, root, xMin, yMin, xMax, yMax); 180 } catch ( StackOverflowError e ) { 181 e.printStackTrace(); 183 } 184 } 185 186 private void insert(ForceItem p, QuadTreeNode n, 187 float x1, float y1, float x2, float y2) 188 { 189 if ( n.hasChildren ) { 192 insertHelper(p,n,x1,y1,x2,y2); 194 } else if ( n.value != null ) { 195 if ( isSameLocation(n.value, p) ) { 197 insertHelper(p,n,x1,y1,x2,y2); 198 } else { 199 ForceItem v = n.value; n.value = null; 200 insertHelper(v,n,x1,y1,x2,y2); 201 insertHelper(p,n,x1,y1,x2,y2); 202 } 203 } else { 204 n.value = p; 206 } 207 } 208 209 private static boolean isSameLocation(ForceItem f1, ForceItem f2) { 210 float dx = Math.abs(f1.location[0]-f2.location[0]); 211 float dy = Math.abs(f1.location[1]-f2.location[1]); 212 return ( dx < 0.01 && dy < 0.01 ); 213 } 214 215 private void insertHelper(ForceItem p, QuadTreeNode n, 216 float x1, float y1, float x2, float y2) 217 { 218 float x = p.location[0], y = p.location[1]; 219 float splitx = (x1+x2)/2; 220 float splity = (y1+y2)/2; 221 int i = (x>=splitx ? 1 : 0) + (y>=splity ? 2 : 0); 222 if ( n.children[i] == null ) { 224 n.children[i] = factory.getQuadTreeNode(); 225 n.hasChildren = true; 226 } 227 if ( i==1 || i==3 ) x1 = splitx; else x2 = splitx; 229 if ( i > 1 ) y1 = splity; else y2 = splity; 230 insert(p,n.children[i],x1,y1,x2,y2); 232 } 233 234 private void calcMass(QuadTreeNode n) { 235 float xcom = 0, ycom = 0; 236 n.mass = 0; 237 if ( n.hasChildren ) { 238 for ( int i=0; i < n.children.length; i++ ) { 239 if ( n.children[i] != null ) { 240 calcMass(n.children[i]); 241 n.mass += n.children[i].mass; 242 xcom += n.children[i].mass * n.children[i].com[0]; 243 ycom += n.children[i].mass * n.children[i].com[1]; 244 } 245 } 246 } 247 if ( n.value != null ) { 248 n.mass += n.value.mass; 249 xcom += n.value.mass * n.value.location[0]; 250 ycom += n.value.mass * n.value.location[1]; 251 } 252 n.com[0] = xcom / n.mass; 253 n.com[1] = ycom / n.mass; 254 } 255 256 260 public void getForce(ForceItem item) { 261 try { 262 forceHelper(item,root,xMin,yMin,xMax,yMax); 263 } catch ( StackOverflowError e ) { 264 e.printStackTrace(); 266 } 267 } 268 269 private void forceHelper(ForceItem item, QuadTreeNode n, 270 float x1, float y1, float x2, float y2) 271 { 272 float dx = n.com[0] - item.location[0]; 273 float dy = n.com[1] - item.location[1]; 274 float r = (float)Math.sqrt(dx*dx+dy*dy); 275 boolean same = false; 276 if ( r == 0.0f ) { 277 dx = (rand.nextFloat()-0.5f) / 50.0f; 279 dy = (rand.nextFloat()-0.5f) / 50.0f; 280 r = (float)Math.sqrt(dx*dx+dy*dy); 281 same = true; 282 } 283 boolean minDist = params[MIN_DISTANCE]>0f && r>params[MIN_DISTANCE]; 284 285 if ( (!n.hasChildren && n.value != item) || 289 (!same && (x2-x1)/r < params[BARNES_HUT_THETA]) ) 290 { 291 if ( minDist ) return; 292 float v = params[GRAVITATIONAL_CONST]*item.mass*n.mass 295 / (r*r*r); 296 item.force[0] += v*dx; 297 item.force[1] += v*dy; 298 } else if ( n.hasChildren ) { 299 float splitx = (x1+x2)/2; 301 float splity = (y1+y2)/2; 302 for ( int i=0; i<n.children.length; i++ ) { 303 if ( n.children[i] != null ) { 304 forceHelper(item, n.children[i], 305 (i==1||i==3?splitx:x1), (i>1?splity:y1), 306 (i==1||i==3?x2:splitx), (i>1?y2:splity)); 307 } 308 } 309 if ( minDist ) return; 310 if ( n.value != null && n.value != item ) { 311 float v = params[GRAVITATIONAL_CONST]*item.mass*n.value.mass 312 / (r*r*r); 313 item.force[0] += v*dx; 314 item.force[1] += v*dy; 315 } 316 } 317 } 318 319 322 public static final class QuadTreeNode { 323 public QuadTreeNode() { 324 com = new float[] {0.0f, 0.0f}; 325 children = new QuadTreeNode[4]; 326 } boolean hasChildren = false; 328 float mass; float[] com; ForceItem value; QuadTreeNode[] children; } 334 338 public static final class QuadTreeNodeFactory { 339 private int maxNodes = 50000; 340 private ArrayList nodes = new ArrayList (); 341 342 public QuadTreeNode getQuadTreeNode() { 343 if ( nodes.size() > 0 ) { 344 return (QuadTreeNode)nodes.remove(nodes.size()-1); 345 } else { 346 return new QuadTreeNode(); 347 } 348 } 349 public void reclaim(QuadTreeNode n) { 350 n.mass = 0; 351 n.com[0] = 0.0f; n.com[1] = 0.0f; 352 n.value = null; 353 n.hasChildren = false; 354 Arrays.fill(n.children, null); 355 if ( nodes.size() < maxNodes ) 356 nodes.add(n); 357 } 358 } 360 } | Popular Tags |