/*
* 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 static org.jcae.mesh.stitch.TriangleHelper.isOnEdge;
/**
*
* @author Jerome Robert
*/
class TriangleSplitter {
private TriangleHelper triangleHelper;
private final Location splitPoint = new Location();
private AbstractHalfEdge toSplit;
private Vertex vertex;
public void setTriangle(TriangleHelper th)
{
this.triangleHelper = th;
}
public Vertex getSplitVertex(Mesh mesh)
{
if(vertex == null)
{
if(toSplit == null)
return null;
else
return mesh.createVertex(splitPoint);
}
else
return vertex;
}
public AbstractHalfEdge getSplittedEdge()
{
return toSplit;
}
/**
* Return a point of the projected segment which is close to the split point
* @param b the projection of c on the triangle
* @param c a point of the projected segment
* @param d a point of the projected segment
* @return
*/
public double getReverseSplitPoint(Location b, Location c, Location d, Location result)
{
Location e = vertex;
if( e == null )
if(toSplit == null)
return Double.POSITIVE_INFINITY;
else
e = splitPoint;
return lineToLineDistance(b, c, d, e, result);
}
private boolean isVertex(double u, double v, double sqrTol)
{
splitPoint.moveTo(triangleHelper.getLocation(u, v));
if (splitPoint.sqrDistance3D(toSplit.origin()) < sqrTol)
{
vertex = toSplit.origin();
toSplit = null;
return true;
}
else if (splitPoint.sqrDistance3D(toSplit.destination()) < sqrTol)
{
vertex = toSplit.destination();
toSplit = null;
return true;
}
else
{
vertex = null;
return false;
}
}
/**
* Intersect an edge of the triangle with the (apex, point=) segment.
* @param apex the apex of the edge to intersect
* @param p the other point of the segment
*/
public void splitApex(Vertex apex, Location p, double sqrTol) {
Triangle triangle = triangleHelper.getTriangle();
assert triangle.getV0().sqrDistance3D(p) > 1E-24;
assert triangle.getV1().sqrDistance3D(p) > 1E-24;
assert triangle.getV2().sqrDistance3D(p) > 1E-24;
double[] uv = new double[2];
triangleHelper.getBarycentricCoords(p, uv);
double u = 0;
double v = 0;
if (apex == triangle.getV2()) {
if(uv[0] > 1)
{
toSplit = null;
vertex = null;
return;
}
//intersect the segment u = 0
toSplit = triangle.getAbstractHalfEdge().prev(); // 0->1 = V
v = uv[1] / (1 - uv[0]);
if(!isVertex(u, v, sqrTol) && (v < 0 || v > 1 || uv[0] > 1 ))
{
toSplit = null;
}
else
{
assert toSplit == null || toSplit.origin() == triangle.getV0() :
toSplit.origin() + "\n" + triangle;
assert toSplit == null || toSplit.destination() == triangle.getV1();
assert vertex != null || isOnEdge(splitPoint, apex, p, sqrTol): u+" "+v+" "+Arrays.toString(uv);
assert vertex == null || isOnEdge(vertex, apex, p, sqrTol):
u + " " + v + " " + Arrays.toString(uv) + "\n" + vertex + "\n" + apex + "\n" + p;
}
} else if (apex == triangle.getV1()) {
if(uv[1] > 1)
{
toSplit = null;
vertex = null;
return;
}
//intersect the segment v = 0
toSplit = triangle.getAbstractHalfEdge().next(); // 0->2 = -U
u = uv[0] / (1 - uv[1]);
if(!isVertex(u, v, sqrTol) && (u < 0 || u > 1))
{
toSplit = null;
}
else
{
assert toSplit == null || toSplit.origin() == triangle.getV2();
assert toSplit == null || toSplit.destination() == triangle.getV0();
assert isOnEdge(splitPoint, apex, p, sqrTol): u+" "+v+" "+Arrays.toString(uv);
}
} else if(apex == triangle.getV0())
{
if(uv[0] + uv[1] < 0)
{
toSplit = null;
vertex = null;
return;
}
//intersect the segement u+v = 1
toSplit = triangle.getAbstractHalfEdge(); // 1->2
double tt = uv[0] + uv[1];
u = uv[0] / tt;
v = uv[1] / tt;
if(!isVertex(u, v, sqrTol) && (u < 0 || v < 0 || uv[0] < 0 || uv[1] < 0))
{
toSplit = null;
}
else
{
assert toSplit == null || toSplit.origin() == triangle.getV1();
assert toSplit == null || toSplit.destination() == triangle.getV2();
assert isOnEdge(splitPoint, apex, p, sqrTol): u+" "+v+" "+Arrays.toString(uv);
}
}
else
{
//the apex is not in the triangle so
toSplit = null;
vertex = null;
}
}
/**
* Given a line L1: D+t*DC and L2 E+t*BC find the point of L1 which is the
* closest of L2 and return the distance
* @param b A point of the direction of L2
* @param c A point of L1 and of the direction of L2
* @param d A point of L1
* @param e A point of L2
* @param f set to the point of L1 which is the closest of L2
* @return the square of the distance between L1 and L2
*/
private static double lineToLineDistance(Location b, Location c,
Location d, Location e, Location f)
{
double a01 = 0;
double b0 = 0;
double cc = 0;
double b1 = 0;
double normd1 = 0;
double normd2 = 0;
//use e as temporary storage for the direction of L1
f.moveTo(c.getX() - d.getX(),
c.getY() - d.getY(),
c.getZ() - d.getZ());
for(int i = 0; i < 3; i++)
{
double diff = d.get(i) - e.get(i);
double d1 = f.get(i);
double d2 = c.get(i) - b.get(i);
normd1 += d1 * d1;
normd2 += d2 * d2;
a01 -= d1 * d2;
b0 += diff * d1;
cc += diff * diff;
b1 -= diff * d2;
}
double s0;
if(normd2 < 1E-10)
{
//L2 is degenerated so just project e to L1
s0 = - b0 / Math.sqrt(cc);
}
else
{
normd1 = Math.sqrt(normd1);
normd2 = Math.sqrt(normd2);
b0 /= normd1;
b1 /= normd2;
a01 /= normd1 * normd2;
s0 = (a01 * b1 - b0) / Math.abs(1 - a01 * a01);
}
s0 /= normd1;
f.moveTo(
d.getX() + s0 * f.getX(),
d.getY() + s0 * f.getY(),
d.getZ() + s0 * f.getZ());
double sqrDist = f.sqrDistance3D(e);
assert s0 > 0 && s0 < 1 : "\ns0 :" + s0 + "\nb: " + b + "\nc: " + c +
"\nd: " + d + "\ne: " + e + "\ndist: " + Math.sqrt(sqrDist) +
"\n" + "normd1: " + normd1 + "\nnormd2: " + normd2 + "\n";
return sqrDist;
}
/**
* Intersection an edge of the triangle with the (p1, p2) segment
* @param sqrTol update splitVertex property instead of splittedEdge if the
* split point is closer from a triangle vertex than this distance
* @return the intersected edge of the triangle
*/
public void split(Location p1, Location p2, double sqrTol)
{
toSplit = null;
vertex = null;
Triangle triangle = triangleHelper.getTriangle();
double[] uvO = new double[2];
double[] uvD = new double[2];
triangleHelper.getBarycentricCoords(p1, uvO);
triangleHelper.getBarycentricCoords(p2, uvD);
double deltaU = uvD[0] - uvO[0];
double deltaV = uvD[1] - uvO[1];
double u = 0;
double v = 0;
//intersect the segment u = 0
if (uvO[0] < 0 && uvD[0] > 0) {
v = uvO[1] - uvO[0] / deltaU * deltaV;
toSplit = triangle.getAbstractHalfEdge().prev(); // 0->1 = V
if(!isVertex(u, v, sqrTol) && (v < 0 || v > 1))
toSplit = null;
} else {
//intersect the segment v = 0
if (uvO[1] < 0 && uvD[1] > 0) {
u = uvO[0] - uvO[1] / deltaV * deltaU;
toSplit = triangle.getAbstractHalfEdge().next(); // 0->2 = -U
if(!isVertex(u, v, sqrTol) && (u < 0 || u > 1))
toSplit = null;
} else {
double tt = (1 - uvO[0] - uvO[1]) / (deltaU + deltaV);
if (tt > 0 && tt < 1) {
toSplit = triangle.getAbstractHalfEdge(); // 1->2
u = uvO[0] + tt * deltaU;
v = uvO[1] + tt * deltaV;
assert Math.abs(u + v - 1) < 1E-4: tt + " " + (u + v);
if(!isVertex(u, v, sqrTol))
{
if (!(v > 0 && u > 0))
toSplit = null;
}
}
}
}
assert !(toSplit != null && vertex != null);
if (toSplit != null) {
assert isOnEdge(splitPoint, toSplit.origin(), toSplit.destination(), sqrTol):
triangle + "\np1:" + p1 + "\np2:" + p2 + "\nuv:" +
Arrays.toString(uvO) + " " + Arrays.toString(uvD)+"\nsqrTol: "+sqrTol+"\nu+v: "+(u+v)+" "+(deltaU+deltaV);
assert isOnEdge(splitPoint, p1, p2, 10000) : Arrays.toString(uvO) + " " + Arrays.toString(uvD);
}
}
}