/* Copyright 2006 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.portrayal3d.grid; import sim.util.*; import sim.portrayal.*; import sim.portrayal3d.*; import sim.field.grid.*; import java.util.*; import javax.vecmath.*; import javax.media.j3d.*; /** * Displays objects in a SparseGrid2D along the XY grid in a special way. When multiple objects are * at the same location, SparseGrid2DPortrayal3D will stack them up in a column towards the positive Z axis. * The centers of stacked objects are separated by a value <b>zScale</b>, which by default is 1.0. * * @author Gabriel Balan * */ public class SparseGrid2DPortrayal3D extends SparseGridPortrayal3D { public double zScale; /** Creates a SparseGrid2DPortrayal3D with the provided scale */ public SparseGrid2DPortrayal3D(double zScale) { super(); this.zScale = zScale; } /** Creates a SparseGrid2DPortrayal3D with scale = 1.0 */ public SparseGrid2DPortrayal3D() { this(1.0); } public void setField(Object field) { if (field instanceof SparseGrid2D) super.setField(field); else throw new RuntimeException("Invalid field for StackedSparse2DPortrayal3D: " + field); } public TransformGroup createModel() { TransformGroup globalTG = new TransformGroup(); globalTG.setCapability(TransformGroup.ALLOW_CHILDREN_READ); globalTG.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE); globalTG.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND); if (field == null) return globalTG; Vector3d tmpV3D = new Vector3d(); Transform3D tmpLocalT = new Transform3D(); HashMap map = new HashMap(); SparseGrid2D grid = (SparseGrid2D)field; Bag allobjs = grid.getAllObjects(); for(int i = 0; i < allobjs.numObjs; i++) { if (!map.containsKey(allobjs.objs[i])) // not done yet { Int2D location = grid.getObjectLocation(allobjs.objs[i]); Bag b = grid.getObjectsAtLocation(location); tmpV3D.x = location.x; tmpV3D.y = location.y; if(b != null) for(int z = 0; z<b.numObjs; z++) { map.put(b.objs[z],b.objs[z]); // mark as already completed tmpV3D.z = z * zScale; tmpLocalT.setTranslation(tmpV3D); //wrap the TG in a BG so it can be removed dynamically BranchGroup localBG = wrapModelForNewObject(b.objs[z], tmpLocalT); globalTG.addChild(localBG); } } } return globalTG; } public void updateModel(TransformGroup globalTG) { SparseGrid2D grid = (SparseGrid2D)field; if (grid==null) return; Vector3d tmpV3D = new Vector3d(); Bag b = grid.getAllObjects(); Transform3D tmpLocalT = new Transform3D(); HashMap hm = new HashMap(); HashMap stackCountByLocation = new HashMap(); for(int i=0;i<b.numObjs;i++) hm.put(b.objs[i],b.objs[i]); for(int t= globalTG.numChildren()-1; t>=0; t--) { BranchGroup localBG = (BranchGroup)globalTG.getChild(t); Object fieldObj = localBG.getUserData(); if( hm.remove(fieldObj) != null) { TransformGroup localTG = (TransformGroup)localBG.getChild(0); Portrayal p = getPortrayalForObject(fieldObj); if(! (p instanceof SimplePortrayal3D)) throw new RuntimeException("Unexpected Portrayal " + p + " for object " + fieldObj + " -- expecting a SimplePortrayal3D"); SimplePortrayal3D p3d = (SimplePortrayal3D)p; p3d.setCurrentFieldPortrayal(this); TransformGroup localTG2 = p3d.getModel(fieldObj, localTG); // Vector3d locationV3d = getLocationOfObjectAsVector3d(fieldObj); Int2D location = grid.getObjectLocation(fieldObj); tmpV3D.x = location.x; tmpV3D.y = location.y; MutableDouble d = (MutableDouble)(stackCountByLocation.get(location)); if (d==null) { d = new MutableDouble(0); stackCountByLocation.put(location,d); } else d.val++; // set Z tmpV3D.z = d.val * zScale; tmpLocalT.setTranslation(tmpV3D); localTG2.setTransform(tmpLocalT); if(localTG != localTG2) { localTG2.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); localTG2.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); BranchGroup newlocalBG = new BranchGroup(); newlocalBG.setCapability(BranchGroup.ALLOW_CHILDREN_READ); newlocalBG.setCapability(BranchGroup.ALLOW_DETACH); newlocalBG.setUserData(fieldObj); newlocalBG.addChild(localTG2); globalTG.setChild(newlocalBG, t); } } else // object is no longer in the field -- remove it from the scenegraph globalTG.removeChild(t); } // The remaining objects in hm must be new. We add them to the scenegraph. // But first, we should check to see if hm is empty. if (!hm.isEmpty()) { Iterator newObjs = hm.values().iterator(); while(newObjs.hasNext()) { Object fieldObj = newObjs.next(); //Vector3d locationV3d = getLocationOfObjectAsVector3d(fieldObj); Int2D location = grid.getObjectLocation(fieldObj); tmpV3D.x = location.x; tmpV3D.y = location.y; MutableDouble d = (MutableDouble)(stackCountByLocation.get(location)); if (d==null) { d = new MutableDouble(0); stackCountByLocation.put(location,d); } else d.val++; // set Z tmpV3D.z = d.val * zScale; tmpLocalT.setTranslation(tmpV3D); //wrap the TG in a BG so it can be added //and later on removed dynamically BranchGroup localBG = wrapModelForNewObject(fieldObj, tmpLocalT); globalTG.addChild(localBG); } hm.clear(); } } }