/* 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.network; import java.util.HashMap; import java.util.Iterator; import sim.portrayal3d.*; import sim.portrayal.*; import sim.field.network.*; import sim.util.*; import javax.media.j3d.*; import com.sun.j3d.utils.picking.*; /** Portrays network fields. Only draws the edges. To draw the nodes, use a ContinuousPortrayal2D or SparseGridPortrayal2D. */ public class NetworkPortrayal3D extends FieldPortrayal3D { // a line with a label SimpleEdgePortrayal3D defaultPortrayal = new SimpleEdgePortrayal3D(); public Portrayal getDefaultPortrayal() { return defaultPortrayal; } public void setField(Object field) { if (field instanceof SpatialNetwork3D ) super.setField(field); else throw new RuntimeException("Invalid field for FieldPortrayal3D: " + field + ". You probably wanted a SpatialNetwork3D."); } public Object getField() { return field; } public TransformGroup createModel() { TransformGroup globalTG = new TransformGroup(); globalTG.setCapability(TransformGroup.ALLOW_CHILDREN_READ); globalTG.setCapability(Group.ALLOW_CHILDREN_WRITE); globalTG.setCapability(Group.ALLOW_CHILDREN_EXTEND); SpatialNetwork3D field = (SpatialNetwork3D)this.field; if( field == null ) return globalTG; // draw ALL the edges -- one never knows if an edge will cross into our boundary Bag nodes = field.network.getAllNodes(); for(int x=0;x<nodes.numObjs;x++) { Bag edges = field.network.getEdgesOut(nodes.objs[x]); for(int y=0;y<edges.numObjs;y++) { Edge edge = (Edge)edges.objs[y]; globalTG.addChild(wrapModelForNewEdge(edge)); } } return globalTG; } /** * This function is called from createModel for each edge in the * field and from the updateModel part of getModel for the * new edges. * * <p>In order to dynamically add/remove the subtrees associated with * edges, this function wraps their TransformGroups into BranchGroups. **/ protected BranchGroup wrapModelForNewEdge(Edge edge) { LocationWrapper newwrapper = new LocationWrapper(edge.info, edge, this); Portrayal p = getPortrayalForObject(newwrapper); if (!(p instanceof SimpleEdgePortrayal3D)) throw new RuntimeException("Unexpected Portrayal " + p + " for object " + edge + " -- expected a SimpleEdgePortrayal3D"); SimpleEdgePortrayal3D portrayal = (SimpleEdgePortrayal3D) p; portrayal.setCurrentFieldPortrayal(this); TransformGroup localTG = portrayal.getModel(newwrapper, null); localTG.setCapability(Group.ALLOW_CHILDREN_READ); localTG.setUserData(newwrapper); BranchGroup localBG = new BranchGroup(); localBG.setCapability(BranchGroup.ALLOW_CHILDREN_READ); localBG.setCapability(BranchGroup.ALLOW_DETACH); localBG.addChild(localTG); localBG.setUserData(newwrapper); //I set the user data in both localBG, and localTG. return localBG; } public void updateModel(TransformGroup globalTG) { SpatialNetwork3D field = (SpatialNetwork3D)this.field; if (field == null) return; HashMap hm = new HashMap(); Network net = field.network; Bag nodes = net.getAllNodes(); for(int n=0;n<nodes.numObjs;n++) { Bag edges = net.getEdgesOut(nodes.objs[n]); for(int i=0;i<edges.numObjs;i++) { Object edge = edges.objs[i]; hm.put(edge,edge); } } // update children (edges) if they're still in the field (network), // else remove the children if they appear to have left. // We use a hashmap to efficiently mark out the children // as we delete them and update them for(int t= globalTG.numChildren()-1; t>=0; t--) { BranchGroup localBG = (BranchGroup)globalTG.getChild(t); LocationWrapper wrapper = (LocationWrapper)localBG.getUserData(); Object edge = wrapper.getLocation(); if(hm.remove(edge) != null) // hm.containsKey(edgeObj)) // object still in the field { // we can pull this off because valid edges can't be null -- Gabriel TransformGroup localTG = (TransformGroup)localBG.getChild(0); Portrayal p = getPortrayalForObject(wrapper); if(! (p instanceof SimplePortrayal3D)) throw new RuntimeException("Unexpected Portrayal " + p + " for object " + wrapper + " -- expecting a SimplePortrayal3D"); SimplePortrayal3D p3d = (SimplePortrayal3D)p; p3d.setCurrentFieldPortrayal(this); TransformGroup localTG2 = p3d.getModel(wrapper, localTG); if(localTG != localTG2) { localTG2.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); localTG2.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); localTG2.setUserData(wrapper); BranchGroup newlocalBG = new BranchGroup(); newlocalBG.setCapability(BranchGroup.ALLOW_CHILDREN_READ); newlocalBG.setCapability(BranchGroup.ALLOW_DETACH); newlocalBG.setUserData(wrapper); 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 edges 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(); // yuck, inefficient while(newObjs.hasNext()) { Edge edge = (Edge)newObjs.next(); globalTG.addChild(wrapModelForNewEdge(edge)); } } } public LocationWrapper completedWrapper(LocationWrapper w, PickIntersection pi, PickResult pr) { return w; } }