All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scijava.java3d.utils.pickfast.PickIntersection Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 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 MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS 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 THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 *
 */

package org.scijava.java3d.utils.pickfast;

import org.scijava.java3d.Geometry;
import org.scijava.java3d.GeometryArray;
import org.scijava.java3d.IndexedGeometryArray;
import org.scijava.java3d.PickInfo;
import org.scijava.java3d.Transform3D;
import org.scijava.vecmath.Color3f;
import org.scijava.vecmath.Color4f;
import org.scijava.vecmath.Point3d;
import org.scijava.vecmath.TexCoord3f;
import org.scijava.vecmath.Vector3d;
import org.scijava.vecmath.Vector3f;

/**
 * Holds information about an intersection of a PickShape with a Node
 * as part of a PickInfo.IntersectionInfo. Information about
 * the intersected geometry, intersected primitive, intersection point, and
 * closest vertex can be inquired.
 * 

* The intersected primitive indicates which primitive out of the GeometryArray * was intersected (where the primitive is a point, line, triangle or quad, * not a * org.scijava.java3d.utils.geometry.Primitive). * For example, the intersection would indicate which triangle out of a * triangle strip was intersected. * The methods which return primitive data will have one value if the primitive * is * a point, two values if the primitive is a line, three values if the primitive * is a triangle and four values if the primitive is quad. *

* The primitive's VWorld coordinates are saved when then intersection is * calculated. The local coordinates, normal, color and texture coordinates * for the primitive can also be inquired if they are present and readable. *

* The intersection point is the location on the primitive which intersects the * pick shape closest to the center of the pick shape. The intersection point's * location in VWorld coordinates is saved when the intersection is calculated. * The local coordinates, normal, color and texture coordiantes of at the * intersection can be interpolated if they are present and readable. *

* The closest vertex is the vertex of the primitive closest to the intersection * point. The vertex index, VWorld coordinates and local coordinates of the * closest vertex can be inquired. The normal, color and texture coordinate * of the closest vertex can be inquired from the geometry array: *

 *      Vector3f getNormal(PickIntersection pi, int vertexIndex) {
 *          int index;
 *          Vector3d normal = new Vector3f();
 *          GeometryArray ga = pickIntersection.getGeometryArray();
 *          if (pickIntersection.geometryIsIndexed()) {
 *              index = ga.getNormalIndex(vertexIndex);
 *          } else {
 *              index = vertexIndex;
 *          }
 *          ga.getNormal(index, normal);
 *          return normal;
 *      }
 * 
*

* The color, normal * and texture coordinate information for the intersected primitive and the * intersection point * can be inquired * the geometry includes them and the corresponding READ capibility bits are * set. */ public class PickIntersection { /* The intersection point */ // Point3d getIntersectionPoint() /* Distance between start point of pickShape and intersection point */ // double getDistance() /* The vertex indices of the intersected primitive in the geometry */ // int[] getVertexIndices() /*************************/ /** Weight factors for interpolation, values correspond to vertex indices, * sum == 1 */ private double[] interpWeights; private static final boolean debug = false; // Axis constants private static final int X_AXIS = 1; private static final int Y_AXIS = 2; private static final int Z_AXIS = 3; // Tolerance for numerical stability static final double TOL = 1.0e-5; /* The references to the intersectionInfo object */ private PickInfo.IntersectionInfo iInfo = null; private Transform3D l2vw = null; private Geometry geometry = null; private boolean geometryIsIndexed = false; private double distance; private boolean hasColors; private boolean hasNormals; private boolean hasTexCoords; // Primitive /* indices for the different data types */ private int[] primitiveCoordinateIndices; private int[] primitiveNormalIndices; private int[] primitiveColorIndices; private int[] primitiveTexCoordIndices; /** Indices of the intersected primitive */ private int[] primitiveVertexIndices = null; /* Local coordinates of the intersected primitive */ private Point3d[] primitiveCoordinates = null; /** VWorld coordinates of intersected primitive */ private Point3d[] primitiveCoordinatesVW = null; /* Normals of the intersected primitive */ private Vector3f[] primitiveNormals = null; /* Colors of the intersected primitive */ private Color4f[] primitiveColors = null; /* TextureCoordinates of the intersected primitive */ private TexCoord3f[] primitiveTexCoords = null; // Intersection point /** VWorld Coordinates of the intersection point */ private Point3d pointCoordinatesVW = null; /** Local Coordinates of the intersection point */ private Point3d pointCoordinates = null; /** Normal at the intersection point */ private Vector3f pointNormal = null; /** Color at the intersection point */ private Color4f pointColor = null; /** TexCoord at the intersection point */ private TexCoord3f pointTexCoord = null; // Closest Vertex /** Index of the closest vertex */ private int closestVertexIndex = -1; /** Coordinates of the closest vertex */ private Point3d closestVertexCoordinates = null; /** Coordinates of the closest vertex (World coordinates) */ private Point3d closestVertexCoordinatesVW = null; /* =================== METHODS ======================= */ /** * Constructor * @param intersectionInfo The IntersectionInfo this intersection is part of. */ public PickIntersection (Transform3D localToVWorld, PickInfo.IntersectionInfo intersectionInfo) { // Should check and throw NPE if the following is null. // localToVWorld can't be null. l2vw = localToVWorld; // intersectionInfo can't be null. iInfo = intersectionInfo; // geometry can't be null. geometry = iInfo.getGeometry(); pointCoordinates = iInfo.getIntersectionPoint(); distance = iInfo.getDistance(); primitiveVertexIndices = iInfo.getVertexIndices(); if (geometry instanceof GeometryArray) { int vertexFormat = ((GeometryArray)geometry).getVertexFormat(); hasColors = (0 != (vertexFormat & (GeometryArray.COLOR_3 | GeometryArray.COLOR_4))); hasNormals = (0 != (vertexFormat & GeometryArray.NORMALS)); hasTexCoords = (0 != (vertexFormat & (GeometryArray.TEXTURE_COORDINATE_2 | GeometryArray.TEXTURE_COORDINATE_3))); if (geometry instanceof IndexedGeometryArray) { geometryIsIndexed = true; } } } /** * Returns true if the geometry is indexed * */ public boolean geometryIsIndexed() { return geometryIsIndexed; } /** * Get coordinates of closest vertex (local) * @return the coordinates of the vertex closest to the intersection point * */ public Point3d getClosestVertexCoordinates() { // System.out.println("PI.closestVertexCoordinates " + closestVertexCoordinates); GeometryArray geom = (GeometryArray) geometry; if (closestVertexCoordinates == null) { int vertexIndex = getClosestVertexIndex(); int vformat = geom.getVertexFormat(); int val; int[] indices = getPrimitiveCoordinateIndices(); if ((vformat & GeometryArray.BY_REFERENCE) == 0) { closestVertexCoordinates = new Point3d(); geom.getCoordinate(indices[vertexIndex], closestVertexCoordinates); // System.out.println("PI.closestVertexCoordinates " + // closestVertexCoordinates + " vertexIndex " + // vertexIndex); } else { if ((vformat & GeometryArray.INTERLEAVED) == 0) { double[] doubleData = geom.getCoordRefDouble(); // If data was set as float then .. if (doubleData == null) { float[] floatData = geom.getCoordRefFloat(); if (floatData == null) { throw new UnsupportedOperationException("Deprecated : BY_REF - p3f and p3d"); } else { val = indices[vertexIndex] * 3; // for x,y,z closestVertexCoordinates = new Point3d(floatData[val], floatData[val+1], floatData[val+2]); } } else { val = indices[vertexIndex] * 3; // for x,y,z closestVertexCoordinates = new Point3d(doubleData[val], doubleData[val+1], doubleData[val+2]); } } else { float[] floatData = geom.getInterleavedVertices(); int offset = getInterleavedVertexOffset(geom); int stride = offset + 3; // for the vertices . val = stride * indices[vertexIndex]+offset; closestVertexCoordinates = new Point3d(floatData[val], floatData[val+1], floatData[val+2]); } } } return closestVertexCoordinates; } /** * Get coordinates of closest vertex (world) * @return the coordinates of the vertex closest to the intersection point * */ public Point3d getClosestVertexCoordinatesVW() { if (closestVertexCoordinatesVW == null) { int vertexIndex = getClosestVertexIndex(); Point3d[] coordinatesVW = getPrimitiveCoordinatesVW(); closestVertexCoordinatesVW = coordinatesVW[vertexIndex]; } return closestVertexCoordinatesVW; } /** * Get index of closest vertex * @return the index of the closest vertex */ public int getClosestVertexIndex() { if (closestVertexIndex == -1) { double maxDist = Double.MAX_VALUE; double curDist = Double.MAX_VALUE; int closestIndex = -1; primitiveCoordinates = getPrimitiveCoordinates(); assert(primitiveCoordinates != null); // System.out.println("PI.getClosestVertexIndex : primitiveCoordinates.length " + // primitiveCoordinates.length); for (int i=0;i max) { axis = Y_AXIS; max = abs(delta.y); } if (abs(delta.z) > max) { axis = Z_AXIS; } return axis; } /* Triangle interpolation. Basic idea: * Map the verticies of the triangle to the form: * * L--------R * \ / * IL+--P-+IR * \ / * Base * where P is the intersection point Base, L and R and the triangle points. IL and IR are the projections if P along the Base-L and Base-R edges using an axis: IL = leftFactor * L + (1- leftFactor) * Base IR = rightFactor * R + (1-rightFactor) * Base then find the interp factor, midFactor, for P between IL and IR. If this is outside the range 0->1 then we have the wrong triangle of a quad and we return false. Else, the weighting is: IP = midFactor * IL + (1 - midFactor) * IR; Solving for weights for the formula: IP = BaseWeight * Base + LeftWeight * L + RightWeight * R; We get: BaseWeight = 1 - midFactor * leftFactor - rightFactor + midFactor * rightFactor; LeftWeight = midFactor * leftFactor; RightWeight = righFactor - midFactor * rightFactor; As a check, note that the sum of the weights is 1.0. */ boolean interpTriangle(int index0, int index1, int index2, Point3d[] coords, Point3d intPt) { // find the longest edge, we'll use that to pick the axis */ Vector3d delta0 = new Vector3d(); Vector3d delta1 = new Vector3d(); Vector3d delta2 = new Vector3d(); delta0.sub(coords[index1], coords[index0]); delta1.sub(coords[index2], coords[index0]); delta2.sub(coords[index2], coords[index1]); double len0 = delta0.lengthSquared(); double len1 = delta1.lengthSquared(); double len2 = delta2.lengthSquared(); Vector3d longest = delta0; double maxLen = len0; if (len1 > maxLen) { longest = delta1; maxLen = len1; } if (len2 > maxLen) { longest = delta2; } int mainAxis = maxAxis(longest); /* System.out.println("index0 = " + index0 + " index1 = " + index1 + " index2 = " + index2); System.out.println("coords[index0] = " + coords[index0]); System.out.println("coords[index1] = " + coords[index1]); System.out.println("coords[index2] = " + coords[index2]); System.out.println("intPt = " + intPt); System.out.println("delta0 = " + delta0 + " len0 " + len0); System.out.println("delta1 = " + delta1 + " len1 " + len1); System.out.println("delta2 = " + delta2 + " len2 " + len2); */ /* now project the intersection point along the axis onto the edges */ double[] factor = new double[3]; /* the factor is for the projection opposide the vertex 0 = 1->2, etc*/ factor[0] = getInterpFactorForBase(intPt, coords[index1], coords[index2], mainAxis); factor[1] = getInterpFactorForBase(intPt, coords[index2], coords[index0], mainAxis); factor[2] = getInterpFactorForBase(intPt, coords[index0], coords[index1], mainAxis); if (debug) { System.out.println("intPt = " + intPt); switch(mainAxis) { case X_AXIS: System.out.println("mainAxis = X_AXIS"); break; case Y_AXIS: System.out.println("mainAxis = Y_AXIS"); break; case Z_AXIS: System.out.println("mainAxis = Z_AXIS"); break; } System.out.println("factor[0] = " + factor[0]); System.out.println("factor[1] = " + factor[1]); System.out.println("factor[2] = " + factor[2]); } /* Find the factor that is out of range, it will tell us which * vertex to use for base */ int base, left, right; double leftFactor, rightFactor; if ((factor[0] < 0.0) || (factor[0] > 1.0)) { base = index0; right = index1; left = index2; rightFactor = factor[2]; leftFactor = 1.0 - factor[1]; if (debug) { System.out.println("base 0, rightFactor = " + rightFactor + " leftFactor = " + leftFactor); } } else if ((factor[1] < 0.0) || (factor[1] > 1.0)) { base = index1; right = index2; left = index0; rightFactor = factor[0]; leftFactor = 1.0 - factor[2]; if (debug) { System.out.println("base 1, rightFactor = " + rightFactor + " leftFactor = " + leftFactor); } } else { base = index2; right = index0; left = index1; rightFactor = factor[1]; leftFactor = 1.0 - factor[0]; if (debug) { System.out.println("base 2, rightFactor = " + rightFactor + " leftFactor = " + leftFactor); } } if (debug) { System.out.println("base = " + coords[base]); System.out.println("left = " + coords[left]); System.out.println("right = " + coords[right]); } /* find iLeft and iRight */ Point3d iLeft = new Point3d(leftFactor * coords[left].x + (1.0-leftFactor)*coords[base].x, leftFactor * coords[left].y + (1.0-leftFactor)*coords[base].y, leftFactor * coords[left].z + (1.0-leftFactor)*coords[base].z); Point3d iRight = new Point3d(rightFactor * coords[right].x + (1.0-rightFactor)*coords[base].x, rightFactor * coords[right].y + (1.0-rightFactor)*coords[base].y, rightFactor * coords[right].z + (1.0-rightFactor)*coords[base].z); if (debug) { System.out.println("iLeft = " + iLeft); System.out.println("iRight = " + iRight); } /* now find an axis and solve for midFactor */ delta0.sub(iLeft, iRight); int midAxis = maxAxis(delta0); double midFactor = getInterpFactor(intPt, iRight, iLeft, midAxis); if (debug) { switch(midAxis) { case X_AXIS: System.out.println("midAxis = X_AXIS"); break; case Y_AXIS: System.out.println("midAxis = Y_AXIS"); break; case Z_AXIS: System.out.println("midAxis = Z_AXIS"); break; } System.out.println("midFactor = " + midFactor); } if (midFactor < 0.0) { // System.out.println("midFactor = " + midFactor); if ((midFactor + TOL) >= 0.0) { // System.out.println("In Tol case : midFactor = " + midFactor); midFactor = 0.0; } else { /* int point is outside triangle */ return false; } } else if (midFactor > 1.0) { // System.out.println("midFactor = " + midFactor); if ((midFactor-TOL) <= 1.0) { // System.out.println("In Tol case : midFactor = " + midFactor); midFactor = 1.0; } else { /* int point is outside triangle */ return false; } } // Assign the weights interpWeights[base] = 1.0 - midFactor * leftFactor - rightFactor + midFactor * rightFactor; interpWeights[left] = midFactor * leftFactor; interpWeights[right] = rightFactor - midFactor * rightFactor; return true; } /* Get the interpolation weights for each of the verticies of the * primitive. */ double[] getInterpWeights() { Point3d pt = getPointCoordinates(); Point3d[] coordinates = getPrimitiveCoordinates(); double factor; int axis; if (interpWeights != null) { return interpWeights; } interpWeights = new double[coordinates.length]; // Interpolate switch (coordinates.length) { case 1: // Nothing to interpolate interpWeights[0] = 1.0; break; case 2: // edge Vector3d delta = new Vector3d(); delta.sub (coordinates[1], coordinates[0]); axis = maxAxis(delta); factor = getInterpFactor (pt, coordinates[1], coordinates[0], axis); interpWeights[0] = factor; interpWeights[1] = 1.0 - factor; break; case 3: // triangle if (!interpTriangle(0, 1, 2, coordinates, pt)) { throw new RuntimeException ("Interp point outside triangle"); } break; case 4: // quad if (!interpTriangle(0, 1, 2, coordinates, pt)) { if (!interpTriangle(0, 2, 3, coordinates, pt)) { throw new RuntimeException ("Interp point outside quad"); } } break; default: throw new RuntimeException ("Unexpected number of points."); } return interpWeights; } /** Calculate the interpolation factor for point p by projecting it along an axis (x,y,z) onto the edge between p1 and p2. If the result is in the 0->1 range, point is between p1 and p2 (0 = point is at p1, 1 => point is at p2). */ private static float getInterpFactor (Point3d p, Point3d p1, Point3d p2, int axis) { float t; switch (axis) { case X_AXIS: if (p1.x == p2.x) //t = Float.MAX_VALUE; // TODO: should be 0? t = 0.0f; else t = (float) ((p1.x - p.x) / (p1.x - p2.x)); break; case Y_AXIS: if (p1.y == p2.y) // t = Float.MAX_VALUE; t = 0.0f; else t = (float) ((p1.y - p.y) / (p1.y - p2.y)); break; case Z_AXIS: if (p1.z == p2.z) // t = Float.MAX_VALUE; t = 0.0f; else t = (float)((p1.z - p.z) / (p1.z - p2.z)); break; default: throw new RuntimeException ("invalid axis parameter "+axis+" (must be 0-2)"); } return t; } /** Calculate the interpolation factor for point p by projecting it along an axis (x,y,z) onto the edge between p1 and p2. If the result is in the 0->1 range, point is between p1 and p2 (0 = point is at p1, 1 => point is at p2). return MAX_VALUE if component of vertices are the same. */ private static float getInterpFactorForBase (Point3d p, Point3d p1, Point3d p2, int axis) { float t; switch (axis) { case X_AXIS: if (p1.x == p2.x) t = Float.MAX_VALUE; else t = (float) ((p1.x - p.x) / (p1.x - p2.x)); break; case Y_AXIS: if (p1.y == p2.y) t = Float.MAX_VALUE; else t = (float) ((p1.y - p.y) / (p1.y - p2.y)); break; case Z_AXIS: if (p1.z == p2.z) t = Float.MAX_VALUE; else t = (float)((p1.z - p.z) / (p1.z - p2.z)); break; default: throw new RuntimeException ("invalid axis parameter "+axis+" (must be 0-2)"); } return t; } } // PickIntersection





© 2015 - 2024 Weber Informatics LLC | Privacy Policy