/*
* 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 2014, by Airbus Group SAS
*/
package org.jcae.mesh.amibe.projection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jcae.mesh.amibe.algos3d.TriMultPoly;
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.traits.MeshTraitsBuilder;
import org.jcae.mesh.xmldata.Amibe2VTK;
import org.jcae.mesh.xmldata.MeshReader;
import org.jcae.mesh.xmldata.MeshWriter;
/**
* Insert an edge into a triangulation.
* Triangles under the edge are removed, then the created hole is remeshed
* taking the inserted edge into account.
* @author Jerome Robert
*/
public class EdgeProjectorNG {
private final Mesh mesh;
private final TriangleKdTree kdTree;
private final double tolerance;
private final EdgeTrianglesLocator triangleFinder;
private final TriMultPoly triMultPoly = new TriMultPoly();
private final HoleFiller holeFiller = new HoleFiller();
public EdgeProjectorNG(Mesh mesh, TriangleKdTree kdTree, double tolerance) {
this.mesh = mesh;
this.kdTree = kdTree;
this.tolerance = tolerance;
triangleFinder = new EdgeTrianglesLocator(kdTree) {
@Override
protected boolean isValidTriangle(Triangle t) {
return isProjectionAllowed(t);
}
};
}
/** Ensure that removing triangles would not remove not removable edges */
private boolean checkInternalEdges(Collection<Triangle> triangles) {
boolean toReturn = true;
Iterator<Triangle> it = triangles.iterator();
mainLoop: while(it.hasNext()) {
Triangle t = it.next();
AbstractHalfEdge e = t.getAbstractHalfEdge();
for(int i = 0; i < 3; i++) {
boolean isInternalEdge = triangles.contains(e.sym().getTri());
if(isInternalEdge && !isEdgeRemovable(e)) {
it.remove();
toReturn = false;
continue mainLoop;
}
e = e.next();
}
}
return toReturn;
}
public AbstractHalfEdge project(Vertex v1, Vertex v2, int group)
{
assert v1 != null;
assert v2 != null;
triangleFinder.locate(v1, v2, group, tolerance);
ArrayList<AbstractHalfEdge> border = new ArrayList<AbstractHalfEdge>();
for(Triangle t:triangleFinder.getResult())
{
AbstractHalfEdge e = t.getAbstractHalfEdge();
for(int i = 0; i < 3; i++)
{
if(!triangleFinder.getResult().contains(e.sym().getTri()))
border.add(e);
e = e.next();
}
}
if(!checkInternalEdges(triangleFinder.getResult()))
return null;
if(border.isEmpty())
return null;
holeFiller.triangulate(mesh, border,
Collections.singleton(Arrays.asList(v1, v2)));
for(Triangle t:triangleFinder.getResult())
{
mesh.remove(t);
kdTree.remove(t);
}
for(Triangle t:holeFiller.getNewTriangles())
{
mesh.add(t);
kdTree.addTriangle(t);
}
assert mesh.isValid();
return holeFiller.getEdge(v1, v2);
}
private boolean checkVertexLinks() {
for(Triangle t: mesh.getTriangles())
{
for(int i = 0; i < 3; i++) {
Vertex v = t.getV(i);
if(v.getLink() == null && v != mesh.outerVertex) {
System.err.println("invalid vertex: "+v);
return false;
}
}
}
return true;
}
public void projectTriMultPoly(Vertex v1, Vertex v2, int group) throws IOException
{
//TODO make it work with delauney
triMultPoly.setDelauneyTetra(false);
triangleFinder.locate(v1, v2, group, tolerance);
ArrayList<AbstractHalfEdge> border = new ArrayList<AbstractHalfEdge>();
for(Triangle t:triangleFinder.getResult())
{
AbstractHalfEdge e = t.getAbstractHalfEdge();
for(int i = 0; i < 3; i++)
{
if(!triangleFinder.getResult().contains(e.sym().getTri()))
border.add(e);
e = e.next();
}
}
Vertex[] vToAdd = new Vertex[3];
vToAdd[0] = v2;
vToAdd[1] = v1;
// Add a 3rd point so that the polyline is not degenerated
vToAdd[2] = mesh.createVertex(
v1.getX() - 1E-9 * (v1.getX() - v2.getX()),
v1.getY(), v1.getZ());
vToAdd[2] = mesh.createVertex(v1.getX(), v2.getY(), v1.getZ());
triMultPoly.triangulate(mesh, border,
Collections.singleton(Arrays.asList(vToAdd)));
AbstractHalfEdge e = triMultPoly.getEdge(v1, vToAdd[2]);
for(Triangle t:triMultPoly.getNewTriangles())
mesh.add(t);
for(Triangle t:triangleFinder.getResult())
mesh.remove(t);
assert mesh.isValid();
mesh.edgeCollapse(e, v1);
}
/**
* Return true is the projection can be done on this triangle.
* This methods aims at being redefine in subclasses.
* The default implementation return true.
*/
protected boolean isProjectionAllowed(Triangle triangle)
{
return true;
}
protected boolean isEdgeRemovable(AbstractHalfEdge edge) {
return true;
}
public static void test()
{
Mesh m = new Mesh(MeshTraitsBuilder.getDefault3D());
Vertex v1 = m.createVertex(0,0,0);
Vertex v2 = m.createVertex(1,0,0);
Vertex v3 = m.createVertex(0,1,0);
Vertex v4 = m.createVertex(0.1,0.1,0);
Vertex v5 = m.createVertex(0.2,0.2,0);
m.add(m.createTriangle(v1, v2, v3));
m.buildAdjacency();
TriangleKdTree mkdTree = new TriangleKdTree(m);
EdgeProjectorNG projector = new EdgeProjectorNG(m, mkdTree, 0.1);
projector.project(v4, v5, -1);
assert m.isValid();
}
public static void main(final String[] args) {
try {
test();
String file = "/tmp/A350-1000_FULL-Config_Masked_Shape3422.amibe";
Mesh m = new Mesh(MeshTraitsBuilder.getDefault3D());
MeshReader.readObject3D(m, file);
Vertex v1 = new Vertex(null, 5969.1334634938175, 602.1781046704872, -2639.907227074926);
Vertex v2 = new Vertex(null, 5974.089704834468, 576.5380328193008, -2675.8479767020112);
TriangleKdTree mkdTree = new TriangleKdTree(m);
EdgeProjectorNG projector = new EdgeProjectorNG(m, mkdTree, 0.1);
projector.project(v1, v2, -1);
String tmpFile = "/tmp/debug.amibe";
MeshWriter.writeObject3D(m, tmpFile, null);
new Amibe2VTK(tmpFile).write("/tmp/debug.vtp");
} catch (Exception ex) {
Logger.getLogger(EdgeProjectorNG.class.getName()).log(Level.SEVERE, null, ex);
}
}
}