/*
 * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.geom;

import com.sun.javafx.geom.transform.Affine3D;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.geom.transform.NoninvertibleTransformException;

A ray used for picking.
/** * A ray used for picking. */
public class PickRay { private Vec3d origin = new Vec3d(); private Vec3d direction = new Vec3d(); private double nearClip = 0.0; private double farClip = Double.POSITIVE_INFINITY; // static final double EPS = 1.0e-13; static final double EPS = 1.0e-5f; public PickRay() { } public PickRay(Vec3d origin, Vec3d direction, double nearClip, double farClip) { set(origin, direction, nearClip, farClip); } public PickRay(double x, double y, double z, double nearClip, double farClip) { set(x, y, z, nearClip, farClip); } public static PickRay computePerspectivePickRay( double x, double y, boolean fixedEye, double viewWidth, double viewHeight, double fieldOfViewRadians, boolean verticalFieldOfView, Affine3D cameraTransform, double nearClip, double farClip, PickRay pickRay) { if (pickRay == null) { pickRay = new PickRay(); } Vec3d direction = pickRay.getDirectionNoClone(); double halfViewWidth = viewWidth / 2.0; double halfViewHeight = viewHeight / 2.0; double halfViewDim = verticalFieldOfView? halfViewHeight: halfViewWidth; // Distance to projection plane from eye double distanceZ = halfViewDim / Math.tan(fieldOfViewRadians / 2.0); direction.x = x - halfViewWidth; direction.y = y - halfViewHeight; direction.z = distanceZ; Vec3d eye = pickRay.getOriginNoClone(); if (fixedEye) { eye.set(0.0, 0.0, 0.0); } else { // set eye at center of viewport and move back so that projection plane // is at Z = 0 eye.set(halfViewWidth, halfViewHeight, -distanceZ); } pickRay.nearClip = nearClip * (direction.length() / (fixedEye ? distanceZ : 1.0)); pickRay.farClip = farClip * (direction.length() / (fixedEye ? distanceZ : 1.0)); pickRay.transform(cameraTransform); return pickRay; } public static PickRay computeParallelPickRay( double x, double y, double viewHeight, Affine3D cameraTransform, double nearClip, double farClip, PickRay pickRay) { if (pickRay == null) { pickRay = new PickRay(); } // This is the same math as in the perspective case, fixed // for the default 30 degrees vertical field of view. final double distanceZ = (viewHeight / 2.0) / Math.tan(Math.toRadians(15.0)); pickRay.set(x, y, distanceZ, nearClip * distanceZ, farClip * distanceZ); if (cameraTransform != null) { pickRay.transform(cameraTransform); } return pickRay; } public final void set(Vec3d origin, Vec3d direction, double nearClip, double farClip) { setOrigin(origin); setDirection(direction); this.nearClip = nearClip; this.farClip = farClip; } public final void set(double x, double y, double z, double nearClip, double farClip) { setOrigin(x, y, -z); setDirection(0, 0, z); this.nearClip = nearClip; this.farClip = farClip; } public void setPickRay(PickRay other) { setOrigin(other.origin); setDirection(other.direction); nearClip = other.nearClip; farClip = other.farClip; } public PickRay copy() { return new PickRay(origin, direction, nearClip, farClip); }
Sets the origin of the pick ray in world coordinates.
Params:
  • origin – the origin (in world coordinates).
/** * Sets the origin of the pick ray in world coordinates. * * @param origin the origin (in world coordinates). */
public void setOrigin(Vec3d origin) { this.origin.set(origin); }
Sets the origin of the pick ray in world coordinates.
Params:
  • x – the origin X coordinate
  • y – the origin Y coordinate
  • z – the origin Z coordinate
/** * Sets the origin of the pick ray in world coordinates. * * @param x the origin X coordinate * @param y the origin Y coordinate * @param z the origin Z coordinate */
public void setOrigin(double x, double y, double z) { this.origin.set(x, y, z); } public Vec3d getOrigin(Vec3d rv) { if (rv == null) { rv = new Vec3d(); } rv.set(origin); return rv; } public Vec3d getOriginNoClone() { return origin; }
Sets the direction vector of the pick ray. This vector need not be normalized.
Params:
  • direction – the direction vector
/** * Sets the direction vector of the pick ray. This vector need not * be normalized. * * @param direction the direction vector */
public void setDirection(Vec3d direction) { this.direction.set(direction); }
Sets the direction of the pick ray. The vector need not be normalized.
Params:
  • x – the direction X magnitude
  • y – the direction Y magnitude
  • z – the direction Z magnitude
/** * Sets the direction of the pick ray. The vector need not be normalized. * * @param x the direction X magnitude * @param y the direction Y magnitude * @param z the direction Z magnitude */
public void setDirection(double x, double y, double z) { this.direction.set(x, y, z); } public Vec3d getDirection(Vec3d rv) { if (rv == null) { rv = new Vec3d(); } rv.set(direction); return rv; } public Vec3d getDirectionNoClone() { return direction; } public double getNearClip() { return nearClip; } public double getFarClip() { return farClip; } public double distance(Vec3d iPnt) { double x = iPnt.x - origin.x; double y = iPnt.y - origin.y; double z = iPnt.z - origin.z; return Math.sqrt(x*x + y*y + z*z); }
Project the ray through the specified (inverted) transform and onto the Z=0 plane of the resulting coordinate system. If a perspective projection is being used then only a point that projects forward from the eye to the plane will be returned, otherwise a null will be returned to indicate that the projection is behind the eye.
Params:
  • inversetx – the inverse of the model transform into which the ray is to be projected
  • perspective – true if the projection is happening in perspective
  • tmpvec – a temporary Vec3d object for internal use (may be null)
  • ret – a Point2D object for storing the return value, or null if a new object should be returned.
Returns:
/** * Project the ray through the specified (inverted) transform and * onto the Z=0 plane of the resulting coordinate system. * If a perspective projection is being used then only a point * that projects forward from the eye to the plane will be returned, * otherwise a null will be returned to indicate that the projection * is behind the eye. * * @param inversetx the inverse of the model transform into which the * ray is to be projected * @param perspective true if the projection is happening in perspective * @param tmpvec a temporary {@code Vec3d} object for internal use * (may be null) * @param ret a {@code Point2D} object for storing the return value, * or null if a new object should be returned. * @return */
public Point2D projectToZeroPlane(BaseTransform inversetx, boolean perspective, Vec3d tmpvec, Point2D ret) { if (tmpvec == null) { tmpvec = new Vec3d(); } inversetx.transform(origin, tmpvec); double origX = tmpvec.x; double origY = tmpvec.y; double origZ = tmpvec.z; tmpvec.add(origin, direction); inversetx.transform(tmpvec, tmpvec); double dirX = tmpvec.x - origX; double dirY = tmpvec.y - origY; double dirZ = tmpvec.z - origZ; // Handle the case where pickRay is almost parallel to the Z-plane if (almostZero(dirZ)) { return null; } double t = -origZ / dirZ; if (perspective && t < 0) { // TODO: Or should we use Infinity? (RT-26888) return null; } if (ret == null) { ret = new Point2D(); } ret.setLocation((float) (origX + (dirX * t)), (float) (origY + (dirY * t))); return ret; } // Good to find a home for commonly use util. code such as EPS. // and almostZero. This code currently defined in multiple places, // such as Affine3D and GeneralTransform3D. private static final double EPSILON_ABSOLUTE = 1.0e-5; static boolean almostZero(double a) { return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE)); } private static boolean isNonZero(double v) { return ((v > EPS) || (v < -EPS)); } public void transform(BaseTransform t) { t.transform(origin, origin); t.deltaTransform(direction, direction); } public void inverseTransform(BaseTransform t) throws NoninvertibleTransformException { t.inverseTransform(origin, origin); t.inverseDeltaTransform(direction, direction); } public PickRay project(BaseTransform inversetx, boolean perspective, Vec3d tmpvec, Point2D ret) { if (tmpvec == null) { tmpvec = new Vec3d(); } inversetx.transform(origin, tmpvec); double origX = tmpvec.x; double origY = tmpvec.y; double origZ = tmpvec.z; tmpvec.add(origin, direction); inversetx.transform(tmpvec, tmpvec); double dirX = tmpvec.x - origX; double dirY = tmpvec.y - origY; double dirZ = tmpvec.z - origZ; PickRay pr = new PickRay(); pr.origin.x = origX; pr.origin.y = origY; pr.origin.z = origZ; pr.direction.x = dirX; pr.direction.y = dirY; pr.direction.z = dirZ; return pr; } @Override public String toString() { return "origin: " + origin + " direction: " + direction; } }