// // ScreenAnnotatorUtils.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.bom.annotations; import visad.PlotText; import visad.TextControl; import visad.VisADException; import visad.VisADGeometryArray; import visad.java3d.VisADCanvasJ3D; import visad.java3d.DisplayImplJ3D; import visad.java3d.DisplayRendererJ3D; import visad.util.HersheyFont; import java.awt.Font; import java.awt.Image; import java.awt.image.ColorModel; import java.awt.image.PixelGrabber; import javax.media.j3d.Appearance; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.Font3D; import javax.media.j3d.GeometryArray; import javax.media.j3d.LineArray; import javax.media.j3d.LineAttributes; import javax.media.j3d.PointArray; import javax.media.j3d.PointAttributes; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Text3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TransparencyAttributes; import javax.media.j3d.TriangleArray; import javax.media.j3d.View; import javax.vecmath.Point2d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; /** * This is a collection of static methods to help with the construction * of java3D objects used for annotating a VisAD display. The objects are * intended to be drawn directly to the screen using screen pixel * locations. See {@link ScreenAnnotator}. * * This is meant to contain as much of the Java3D dependencies as posible. */ public class ScreenAnnotatorUtils { /** * Construct a {@link BranchGroup} object from a routine * description of a Label using {@link Text3D}. A label is a Text * string with attributes used for drawing it. * * @param display the VisAD display for this Label. * @param text the string. * @param x x screen coordinate of the text. * @param y y screen coordinate of the text. * @param colour red green blue triple; each value in [0.0, 1.0]. * @param font {@link java.awt.Font} to use. * @param zValue Virtual world value; larger z is in front. * @param fontSizeInPixels height of text. * @param align one of: <ul> * <li>Text3D.ALIGN_FIRST * <li>Text3D.ALIGN_CENTER * <li>Text3D.ALIGN_LAST </ul> * @param path one of: <ul> * <li>Text3D.PATH_RIGHT * <li>Text3D.PATH_LEFT * <li>Text3D.PATH_DOWN * <li>Text3D.PATH_UP </ul> * @return the {@link BranchGroup}, suitably scaled, representing the * label. */ public static BranchGroup makeJLabelShape3D(DisplayImplJ3D display, String text, int x, int y, float[] colour, Font font, double zValue, double fontSizeInPixels, int align, int path) { DisplayRendererJ3D renderer = (DisplayRendererJ3D)display.getDisplayRenderer(); VisADCanvasJ3D canvas = renderer.getCanvas(); // get the start point and one pixel up in vworld coordinates int[][] screenXY = { {x, x}, {y, y-1} // just one pixel up }; Point3d[] points = screenToVworld(screenXY, canvas); //adjust z ditherPoints(canvas, 0.4, points); adjustZ(display, canvas, zValue, points); // scale by distance in vworld representing one pixel double scale = fontSizeInPixels* (points[1].y - points[0].y)/font.getSize2D(); // make the Text3D geometry object Font3D font3D = new Font3D(font, null); // no extrusion // create at Origin - scale and move later Text3D text3D = new Text3D(font3D, text, new Point3f((float)0, (float)0, (float)0), align, path); // make some colours ColoringAttributes textColor = new ColoringAttributes(); textColor.setColor(colour[0], colour[1], colour[2]); Appearance textAppearance = new Appearance(); textAppearance.setColoringAttributes(textColor); Shape3D textShape = new Shape3D(text3D, textAppearance); // Build a Transformation // create at Origin, Scale and move to point Transform3D tMove = new Transform3D(); tMove.set(new Vector3d(points[0].x, points[0].y, points[0].z)); Transform3D tScale = new Transform3D(); tScale.set(scale); tMove.mul(tScale); TransformGroup tg = new TransformGroup(); Transform3D t3D = new Transform3D(); tg.setTransform(tMove); tg.addChild(textShape); BranchGroup bg = new BranchGroup(); bg.addChild(tg); return bg; } // makeJLabelShape3D /** * Construct a {@link Shape3D} object from a routine description * of a Label using 2D fonts. A label is a Text string with * attributes used drawing it. * * @param display the VisAD display for this Label. * @param text the string. * @param x x screen coordinate of the text. * @param y y screen coordinate of the text. * @param colour red green blue triple; each value in [0.0, 1.0]. * @param font {@link java.awt.Font} to use. * @param hfont Hershey font to use; if both fonts are null * then use the default VisAD line font. * @param zValue Virtual world value; larger z is in front. * @param scaleFactor scale the font by this factor; by default * characters are 1 pixel in size. * @param filled if <code>true</code> the font is rendered as filled, * if <code>false</code> just the triangles are drawn. * @param thickness line width to use if just drawing triangles; * usually 1.0 is the most useful. * @param orientation angle of rotation of the text anticlockwise * from the horizontal. * @param horizontal one of:<ul> * <li> TextControl.Justification.LEFT - Left justified * text (ie: normal text) * <li> TextControl.Justification.CENTER - Centered text * <li> TextControl.Justification.RIGHT - Right justified text * </ul> * @param vertical one of:<ul> * <li> TextControl.Justification.BOTTOM - Bottom justified * text (normal). * <li> TextControl.Justification.TOP - Top justified text * <li> TextControl.Justification.CENTER - Centered text * </ul> * @param charRotation rotate each character * <code>charRotation</code> degrees clockwise from base line. * * @return the Label description as a {@link Shape3D}. * * @throws VisADException VisAD couldn't make the geometry array. */ public static Shape3D makeLabelShape3D(DisplayImplJ3D display, String text, int x, int y, float[] colour, Font font, HersheyFont hfont, double zValue, double scaleFactor, boolean filled, double thickness, double orientation, TextControl.Justification horizontal, TextControl.Justification vertical, double charRotation) throws VisADException { Shape3D shape = null; DisplayRendererJ3D renderer = (DisplayRendererJ3D)display.getDisplayRenderer(); VisADCanvasJ3D canvas = renderer.getCanvas(); // set up some stuff in the Virtual World Point3d position1 = new Point3d(); Point3d position2 = new Point3d(); Point3d position3 = new Point3d(); canvas.getPixelLocationInImagePlate(x, y, position1); // possible different scale in x and y direction? // use default of 1 pixel for font height canvas.getPixelLocationInImagePlate(x+1, y, position2); double pixelInImagePlate = position2.x - position1.x; position2.x = position1.x + pixelInImagePlate* Math.cos(Math.toRadians(orientation)); position2.y = position1.y + pixelInImagePlate* Math.sin(Math.toRadians(orientation)); position2.z = position1.z; position3.x = position1.x - pixelInImagePlate* Math.sin(Math.toRadians(orientation)); position3.y = position1.y + pixelInImagePlate* Math.cos(Math.toRadians(orientation)); position3.z = position1.z; Transform3D t = new Transform3D(); canvas.getImagePlateToVworld(t); t.transform(position1); t.transform(position2); t.transform(position3); // adjust z Point3d[] textStart = {position1, position2, position3}; ditherPoints(canvas, 0.4, textStart); adjustZ(display, canvas, zValue, textStart); position1 = textStart[0]; position2 = textStart[1]; position3 = textStart[2]; double[] start = {(double) position1.x, (double) position1.y, (double) position1.z}; double[] base = {(double) (position2.x - position1.x), (double) (position2.y - position1.y), (double) (position2.z - position1.z)}; double[] up = {(double) (position3.x - position1.x), (double) (position3.y - position1.y), (double) (position3.z - position1.z)}; // scale is now in visad PlotText.java but do here // and put scale = 1 in call to PlotText for (int i=0; i<3; i++) { up[i] = scaleFactor*up[i]; base[i] = scaleFactor*base[i]; } // always use offset of zero. Just change x y if offset wanted double[] offset = {0.0, 0.0, 0.0}; VisADGeometryArray array = null; if (font != null) { // to be compatable with versions of VisAD with old // PlotText call the old interface if charRotation // is zero if (charRotation == 0.0) { array = PlotText.render_font(text, font, start, base, up, horizontal, vertical); } else { array = PlotText.render_font(text, font, start, base, up, horizontal, vertical, charRotation, 1.0, offset); } } else if (hfont != null) { if (charRotation == 0.0) { array = PlotText.render_font(text, hfont, start, base, up, horizontal, vertical); } else { array = PlotText.render_font(text, hfont, start, base, up, horizontal, vertical, charRotation, 1.0, offset); } } else { // font && hfont are null if (charRotation == 0.0) { array = PlotText.render_label(text, start, base, up, horizontal, vertical); } else { array = PlotText.render_label(text, start, base, up, horizontal, vertical, charRotation, 1.0, offset); } } GeometryArray geom = display.makeGeometry(array); Appearance appearance = new Appearance(); ColoringAttributes coloringAttributes = new ColoringAttributes(); coloringAttributes.setColor(colour[0], colour[1], colour[2]); appearance.setColoringAttributes(coloringAttributes); LineAttributes lineAttributes = new LineAttributes(); lineAttributes.setLineWidth((float)thickness); appearance.setLineAttributes(lineAttributes); PolygonAttributes polygonAttributes = new PolygonAttributes(); polygonAttributes.setCullFace(PolygonAttributes.CULL_NONE); // should be this by default but didn't render the font if (filled) { polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_FILL); } else { polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_LINE); } appearance.setPolygonAttributes(polygonAttributes); // change this to return a branch group return new Shape3D(geom, appearance); } /** * This adjusts a set of points so that the drawn object has the * same appearance even though the Z value is adjusted to a user * given setting. Important for Perspective view. * * @param display the VisAD display. * @param canvas Java3D canvas being drawn on * @param z Virtual world value; larger z is in front. * @param points array of values to have their z value adjusted. */ public static void adjustZ(DisplayImplJ3D display, Canvas3D canvas, double z, Point3d[] points) { if (display.getGraphicsModeControl().getProjectionPolicy() == View.PERSPECTIVE_PROJECTION) { Point3d leftEye = new Point3d(); Point3d rightEye = new Point3d(); canvas.getLeftEyeInImagePlate(leftEye); canvas.getRightEyeInImagePlate(rightEye); double xLoc = (leftEye.x + rightEye.x)/2.0; double yLoc = (leftEye.y + rightEye.y)/2.0; double zLoc = (leftEye.z + rightEye.z)/2.0; Point3d eye = new Point3d(xLoc, yLoc, zLoc); Transform3D t = new Transform3D(); canvas.getImagePlateToVworld(t); t.transform(eye); double a; for (int i=0; i<points.length; i++) { a = (z - points[i].z)/(eye.z - points[i].z); points[i].x = a*(eye.x - points[i].x) + points[i].x; points[i].y = a*(eye.y - points[i].y) + points[i].y; points[i].z = z; } } else { // PARALLEL_PROJECTION for (int i=0; i<points.length; i++) { points[i].z = z; } } } // adjustZ /** * Construct a {@link Shape3D} object from a routine description * of a Quadrilateral. * * @param display the VisAD display for this Quadrilateral. * @param style one of: <ul> * <li> QuadrilateralJ3D.FILL * <li> QuadrilateralJ3D.LINE * <li> QuadrilateralJ3D.POINT </ul> * @param x1 x screen coordinate of the first point. * @param y1 y screen coordinate of the first point. * @param x2 x screen coordinate of the second point. * @param y2 y screen coordinate of the second point. * @param x3 x screen coordinate of the third point. * @param y3 y screen coordinate of the third point. * @param x4 x screen coordinate of the fourth point. * @param y4 y screen coordinate of the fourth point. * @param colour red green blue triple; each value in [0.0, 1.0]. * @param z Virtual world value; larger z is in front. * @param thickness used for LINE and POINT node. * * @return the QuadrilateralJ3D description as a {@link Shape3D}. */ public static Shape3D makeQuadrilateralShape3D(DisplayImplJ3D display, int style, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, float[] colour, double z, double thickness) { DisplayRendererJ3D renderer = (DisplayRendererJ3D)display.getDisplayRenderer(); VisADCanvasJ3D canvas = renderer.getCanvas(); int[][] screenXY = { {x1, x2, x3, x4}, {y1, y2, y3, y4} }; Point3d[] points = screenToVworld(screenXY, canvas); //adjust z ditherPoints(canvas, 0.4, points); adjustZ(display, canvas, z, points); QuadArray quadArray = new QuadArray(points.length, GeometryArray.COORDINATES); quadArray.setCoordinates(0, points); // Make an appearance for outline PointAttributes pointAttributes = new PointAttributes(); pointAttributes.setPointSize((float)thickness); LineAttributes lineAttributes = new LineAttributes(); lineAttributes.setLineWidth((float)thickness); PolygonAttributes pointPolygon = new PolygonAttributes(); pointPolygon.setCullFace(PolygonAttributes.CULL_NONE); if (style == QuadrilateralJ3D.POINT) { pointPolygon.setPolygonMode(PolygonAttributes.POLYGON_POINT); } else if (style == QuadrilateralJ3D.LINE) { pointPolygon.setPolygonMode(PolygonAttributes.POLYGON_LINE); } else { pointPolygon.setPolygonMode(PolygonAttributes.POLYGON_FILL); } // make some colours ColoringAttributes pointColor = new ColoringAttributes(); pointColor.setColor(colour[0], colour[1], colour[2]); Appearance pointAppearance = new Appearance(); pointAppearance.setPointAttributes(pointAttributes); pointAppearance.setLineAttributes(lineAttributes); pointAppearance.setPolygonAttributes(pointPolygon); pointAppearance.setColoringAttributes(pointColor); return new Shape3D(quadArray, pointAppearance); } // makeQuadrilateralShape3D /** * Construct a {@link Shape3D} object from a routine description * of a Triangle. * * @param display the VisAD display for this Point. * @param style one of <ul> * <li> TriangleJ3D.FILL, </li> * <li> TriangleJ3D.LINE, </li> * <li> TriangleJ3D.POINT </li> </ul> * @param x1 x screen coordinate of the first point. * @param y1 y screen coordinate of the first point. * @param x2 x screen coordinate of the second point. * @param y2 y screen coordinate of the second point. * @param x3 x screen coordinate of the third point. * @param y3 y screen coordinate of the third point. * @param colour red green blue triple; each value in [0.0, 1.0]. * @param z Virtual world value; larger z is in front. * @param thickness used for LINE and POINT node. * * @return the Triangle description as a {@link Shape3D}. */ public static Shape3D makeTriangleShape3D(DisplayImplJ3D display, int style, int x1, int y1, int x2, int y2, int x3, int y3, float[] colour, double z, double thickness) { DisplayRendererJ3D renderer = (DisplayRendererJ3D)display.getDisplayRenderer(); VisADCanvasJ3D canvas = renderer.getCanvas(); // this becomes a call to screenToVworld() int[][] screenXY = { {x1, x2, x3}, {y1, y2, y3} }; Point3d[] points = screenToVworld(screenXY, canvas); //adjust z ditherPoints(canvas, 0.4, points); adjustZ(display, canvas, z, points); TriangleArray triangleArray = new TriangleArray( points.length, GeometryArray.COORDINATES); triangleArray.setCoordinates(0, points); // Make an appearance for outline PointAttributes pointAttributes = new PointAttributes(); pointAttributes.setPointSize((float)thickness); LineAttributes lineAttributes = new LineAttributes(); lineAttributes.setLineWidth((float)thickness); PolygonAttributes pointPolygon = new PolygonAttributes(); pointPolygon.setCullFace(PolygonAttributes.CULL_NONE); if (style == TriangleJ3D.POINT) { pointPolygon.setPolygonMode(PolygonAttributes.POLYGON_POINT); } else if (style == TriangleJ3D.LINE) { pointPolygon.setPolygonMode(PolygonAttributes.POLYGON_LINE); } else { pointPolygon.setPolygonMode(PolygonAttributes.POLYGON_FILL); } // make some colours ColoringAttributes pointColor = new ColoringAttributes(); pointColor.setColor(colour[0], colour[1], colour[2]); Appearance pointAppearance = new Appearance(); pointAppearance.setPointAttributes(pointAttributes); pointAppearance.setLineAttributes(lineAttributes); pointAppearance.setPolygonAttributes(pointPolygon); pointAppearance.setColoringAttributes(pointColor); return new Shape3D(triangleArray, pointAppearance); } // makeTriangleShape3D /** * Construct a {@link Shape3D} object from a routine description * of a Line. * * @param display the VisAD display for this Point. * @param style one of: <ul> * <li> LineJ3D.SOLID * <li> LineJ3D.DASH * <li> LineJ3D.DOT * <li> LineJ3D.DASH_DOT </ul> * @param x1 x screen coordinate of the first point. * @param y1 y screen coordinate of the first point. * @param x2 x screen coordinate of the second point. * @param y2 y screen coordinate of the second point. * @param colour red green blue triple; each value in [0.0, 1.0]. * @param z Virtual world value; larger z is in front. * @param thickness for the line. * * @return the LineJ3D description as a {@link Shape3D}. */ public static Shape3D makeLineShape3D(DisplayImplJ3D display, int style, int x1, int y1, int x2, int y2, float[] colour, double z, double thickness) { DisplayRendererJ3D renderer = (DisplayRendererJ3D)display.getDisplayRenderer(); VisADCanvasJ3D canvas = renderer.getCanvas(); int[][] screenXY = { {x1, x2}, {y1, y2} }; Point3d[] points = screenToVworld(screenXY, canvas); //adjust z ditherPoints(canvas, 0.4, points); adjustZ(display, canvas, z, points); LineArray lineArray = new LineArray( points.length, GeometryArray.COORDINATES); lineArray.setCoordinates(0, points); // Make an appearance for outline PointAttributes pointAttributes = new PointAttributes(); pointAttributes.setPointSize((float)thickness); LineAttributes lineAttributes = new LineAttributes(); lineAttributes.setLineWidth((float)thickness); if (style == LineJ3D.SOLID) { lineAttributes.setLinePattern(LineAttributes.PATTERN_SOLID); } else if (style == LineJ3D.DASH) { lineAttributes.setLinePattern(LineAttributes.PATTERN_DASH); } else if (style == LineJ3D.DOT) { lineAttributes.setLinePattern(LineAttributes.PATTERN_DOT); } else if (style == LineJ3D.DASH_DOT) { lineAttributes.setLinePattern(LineAttributes.PATTERN_DASH_DOT); } // make some colours ColoringAttributes pointColor = new ColoringAttributes(); pointColor.setColor(colour[0], colour[1], colour[2]); Appearance pointAppearance = new Appearance(); pointAppearance.setPointAttributes(pointAttributes); pointAppearance.setLineAttributes(lineAttributes); pointAppearance.setColoringAttributes(pointColor); return new Shape3D(lineArray, pointAppearance); } // makeLineShape3D /** * Construct a {@link Shape3D} object from a routine description * of a Point. * * @param display the VisAD display for this Point. * @param x1 x screen coordinate of the point. * @param y1 y screen coordinate of the point. * @param colour red green blue triple; each value in [0.0, 1.0]. * @param z Virtual world value; larger z is in front. * @param thickness gives the size of the Point. * * @return the Point description as a {@link Shape3D}. */ public static Shape3D makePointShape3D(DisplayImplJ3D display, int x1, int y1, float[] colour, double z, double thickness) { DisplayRendererJ3D renderer = (DisplayRendererJ3D)display.getDisplayRenderer(); VisADCanvasJ3D canvas = renderer.getCanvas(); int[][] screenXY = { {x1}, {y1} }; Point3d[] points = screenToVworld(screenXY, canvas); //adjust z ditherPoints(canvas, 0.4, points); adjustZ(display, canvas, z, points); PointArray pointArray = new PointArray( points.length, GeometryArray.COORDINATES); pointArray.setCoordinates(0, points); // Make an appearance for outline PointAttributes pointAttributes = new PointAttributes(); pointAttributes.setPointSize((float)thickness); // make some colours ColoringAttributes pointColor = new ColoringAttributes(); pointColor.setColor(colour[0], colour[1], colour[2]); Appearance pointAppearance = new Appearance(); pointAppearance.setPointAttributes(pointAttributes); pointAppearance.setColoringAttributes(pointColor); return new Shape3D(pointArray, pointAppearance); } // makePointShape3D /** * Convert an array of Pixel points to Point3d array in the * Virtual World. * * @param canvas for the virtual world of interest. * @param screenXY the screen points in x, y pairs. * * @return - array of {@link Point3d} values representing the * Virtual World values for the Screen points. */ public static Point3d[] screenToVworld(int[][] screenXY, Canvas3D canvas) { int numPoints = screenXY[0].length; Point3d[] points = new Point3d[numPoints]; Transform3D t = new Transform3D(); canvas.getImagePlateToVworld(t); for (int i=0; i< numPoints; i++) { points[i] = new Point3d(); canvas.getPixelLocationInImagePlate(screenXY[0][i], screenXY[1][i], points[i]); t.transform(points[i]); } return points; } // screenToVWorld /** * Convert an array of Vworld points to int[][] array in * Screen coordinates. * * @param canvas for the virtual world of interest. * @param points array of {@link Point3d} of Vworld coordinates. * * @return - int[2][] array of values representing the Screen points. */ public static int[][] vworldToScreen(Point3d[] points, Canvas3D canvas) { int numPoints = points.length; Point2d point2d = new Point2d(); Point3d point3d = new Point3d(); int[][] screenXY = new int[2][numPoints]; Transform3D t = new Transform3D(); canvas.getVworldToImagePlate(t); for (int j=0; j<numPoints; j++) { // Copy so that the input is unchanged point3d.x = points[j].x; point3d.y = points[j].y; point3d.z = points[j].z; t.transform(point3d); canvas.getPixelLocationFromImagePlate(point3d, point2d); screenXY[0][j] = (int)point2d.x; screenXY[1][j] = (int)point2d.y; } return screenXY; } // vworldToScreen /** * Java3D seems to calculate a floating point pixel value and * then take the integer part for the screen coordinate. * * This routine allows the user to move the x, y value of a * Vworld set of points. If these points represent Vworld values * for some pixels then it allows the coordinate to be moved to * the interior of the pixel rather than the top left. Consequently * converting back from Vworld values to floating point screen * values should ensure the integer part is mapping back to the * original pixel. * * The intended use is in mapping screen coordinates to Vworld, * allow some adjustments, and then be able to map back to the same * screen coordinates. * * An example is adjusting the z value in Vworld so the point is * closer to (or further from) the eye but have the image occupy * the same pixel. Get the original Vworld coordinate, use this * routine to adjust to a Vworld point interior (centre?) of the * pixel, and then adjust the z value. * * Java 3D uses positive y down the screen for pixel values, and * negative y up the screen in Vworld coordinates. So this routine * is written accordinagly for doing fractional adjustments. * * @param canvas where the points are displayed. * @param frac amount of interpoint spacing to use for adjustment; * it is clamped between [0.1, 0.9]. * @param points array of values to have their x, y values adjusted. */ public static void ditherPoints(Canvas3D canvas, double frac, Point3d[] points) { double deltaX; double deltaY; // clamp frac to [(0.1, 0.9] if (frac < 0.1) { frac = 0.1; } if (frac > 0.9) { frac = 0.9; } // get interpixel spacing in Vworld corrdinates Point3d p1 = new Point3d(); Point3d p2 = new Point3d(); Point3d p3 = new Point3d(); canvas.getPixelLocationInImagePlate(1, 1, p1); canvas.getPixelLocationInImagePlate(2, 1, p2); canvas.getPixelLocationInImagePlate(1, 2, p3); Transform3D t = new Transform3D(); canvas.getImagePlateToVworld(t); t.transform(p1); t.transform(p2); t.transform(p3); deltaX = p2.x - p1.x; deltaY = p1.y - p3.y; // use this to get the dithered points for (int i=0; i<points.length; i++) { points[i].x = points[i].x + frac*deltaX; points[i].y = points[i].y - frac*deltaY; } } // ditherPoints /** * Transforms an Image object into a {@link Shape3D}. The image * object may be scaled. * * @param display the VisAD display for this Point. * @param image to be converted to {@link Shape3D}. * @param position how to place the image relative to (x, y); * one of: <ul> * <li> Image.TOP_LEFT (default) * <li> Image.TOP_RIGHT * <li> Image.BOTTOM_RIGHT * <li> Image.BOTTOM_LEFT * <li> Image.CENTER </ul> * @param x x screen coordinate to place the image. * @param y y screen coordinate to place the image. * @param width of image in pixels. * @param height of image in pixels. * @param zValue Virtual world value; larger z is in front. * @param scale scale factor for image magnification; greater * than 0.0. * * @return the Image description as a {@link Shape3D}. * * @throws VisADException if the image can't be accessed * for some reason. */ public static Shape3D makeImageShape3D(DisplayImplJ3D display, Image image, int position, int x, int y, int width, int height, double zValue, double scale) throws VisADException { int index = 0; int ind = 0; int scaledWidth; int scaledHeight; DisplayRendererJ3D renderer = (DisplayRendererJ3D)display.getDisplayRenderer(); VisADCanvasJ3D canvas = renderer.getCanvas(); // Adjust for scaling factor scaledWidth = Math.round((float)(width*scale)); scaledHeight = Math.round((float)(height*scale)); double scaleW = (double)scaledWidth/(double)width; double scaleH = (double)scaledHeight/(double)height; // Adjust x, y according to position // do nothing for TOP_LEFT if (position == ImageJ3D.TOP_RIGHT) { // adjust x x = x - (scaledWidth-1); } else if (position == ImageJ3D.BOTTOM_RIGHT) { // adjust x & y x = x - (scaledWidth-1); y = y - (scaledHeight-1); } else if (position == ImageJ3D.BOTTOM_LEFT) { // adjust y y = y - (scaledHeight-1); } else if (position == ImageJ3D.CENTER) { // adjust x & y x = x - (scaledWidth-1)/2; y = y - (scaledHeight-1)/2; } // now calculate the VWorld coordinates for each pixel Transform3D t = new Transform3D(); canvas.getImagePlateToVworld(t); index = 0; Point3d[] points = new Point3d[scaledWidth*scaledHeight]; for (int i=0; i<scaledHeight; i++) { for (int j=0; j<scaledWidth; j++) { // row first points[index] = new Point3d(); canvas.getPixelLocationInImagePlate(x + j, y + i, points[index]); t.transform(points[index]); index++; } } ditherPoints(canvas, 0.4, points); adjustZ(display, canvas, zValue, points); // now get the colours from the image int[] pixels = new int[width*height]; PixelGrabber pixelGrabber = new PixelGrabber(image.getSource(), 0, 0, width, height, pixels, 0, width); // get the pixels try { pixelGrabber.grabPixels(); } catch (InterruptedException ie) { throw new VisADException( "ScreenAnnotatorUtils.makeImageShape3D():" + " failed to grabPixels()"); } float[] colours = new float[4*scaledWidth*scaledHeight]; ind = 0; index = 0; ColorModel cm = pixelGrabber.getColorModel(); for (int i=0; i<scaledHeight; i++) { // down column last for (int j=0; j<scaledWidth; j++) { // row first ind = (int)((double)j/scaleW) + width*(int)((double)i/scaleH); colours[4*index] = (float)cm.getRed(pixels[ind])/255; colours[4*index+1] = (float)cm.getGreen(pixels[ind])/255; colours[4*index+2] = (float)cm.getBlue(pixels[ind])/255; colours[4*index+3] = (float)cm.getAlpha(pixels[ind])/255; index++; } } PointArray picture = new PointArray(scaledWidth*scaledHeight, GeometryArray.COORDINATES | GeometryArray.COLOR_4); picture.setCoordinates(0, points); picture.setColors(0, colours); Appearance appearance = new Appearance(); appearance.setTransparencyAttributes(new TransparencyAttributes(TransparencyAttributes.FASTEST, 0)); // Use default appearance return new Shape3D(picture, appearance); } // makeImageShape3D } // class ScreenAnnotatorUtils