Try using the attached class.  It keeps track of the transformations and has a
method to transform from local coordinates to window coordinates.

It can probably be simplified using the
Canvas3D.getPixelLocationFromImagePlate() method in J3D 1.2 beta (just
released).

Doug Gehringer
Sun Microsytems
/*
 *      @(#)LocalToWindow.java 1.2 99/02/08 15:39:12
 *
 * Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

/**
 * Utility class for doing local->window transformations for case where
 * Canvas3D is a simple display such as a monitor. This won't work for the
 * more complex cases (i.e. a multiple canvases, head tracking, etc).
 *
 * Usage:
 *    // after the canvas and node are created
 *    LocalToWindow locToWindow = LocalToWindow(node, canvas);
 *    ...
 *    // when we need to transform (canvas location and node transforms may have
 *    // changed)
 *    locToWindow.update(); // make sure transforms are up to date
 *
 *    Point3d[] localPts = <some local coords to transform >
 *    Point[] windowPts = <the area to put the tranformed pts >
 *    for (int i = 0; i < localPts.length; i++) {
 *       locToWindow.transformPt(localPts[i], windowPts[i]);
 *    }
 */

// standard j3d packages
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.Point;
import java.awt.Dimension;

public class LocalToWindow {

    Canvas3D    canvas = null;
    Node        node = null;

    // inquired/derived data
    Transform3D localToVworld = new Transform3D();
    Transform3D vworldToImagePlate  = new Transform3D();
    Transform3D localToImagePlate = new Transform3D();
    Point3d     eyePos = new Point3d();
    int         projType;
    Point       canvasScr;
    Dimension   screenSize;
    double      metersPerPixelX;
    double      metersPerPixelY;

    // Temporaries
    Point3d     imagePlatePt = new Point3d();
    Vector3d    projVec = new Vector3d();
    Point2d     screenPt = new Point2d();
    Point2d     tempPt2d = new Point2d();

    /**
     * Creates a LocalToWindow object with no associated node or canvas.
     * The node and canvas must be set before transforming points
     */
    public LocalToWindow() {};

    /**
     * Called with the Node which specifies the local coordinates for the
     * points to be transformed and the Canvas3D where the points are
     * displayed
     */
    public LocalToWindow(Node node, Canvas3D canvas) {
        this.canvas = canvas;
        this.node = node;
        update();
    }

    /**
     * Either create LocalToWindow() just before transforming points or call
     * this method to ensure that the transforms are up to date.  Note: if
     * you are transforming several points, you only need to call this method
     * once.
     */
    public void update() {
        if ((this.canvas != null) && (this.node != null)) {
            node.getLocalToVworld(localToVworld);
            canvas.getVworldToImagePlate(vworldToImagePlate);

            // Make a composite transform:
            // vWorldPt = LocalToVworld * localPt;
            // imagePlatePt = VworldToImagePlate * vWorldPt;
            // imagePlatePt = VworldToImagePlate * LocalToVworld * localPt;
            localToImagePlate.mul(vworldToImagePlate, localToVworld);

            // we need these to project the point from Image Plate coords to
            // the actual image plate (i.e. perpsective)
            canvas.getCenterEyeInImagePlate(eyePos);
            //System.out.println("eyePos = " + eyePos);
            projType = canvas.getView().getProjectionPolicy();

            // this stuff is to go from image plate coords to window coords
            canvasScr = canvas.getLocationOnScreen();
            //System.out.println("canvasScr = " + canvasScr);
            screenSize = canvas.getScreen3D().getSize();
            double physicalScreenWidth =
                canvas.getScreen3D().getPhysicalScreenWidth();
            double physicalScreenHeight =
                canvas.getScreen3D().getPhysicalScreenHeight();
            metersPerPixelX = physicalScreenWidth / (double) screenSize.width;
            metersPerPixelY = physicalScreenHeight / (double) screenSize.height;
        }
    }

    /**
     * Set the node and canvas and call update()
     */
    public void update(Node node, Canvas3D canvas) {
        this.canvas = canvas;
        this.node = node;
        update();
    }

    /**
     * Transform the point from local coords to window coords
     */
    public void transformPt(Point3d localPt, Point2d windowPt) {
        // TODO: throw some kind of error if node and canvas haven't been
        // set

        //System.out.println("vWorld Pt = " + localPt);

        localToImagePlate.transform(localPt, imagePlatePt);
        //System.out.println("imagePlatePt = " + imagePlatePt);

        double zScale = 1.0; // default, used for PARALELL_PROJECTION
        if (projType == View.PERSPECTIVE_PROJECTION) {
            // get the vector from eyePos to imagePlatePt
            projVec.sub(imagePlatePt, eyePos);

            // Scale this vector to make it end at the projection plane.
            // Scale is ratio :
            //     eye->imagePlate Plane dist  / eye->imagePlatePt dist
            // eye dist to plane is eyePos.z (eye is in +z space)
            // image->eye dist is -projVec.z (image->eye is in -z dir)
            //System.out.println("eye dist = " + (eyePos.z));
            //System.out.println("image dist = " + (-projVec.z));
            zScale = eyePos.z / (-projVec.z);

            screenPt.x = eyePos.x + projVec.x * zScale;
            screenPt.y = eyePos.y + projVec.y * zScale;
        } else {
            screenPt.x = imagePlatePt.x;
            screenPt.y = imagePlatePt.y;
        }
        //System.out.println("screenPt = " + screenPt);
        // Note: screenPt is in image plate coords, at z=0

        // Transform from image plate coords to screen coords
        windowPt.x = (screenPt.x / metersPerPixelX) - canvasScr.x;
        windowPt.y = screenSize.height - 1 - (screenPt.y / metersPerPixelY) -
                canvasScr.y;
        //System.out.println("windowPt = " + windowPt);

    }

    /**
     * Transform the point from local coords to window coords
     */
    public void transformPt(Point3d localPt, Point windowPt) {
        transformPt(localPt, tempPt2d);
        windowPt.x = (int)Math.round(tempPt2d.x);
        windowPt.y = (int)Math.round(tempPt2d.y);
    }
}

Reply via email to