/* 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 sim.portrayal3d.*; import sim.portrayal.*; import sim.util.*; import java.awt.*; import sim.field.network.*; import javax.vecmath.*; import javax.media.j3d.*; /* * A simple portrayal for edges in a network field. */ public class SimpleEdgePortrayal3D extends SimplePortrayal3D { public Color fromColor; public Color toColor; public Color labelColor; public Font labelFont; public boolean showLabels; public void setShowLabels(boolean val){showLabels = val;} public boolean getShowLabels(){return showLabels;} public SimpleEdgePortrayal3D() { this(Color.gray, Color.gray, Color.white); } /** If fromPaint == toPaint, one single color line will be drawn, and if labelPaint is null, no label is drawn. */ public SimpleEdgePortrayal3D(Color edgeColor, Color labelColor) { this(edgeColor, edgeColor, labelColor, new Font("SansSerif", Font.PLAIN, 60)); } public SimpleEdgePortrayal3D(Color fromColor, Color toColor, Color labelColor) { this(fromColor, toColor, labelColor, new Font("SansSerif", Font.PLAIN, 60)); } /** * If fromPaint == toPaint, one single color line will be drawn, and if * labelPaint is null, no label is drawn. */ public SimpleEdgePortrayal3D(Color fromColor, Color toColor, Color labelColor, Font labelFont) { this.fromColor = fromColor; this.toColor = toColor; this.labelColor = labelColor; this.labelFont = labelFont; } static Transform3D transformForOffset(double x, double y, double z) { Transform3D offset = new Transform3D(); offset.setTranslation(new Vector3d(x, y, z)); return offset; } static Transform3D transformForOffset(double[] xyz) { Transform3D offset = new Transform3D(); offset.setTranslation(new Vector3d(xyz)); return offset; } static Transform3D transformForOffset(float x, float y, float z) { Transform3D offset = new Transform3D(); offset.setTranslation(new Vector3f(x, y, z)); return offset; } /** * Returns a name appropriate for the edge. By default, this returns * (edge.info == null ? "" : "" + edge.info). Override this to make a more * customized label to display for the edge on-screen. */ public String getLabel(Edge edge) { Object obj = edge.info; if (obj == null) return ""; return "" + obj; } double[] startPoint = new double[3]; double[] endPoint = new double[3]; double[] middlePoint = new double[3]; public TransformGroup getModel(Object object, TransformGroup j3dModel) { Double3D firstPoint; Double3D secondPoint; SpatialNetwork3D field; EdgeWrapper drawInfo; Transform3D trans = null; com.sun.j3d.utils.geometry.Text2D tempText; drawInfo = (EdgeWrapper) object; field = (SpatialNetwork3D) drawInfo.fieldPortrayal.getField(); secondPoint = field.getObjectLocation(drawInfo.edge.to()); firstPoint = field.getObjectLocation(drawInfo.edge.from()); startPoint[0] = firstPoint.x; startPoint[1] = firstPoint.y; startPoint[2] = firstPoint.z; endPoint[0] = secondPoint.x; endPoint[1] = secondPoint.y; endPoint[2] = secondPoint.z; middlePoint[0] = (secondPoint.x + firstPoint.x) / 2; middlePoint[1] = (secondPoint.y + firstPoint.y) / 2; middlePoint[2] = (secondPoint.z + firstPoint.z) / 2; if (showLabels) trans = transformForOffset(middlePoint); if (j3dModel == null) { // build the whole model from scratch j3dModel = new TransformGroup(); j3dModel.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND); LineArray lineGeometry1 = new LineArray(2, GeometryArray.COORDINATES); lineGeometry1.setCoordinate(0, startPoint); lineGeometry1.setCoordinate(1, middlePoint); lineGeometry1.setCapability(GeometryArray.ALLOW_COORDINATE_WRITE); Shape3D lineShape1 = new Shape3D(lineGeometry1, SimplePortrayal3D.appearanceForColor(fromColor)); lineShape1.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); setPickableFlags(lineShape1); lineShape1.setUserData(drawInfo); j3dModel.addChild(lineShape1); LineArray lineGeometry2 = new LineArray(2, GeometryArray.COORDINATES); lineGeometry2.setCoordinate(0, middlePoint); lineGeometry2.setCoordinate(1, endPoint); lineGeometry2.setCapability(GeometryArray.ALLOW_COORDINATE_WRITE); Shape3D lineShape2 = new Shape3D(lineGeometry2, SimplePortrayal3D.appearanceForColor(toColor)); lineShape2.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); setPickableFlags(lineShape2); lineShape2.setUserData(drawInfo); j3dModel.addChild(lineShape2); // draw the edge labels if the user wants if (showLabels) { String str = getLabel(drawInfo.edge); com.sun.j3d.utils.geometry.Text2D text = new com.sun.j3d.utils.geometry.Text2D( str, new Color3f(labelColor), labelFont.getFamily(), labelFont.getSize(), labelFont.getStyle()); text.setRectangleScaleFactor(1.0f / 16.0f); OrientedShape3D o3d = new OrientedShape3D(text.getGeometry(), text.getAppearance(), OrientedShape3D.ROTATE_ABOUT_POINT, new Point3f(0, 0, 0)); o3d.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE); // may need // to change // the // appearance // (see // below) o3d.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); // may need // to change // the // geometry // (see // below) o3d.clearCapabilityIsFrequent(Shape3D.ALLOW_APPEARANCE_WRITE); o3d.clearCapabilityIsFrequent(Shape3D.ALLOW_GEOMETRY_WRITE); // make the offset TransformGroup TransformGroup o = new TransformGroup(); o.setCapability(TransformGroup.ALLOW_CHILDREN_READ); o.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); o.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); o.clearCapabilityIsFrequent(TransformGroup.ALLOW_CHILDREN_READ); o.setTransform(trans); o.setUserData(str); // the label shouldn't be pickable -- we'll turn this off in the // TransformGroup clearPickableFlags(o); o.addChild(o3d); // Add label to the offset TransformGroup j3dModel.addChild(o); tempText = new com.sun.j3d.utils.geometry.Text2D("", new Color3f(labelColor), labelFont.getFamily(), labelFont.getSize(), labelFont.getStyle()); // tempText = new Text3D(new Font3D(labelFont, new // FontExtrusion()), ""); tempText.setCapability(Appearance.ALLOW_TEXTURE_WRITE); tempText.setCapability(Appearance.ALLOW_TEXTURE_READ); } } else { Shape3D shape = (Shape3D)j3dModel.getChild(0); LineArray geo = (LineArray)shape.getGeometry(); geo.setCoordinate(0, startPoint); geo.setCoordinate(1, middlePoint); shape = (Shape3D)j3dModel.getChild(1); geo = (LineArray)shape.getGeometry(); geo.setCoordinate(0, startPoint); geo.setCoordinate(1, endPoint); if (showLabels) { TransformGroup tg = (TransformGroup) j3dModel.getChild(2); String str = getLabel(drawInfo.edge); // see if the label has changed? if (!tg.getUserData().equals(str)) { // ugh. This is really slow. Using the Shape3D results in // huge text, so, the default // value has to be changed in the constructor. // make the text again com.sun.j3d.utils.geometry.Text2D text = new com.sun.j3d.utils.geometry.Text2D( str, new Color3f(labelColor), labelFont.getFamily(), labelFont.getSize(), labelFont.getStyle()); text.setRectangleScaleFactor(1.0f / 16.0f); // Shape3D text = new Shape3D(new Text3D(new // Font3D(labelFont, new FontExtrusion()), str)); // Grab the OrientedShape3D OrientedShape3D o3d = (OrientedShape3D) (tg.getChild(0)); // update its geometry and appearance to reflect the new // text. o3d.setGeometry(text.getGeometry()); o3d.setAppearance(text.getAppearance()); // update user data to reflect the new text tg.setUserData(str); } // update the position of the text tg.setTransform(trans); } } return j3dModel; } public String getName(LocationWrapper wrapper) { // indicate it's an edge return "Edge: " + super.getName(wrapper); } public static class EdgeWrapper extends LocationWrapper { // we keep this around so we don't keep allocating MutableDoubles // every time getObject is called -- that's wasteful, but more // importantly, // it causes the inspector to load its property inspector entirely // again, // which will cause some flashing... MutableDouble val = null; SpatialNetwork3D field; public EdgeWrapper(FieldPortrayal fieldPortrayal, Edge edge) { super(edge.info, edge, fieldPortrayal); this.edge = edge; field = (SpatialNetwork3D)(fieldPortrayal.getField()); } public String getLocationName() { Edge edge = (Edge)getLocation(); if (field != null && field.network != null) { // do I still exist in the field? Check the from() value Bag b = field.network.getEdgesOut(edge.from()); // if (b != null) // no longer necessary for(int x=0;x<b.numObjs;x++) if (b.objs[x] == edge) return "" + edge.from() + " --> " + edge.to(); } return "Gone. Was: " + edge.from() + " --> " + edge.to(); } /* public String toString() { return "" + edge.info; } public String getLocationName() { SpatialNetwork3D field = (SpatialNetwork3D) fieldPortrayal.getField(); if (field != null && field.network != null) { // do I still exist in the field? Check the from() value Bag b = field.network.getEdgesOut(edge.from()); for (int x = 0; x < b.numObjs; x++) if (b.objs[x] == edge) return "" + edge.from() + " --> " + edge.to(); } return "Gone. Was: " + edge.from() + " --> " + edge.to(); } public Object getObject() { return edge; } */ public Edge edge; } }