/* * Project Info: http://jcae.sourceforge.net * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This program 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 Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * (C) Copyright 2013, by EADS France */ package org.jcae.mesh.stitch; import java.util.Arrays; import org.jcae.mesh.amibe.ds.AbstractHalfEdge; import org.jcae.mesh.amibe.ds.Mesh; import org.jcae.mesh.amibe.ds.Triangle; import org.jcae.mesh.amibe.ds.Vertex; import org.jcae.mesh.amibe.metrics.Location; import org.jcae.mesh.amibe.metrics.Matrix3D; import org.jcae.mesh.amibe.traits.MeshTraitsBuilder; /** * * @author Jerome Robert */ class TriangleProjector { public static enum ProjectionType { FACE, EDGE, VERTEX, /** The projection is out of the triangle. */ OUT, /** * The point to be projected is too far from the triangle but its * projection is on the triangle */ FAR} private final Location projection = new Location(); /** The vertex of the triangle where to project when type is VERTEX. */ private Vertex vertexProject; /** The edge of the triangle where to project when type is EDGE. */ private AbstractHalfEdge edgeProject; private ProjectionType type; /** * The square of the distance between the projection on the triangle * plane and each vertices. */ private final double[] dist2ToVertices = new double[3]; /** * vector from the triangle vertices to the projection on the triangle * plane. */ private final double[][] vectToVertices = new double[3][3]; /** The cross product between vectToVertices and edges. */ private final double[][] crossProducts = new double[3][3]; private double sqrDistance; /** Max projection distance. */ public double sqrMaxDistance = Double.MAX_VALUE; /** Smallest allowed distance from vertex and edge for FACE project. */ public double sqrTolerance = 0.01; public boolean boundaryOnly; /** * The relative abscissa of the projected point on each edges. * If this is between 0 and 1 the point is on the edge. * If this is lower 0 or greater than 1 the point is on the edge line. * If this is NaN the distance between the point and the edge line is * greater than sqrTolerance. * <ul> * <li>edgeAbscissa[0] is for getAbstractHalfEdge().prev()</li> * <li>edgeAbscissa[1] is for getAbstractHalfEdge()</li> * <li>edgeAbscissa[2] is for getAbstractHalfEdge().next()</li> * </ul> */ public double[] edgeAbscissa = new double[3]; public boolean[] onEdgeLine = new boolean[3]; public void project(Location point, Triangle triangle) { project(point, new TriangleHelper(triangle)); } public void projectOnBoundary(Location point, Triangle t) { double sqrClosestEdgeDistance = Double.MAX_VALUE; AbstractHalfEdge currentEdge = t.getAbstractHalfEdge(); type = ProjectionType.FAR; for (int i = 0; i < 3; i++) { assert currentEdge != null: t; if(currentEdge.hasAttributes(AbstractHalfEdge.BOUNDARY)) { //use vectToVertices to store temporary vectors currentEdge.destination().sub(currentEdge.origin(), vectToVertices[0]); double triaEdgeSqrNorm = Matrix3D.prodSca(vectToVertices[0], vectToVertices[0]); point.sub(currentEdge.origin(), vectToVertices[1]); double r = Matrix3D.prodSca(vectToVertices[0], vectToVertices[1]) / triaEdgeSqrNorm; for(int j = 0; j < 3; j++) vectToVertices[2][j] = - vectToVertices[1][j] + vectToVertices[0][j] * r; sqrDistance = Matrix3D.prodSca(vectToVertices[2], vectToVertices[2]); if(sqrDistance < sqrMaxDistance && sqrDistance < sqrClosestEdgeDistance) { sqrClosestEdgeDistance = sqrDistance; projection.moveTo(vectToVertices[2][0], vectToVertices[2][1], vectToVertices[2][2]); projection.add(point); if(currentEdge.origin().sqrDistance3D(projection) < sqrTolerance) { type = ProjectionType.VERTEX; vertexProject = currentEdge.origin(); } else if(projection.sqrDistance3D(currentEdge.destination()) < sqrTolerance) { type = ProjectionType.VERTEX; vertexProject = currentEdge.destination(); } else if(r >= 0 && r <= 1) { type = ProjectionType.EDGE; } else { type = ProjectionType.OUT; } edgeProject = currentEdge; } } currentEdge = currentEdge.next(); } } public void project(Location point, TriangleHelper th) { if(boundaryOnly) { projectOnBoundary(point, th.getTriangle()); return; } // use vectToVertices[0] as a temporary vector point.sub(th.getTriangle().getV0(), vectToVertices[0]); double amDotAN = Matrix3D.prodSca(th.normal, vectToVertices[0]); double alpha = amDotAN / th.normAN2; sqrDistance = amDotAN * alpha; projection.moveTo(point.getX() - alpha * th.normal[0], point.getY() - alpha * th.normal[1], point.getZ() - alpha * th.normal[2]); for (int i = 0; i < 3; i++) { projection.sub(th.getTriangle().getV(i), vectToVertices[i]); dist2ToVertices[i] = Matrix3D.prodSca(vectToVertices[i], vectToVertices[i]); if (dist2ToVertices[i] < sqrTolerance) { if(sqrDistance < sqrMaxDistance) { vertexProject = th.getTriangle().getV(i); type = ProjectionType.VERTEX; } else type = ProjectionType.FAR; return; } } edgeProject = th.getTriangle().getAbstractHalfEdge().prev(); for (int i = 0; i < 3; i++) { assert th.getTriangle().getV(i) == edgeProject.origin(); Matrix3D.prodVect3D(th.edges[i], vectToVertices[i], crossProducts[i]); double edgeNorm = th.getEdgeNorm(i); double normMP2 = Matrix3D.prodSca(crossProducts[i], crossProducts[i]) / edgeNorm; // Compute edgeAbsissa because even if don't need them right here // we may need them in the outVertex method. double dot = Matrix3D.prodSca(vectToVertices[i], th.edges[i]); dot /= edgeNorm; edgeAbscissa[i] = dot; onEdgeLine[i] = normMP2 < sqrTolerance; if (onEdgeLine[i]) { if(sqrDistance >= sqrMaxDistance) { type = ProjectionType.FAR; return; } type = ProjectionType.EDGE; if (dot > 0 && dot < 1) { projection.moveTo(th.getTriangle().getV(i).getX() + dot * th.edges[i][0], th.getTriangle().getV(i).getY() + dot * th.edges[i][1], th.getTriangle().getV(i).getZ() + dot * th.edges[i][2]); return; } } edgeProject = edgeProject.next(); } for (int i = 0; i < 3; i++) { if (Matrix3D.prodSca(crossProducts[i], th.normal) < 0) { type = ProjectionType.OUT; return; } } if(sqrDistance < sqrMaxDistance) type = ProjectionType.FACE; else type = ProjectionType.FAR; } /** * Return the distance between the point and it's projection on the * triangle plane. * As the projection may be fused to close edges or vertices this * distance may not be exactly the one between the projected point * and getProjection(). */ public double getSqrDistance() { return sqrDistance; } public Location getProjection() { return projection; } public Vertex getVertex() { assert type == ProjectionType.VERTEX; return vertexProject; } public AbstractHalfEdge getEdge() { assert type == ProjectionType.EDGE; return edgeProject; } public ProjectionType getType() { return type; } @Override public String toString() { return "TriangleProjector{" + "projection=" + projection + ", type=" + type + ", sqrDistance=" + sqrDistance + '}'; } public void reset() { type = ProjectionType.FAR; sqrDistance = Double.POSITIVE_INFINITY; } }