package ProGAL.geom3d.complex.delaunayComplex;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ProGAL.geom3d.Point;
import ProGAL.geom3d.volumes.Sphere;
import ProGAL.geom3d.Vector;
import ProGAL.geom3d.complex.CEdge;
import ProGAL.geom3d.complex.CTriangle;
import ProGAL.geom3d.complex.CVertex;
import ProGAL.geom3d.complex.CTetrahedron;
import ProGAL.geom3d.complex.SimplicialComplex;
import ProGAL.geom3d.predicates.*;
import ProGAL.geom3d.predicates.Predicates.SphereConfig;
import ProGAL.math.Randomization;
/**
* A Delaunay complex for a set of d-dimensional points is a tesselation of the points such that no point is inside
* the circumscribing hypersphere of the d-simplices (for the 3D case: Tetrahedra).
*
*
*
* This class builds a three-dimensional Delaunay complex in the constructor and accesses it using e.g. the
* getTetrahedra
method. The following example displays the Delaunay complex of ten random points.
*
* {@code
* //Generate the complex
* List pl = PointList.generatePointsInCube(10);
* DelaunayComplex dc = new DelaunayComplex(pl);
*
* //Display the complex
* J3DScene scene = J3DScene.createJ3DSceneInFrame();
* for(CTetrahedron t: dc.getTetrahedra()){
* scene.addShape(t, new Color(200,100,100,100));
* }
* }
*
*
* The original point-set is left unaltered and non-referenced by this class. A new set of vertices is
* allocated using the CVertex class. These are randomly perturbed to avoid degeneracies. If one wishes to
* associate the original points with a vertex in the complex it would be sufficient to test if the distance
* between the point and the vertex is less than 0.0001.
*
* The complex is bounded by a big tetrahedron whose corner-points are located sufficiently far from any of
* the vertices of the complex. The simplices that have one of these 'big points' as corners can not be accessed
* directly via the getter-methods, but they will be neighbors of other normal simplices. For instance:
*
* DelaunayComplex dc = new DelaunayComplex( PointList.generatePointsInCube(4) );
* for(CTetrahedron t: dc.getTetrahedra()){
* System.out.println( t.containsBigPoint() );
* System.out.println( t.getNeighbor(0).containsBigPoint() );
* System.out.println( t.getNeighbor(1).containsBigPoint() );
* System.out.println( t.getNeighbor(2).containsBigPoint() );
* System.out.println( t.getNeighbor(3).containsBigPoint() );
* }
*
* Will print false, true, true, true and true.
*
* @author R.Fonseca
*/
public class DelaunayComplex implements SimplicialComplex{
private final List points;
private final List edges;
private final List triangles;
private final List tetrahedra;
private final Predicates predicates;
private final Walk walk;
private final Flip14 f14;
private final Flips flips;
/** Builds the Delaunay complex of the specified point-set */
public DelaunayComplex(List points) {
this.points = new ArrayList(points.size());
int i=0;
for(Point p: points) this.points.add(new CVertex(p, i++));
this.edges = new ArrayList(points.size()*6);
this.triangles = new ArrayList(points.size()*6);
this.tetrahedra = new ArrayList(points.size()*6);
this.predicates = new ExactJavaPredicates();
this.walk = new Walk(predicates);
this.flips = new Flips(predicates);
this.f14 = new Flip14(flips);
compute();
completeComplex();
}
/** TODO: Finish */
public boolean isDelaunay() {
for (CTetrahedron tetr : tetrahedra) {
Sphere sphere = new Sphere(tetr);
}
return true;
}
/** Get the tetrahedra in the complex. The tetrahedra that has 'big points' as corners are not returned */
public List getTetrahedra() {
return new ArrayList(tetrahedra);
}
/** returns all tetrahedra (including tetrahedra with bigPoint */
public List getAllTetrahedra() {
List allTetrahedra = new ArrayList();
for (CVertex v : getVertices() ) {
List adjacentTetrahedra = v.getAllAdjacentTetrahedra();
for (CTetrahedron tetr : adjacentTetrahedra) {
if (!allTetrahedra.contains(tetr)) allTetrahedra.add(tetr);
}
}
return allTetrahedra;
}
/** returns all big tetrahedra */
public List getBigTetrahedra() {
List bigTetrahedra = new ArrayList();
for (CVertex v : getVertices() ) {
List adjacentTetrahedra = v.getAllAdjacentTetrahedra();
for (CTetrahedron tetr : adjacentTetrahedra) {
if (tetr.containsBigPoint() && !bigTetrahedra.contains(tetr)) bigTetrahedra.add(tetr);
}
}
return bigTetrahedra;
}
/** Get the triangles in the complex. The triangles that has 'big points' as corners are not returned */
public List getTriangles(){
return new ArrayList(triangles);
}
/** Get the edges in the complex. The edges that has 'big points' as end-points are not returned */
public List getEdges(){
return new ArrayList(edges);
}
/** Get the vertices in the complex. The 'big points' are not returned */
public List getVertices(){
return new ArrayList(points);
}
public CVertex getVertex(int i) { return points.get(i); }
protected void compute() {
double max = 1000;//TODO find a more meaningful max
//TODO: Take care of degeneracies in a better way than perturbation
for(CVertex v: points){
v.addThis(new Vector(
Randomization.randBetween(-0.00001, 0.00001),
Randomization.randBetween(-0.00001, 0.00001),
Randomization.randBetween(-0.00001, 0.00001)
)
);
}
//Find the enclosing tetrahedron
CTetrahedron next_t = new FirstTetrahedron(max);
flips.addTetrahedron(next_t);
//Iterer over punkterne
for(CVertex p: points){
next_t = walk.walk(next_t, p);
next_t = f14.flip14(next_t, p);
CTetrahedron tmp = flips.fixDelaunay();
if (tmp != null)
next_t = tmp;
}
}
/** Add edges and triangles and remove auxiliary tetrahedra. */
protected void completeComplex() {
tetrahedra.clear();
triangles.clear();
edges.clear();
//Add the non-modified tetrahedra that doesnt contain one of the big points
for(CTetrahedron t: flips.getTetrahedrastack()){
if (!t.isModified() && !t.containsBigPoint()){
tetrahedra.add(t);
}
}
// flips.setTetrahedrastack(null);//Should free up some memory after garbage collection
// flips.getTetrahedrastack().clear();
class VertexPair{
CVertex p1, p2;
VertexPair(CVertex p1, CVertex p2){
this.p1 = p1;
this.p2 = p2;
}
public boolean equals(Object o){
return (((VertexPair)o).p1==p1 && ((VertexPair)o).p2==p2)||(((VertexPair)o).p1==p2 && ((VertexPair)o).p2==p1);
}
public int hashCode(){
return p1.hashCode()^p2.hashCode();
}
}
//Construct edges
Map edgeMap = new HashMap();
for(CTetrahedron t: tetrahedra){
edgeMap.put( new VertexPair(t.getPoint(0),t.getPoint(1)),new CEdge(t.getPoint(0),t.getPoint(1)) );
edgeMap.put( new VertexPair(t.getPoint(0),t.getPoint(2)),new CEdge(t.getPoint(0),t.getPoint(2)) );
edgeMap.put( new VertexPair(t.getPoint(0),t.getPoint(3)),new CEdge(t.getPoint(0),t.getPoint(3)) );
edgeMap.put( new VertexPair(t.getPoint(1),t.getPoint(2)),new CEdge(t.getPoint(1),t.getPoint(2)) );
edgeMap.put( new VertexPair(t.getPoint(1),t.getPoint(3)),new CEdge(t.getPoint(1),t.getPoint(3)) );
edgeMap.put( new VertexPair(t.getPoint(2),t.getPoint(3)),new CEdge(t.getPoint(2),t.getPoint(3)) );
}
edges.addAll(edgeMap.values());
for(CEdge e: edges){
((CVertex)e.getA()).addAdjacentEdge(e);
((CVertex)e.getB()).addAdjacentEdge(e);
}
//Construct triangles
Set triangleSet = new HashSet();
for(CTetrahedron t: tetrahedra){
triangleSet.add(new CTriangle(t.getPoint(1),t.getPoint(2),t.getPoint(3), t,t.getNeighbour(0)));
triangleSet.add(new CTriangle(t.getPoint(0),t.getPoint(2),t.getPoint(3), t,t.getNeighbour(1)));
triangleSet.add(new CTriangle(t.getPoint(0),t.getPoint(1),t.getPoint(3), t,t.getNeighbour(2)));
triangleSet.add(new CTriangle(t.getPoint(0),t.getPoint(1),t.getPoint(2), t,t.getNeighbour(3)));
}
triangles.addAll(triangleSet);
for(CTriangle t: triangles){
CEdge e1 = edgeMap.get(new VertexPair((CVertex)t.getPoint(0),(CVertex)t.getPoint(1)));
CEdge e2 = edgeMap.get(new VertexPair((CVertex)t.getPoint(1),(CVertex)t.getPoint(2)));
CEdge e3 = edgeMap.get(new VertexPair((CVertex)t.getPoint(2),(CVertex)t.getPoint(0)));
t.setEdge(0, e1);
t.setEdge(1, e2);
t.setEdge(2, e3);
e1.addTriangle(t);
e2.addTriangle(t);
e3.addTriangle(t);
}
//Set faces of tetrahedra
for(CTriangle t: triangles){
CTetrahedron tet = t.getAdjacentTetrahedron(0);
if(tet.getNeighbour(0).containsTriangle(t)) tet.setTriangle(0, t);
else if(tet.getNeighbour(1).containsTriangle(t)) tet.setTriangle(1, t);
else if(tet.getNeighbour(2).containsTriangle(t)) tet.setTriangle(2, t);
else if(tet.getNeighbour(3).containsTriangle(t)) tet.setTriangle(3, t);
tet = t.getAdjacentTetrahedron(1);
if(tet.getNeighbour(0).containsTriangle(t)) tet.setTriangle(0, t);
else if(tet.getNeighbour(1).containsTriangle(t)) tet.setTriangle(1, t);
else if(tet.getNeighbour(2).containsTriangle(t)) tet.setTriangle(2, t);
else if(tet.getNeighbour(3).containsTriangle(t)) tet.setTriangle(3, t);
}
}
/** The vertex-hull of v is the set of all tetrahedrons that has v as a corner-point */
public Set getVertexHull(CVertex v){
Set hull = new HashSet();
for(CEdge e: v.getAdjacentEdges()){
for(CTriangle tri: e.getAdjacentTriangles()){
hull.add(tri.getAdjacentTetrahedron(0));
hull.add(tri.getAdjacentTetrahedron(1));
}
}
return hull;
}
/** Checks that all tetrahedra comply with the Delaunay-criteria. */
public boolean checkTetrahedra() {
for(CTetrahedron t: tetrahedra){
for(CVertex p: points){
if( t.getPoint(0)!=p &&
t.getPoint(1)!=p &&
t.getPoint(2)!=p &&
t.getPoint(3)!=p &&
predicates.insphere(t, p).equals(SphereConfig.INSIDE) )
{
return false;
}
}
}
return true;
}
}