/* 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.simple; import com.sun.j3d.utils.geometry.*; import java.awt.*; import javax.media.j3d.*; import javax.vecmath.*; import sim.util.*; import sim.portrayal3d.SimplePortrayal3D; /** * * @author Gabriel Balan * * This file was initially taken from http://www.j3d.org/downloads/Arrow.java * * and heavily modified */ public class Arrow extends TransformGroup { static final Color DEFAULT_ARROW_COLOR = Color.gray; static final Font3D DEFAULT_FONT3D = new Font3D(new Font(null, Font.PLAIN, 1), null); Cone arrowHead; Cylinder arrowTail; public Cylinder getArrowTail() { return arrowTail; } public Cone getArrowHead() { return arrowHead; } /** * Creates a 3D arrow between points <code>startPoint</code> and * <code>endPoint</code> if either label is not null, it adds a Text2D object * at the appropriate end. */ public Arrow(double arrowTailRadius, Double3D startPoint, Double3D endPoint, String stLabel, String endLabel, Appearance appearance) { Vector3d stPt = new Vector3d(startPoint.x, startPoint.y, startPoint.z); Vector3d endPt = new Vector3d(endPoint.x, endPoint.y, endPoint.z); Vector3d v = new Vector3d(stPt); v.negate(); v.add(new Vector3d(endPt)); // v= start -> end float arrowLen = (float) v.length(); float arrowHeadLen = 5.0f * (float)arrowTailRadius; float arrowHeadMaxRadius = 3.0f * (float)arrowTailRadius; float cylinderLen = arrowLen - arrowHeadLen; if(cylinderLen<0) { //this is a short arrow, //I need a different formula arrowHeadLen = arrowLen/16; cylinderLen = arrowLen - arrowHeadLen; } // Apperance for the arrow Appearance caAppearance = appearance; if(caAppearance==null) { caAppearance = SimplePortrayal3D.appearanceForColors(DEFAULT_ARROW_COLOR, null, DEFAULT_ARROW_COLOR, DEFAULT_ARROW_COLOR, 1.0f, 1.0f); } // Rotation Matrix for whole arrow (cylinder + cone) Transform3D caTransform = new Transform3D(); caTransform.setTranslation(stPt); Vector3d oy = new Vector3d(0, 1, 0); Vector3d axis = new Vector3d(); axis.cross(oy, v); //if v lies ofn Oy, then axis =[0,0,0]. No rotation is needed if(axis.length()!=0) { //TODO I should use rodrigues formula here caTransform.setRotation(new AxisAngle4d(axis, Math.asin(axis.length() / v.length()))); // axis.length() must be v.length() both doubles or floats, otherwise // you might get something bigger than 1. } caTransform.setTranslation(stPt); this.setTransform(caTransform); this.arrowTail = new Cylinder((float)arrowTailRadius, cylinderLen, caAppearance); Transform3D arrowCylinderTransform = new Transform3D(); arrowCylinderTransform.set(new Vector3f(0, cylinderLen / 2, 0)); TransformGroup arrowCylinderTransformGroup = new TransformGroup( arrowCylinderTransform); arrowCylinderTransformGroup.addChild(this.arrowTail); addChild(arrowCylinderTransformGroup); Transform3D arrowHeadTransform = new Transform3D(); arrowHeadTransform.set(new Vector3f(0, cylinderLen, 0)); TransformGroup arrowHeadTransformGroup = new TransformGroup( arrowHeadTransform); this.arrowHead = new Cone(arrowHeadMaxRadius, arrowHeadLen, 1, caAppearance); arrowHeadTransformGroup.addChild(this.arrowHead); this.addChild(arrowHeadTransformGroup); if (stLabel != null) { Text3D txt = new Text3D(DEFAULT_FONT3D, stLabel); OrientedShape3D os3d = new OrientedShape3D(txt, caAppearance, OrientedShape3D.ROTATE_ABOUT_POINT, new Point3f(0, 0, 0)); Transform3D t = new Transform3D(); t.setScale(5 * (float)arrowTailRadius); t.setTranslation(new Vector3f(0, -.1f, 0)); TransformGroup stLabelTG = new TransformGroup(t); stLabelTG.addChild(os3d); this.addChild(stLabelTG); } if (endLabel != null) { Text3D txt = new Text3D(DEFAULT_FONT3D, endLabel); OrientedShape3D os3d = new OrientedShape3D(txt, caAppearance, OrientedShape3D.ROTATE_ABOUT_POINT, new Point3f(0, arrowLen, 0)); Transform3D t = new Transform3D(); t.setScale(5 * (float)arrowTailRadius); t.setTranslation(new Vector3f(0, arrowLen + .1f, 0)); TransformGroup endLabelTG = new TransformGroup(t); endLabelTG.addChild(os3d); this.addChild(endLabelTG); } } }