/*
* 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.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
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.traits.MeshTraitsBuilder;
import org.jcae.mesh.amibe.util.HashFactory;
/**
*
* @author Jerome Robert
*/
class VertexMerger {
private final Map<Vertex, Collection<Triangle>> map = HashFactory.createMap(10);
private final Collection<AbstractHalfEdge> edgeToClear= HashFactory.createSet();
private final List<Triangle> addedTriangles = new ArrayList<Triangle>();
private final Collection<Collection<Triangle>> trianglesByFan = new ArrayList<Collection<Triangle>>();
private final Set<Triangle> triToRemove = HashFactory.createSet();
/** add neigbour triangle of the vertex to the map */
private void indexify(Vertex vertex)
{
Iterator<Triangle> it = vertex.getNeighbourIteratorTriangle();
while(it.hasNext())
{
Triangle t = it.next();
if(t.hasAttributes(AbstractHalfEdge.OUTER))
continue;
AbstractHalfEdge e = t.getAbstractHalfEdge();
for(int i = 0; i < 3; i++)
{
Vertex v = t.getV(i);
Collection<Triangle> l = map.get(v);
if(l == null)
{
l = new ArrayList<Triangle>(10);
map.put(v, l);
}
if(!l.contains(t))
l.add(t);
if(e.origin() == vertex || e.destination() == vertex)
edgeToClear.add(e);
e = e.next();
}
}
}
private void indexify3(Mesh mesh, Collection<Triangle> triangles)
{
for(Triangle t:triangles)
{
for(int i = 0; i < 3; i++)
{
Vertex v = t.getV(i);
Collection<Triangle> l = map.get(v);
if(l == null)
{
l = new ArrayList<Triangle>(10);
map.put(v, l);
}
assert !t.hasAttributes(AbstractHalfEdge.OUTER);
if(!l.contains(t))
l.add(t);
}
}
}
/** add neigbour triangle of the neighbour vertices of v to the map */
private void indexify2(Mesh mesh, Vertex vertex)
{
for(Entry<Vertex, Collection<Triangle>> e: map.entrySet())
{
if(e.getKey() == vertex)
continue;
Vertex toIndex = e.getKey();
Iterator<Triangle> itt = toIndex.getNeighbourIteratorTriangle();
Collection<Triangle> l = map.get(toIndex);
assert l != null: toIndex+" "+vertex;
while(itt.hasNext())
{
Triangle t = itt.next();
if(!t.hasAttributes(AbstractHalfEdge.OUTER) && !l.contains(t))
l.add(t);
}
// Mesh.rebuildVertexLinks only support manifold link
if(!toIndex.isManifold())
{
Triangle[] link = (Triangle[]) toIndex.getLink();
toIndex.setLink(link[0]);
}
}
}
private void clearAdjacency(Mesh mesh)
{
triToRemove.clear();
for(AbstractHalfEdge e:edgeToClear)
{
AbstractHalfEdge s = e.sym();
if(e.hasAttributes(AbstractHalfEdge.OUTER))
triToRemove.add(e.getTri());
else
{
e.glue(null);
e.clearAttributes(AbstractHalfEdge.BOUNDARY);
e.clearAttributes(AbstractHalfEdge.NONMANIFOLD);
}
if(s != null)
{
if(s.hasAttributes(AbstractHalfEdge.OUTER))
triToRemove.add(s.getTri());
else
{
s.glue(null);
s.clearAttributes(AbstractHalfEdge.BOUNDARY);
s.clearAttributes(AbstractHalfEdge.NONMANIFOLD);
}
}
}
for(Triangle t: triToRemove)
mesh.remove(t);
}
//for debugging
private void checkMap()
{
for(Entry<Vertex, Collection<Triangle>> e:map.entrySet())
{
for(Triangle t:e.getValue())
{
assert !t.hasAttributes(AbstractHalfEdge.OUTER);
assert t.getV0() == e.getKey() || t.getV1() == e.getKey() || t.getV2() == e.getKey();
}
assert e.getKey().isManifold(): e.getKey();
}
int n = 0;
Collection<Triangle> test = HashFactory.createSet();
for(Collection<Triangle> l:trianglesByFan)
{
n += l.size();
test.addAll(l);
}
assert n == test.size();
}
private void connectBoundaryTriangles(Mesh mesh,
Iterable<AbstractHalfEdge> edges, Collection<Triangle> newOuterTriangles)
{
for(AbstractHalfEdge ot: edges)
{
Triangle t = ot.createBoundaryTriangle(mesh);
if(t != null)
newOuterTriangles.add(t);
}
}
/**
* Debug method to check that in trianglesByFan each triangles is in only
* one fan
*/
private void checkFan(Vertex mainVertex)
{
Map<Triangle, Integer> checkMap = HashFactory.createMap();
int fanID = 0;
for(Collection<Triangle> l: trianglesByFan)
{
System.err.println("fan "+fanID);
for(Triangle t: l)
{
System.err.println(t.getGroupId());
checkMap.put(t, fanID);
}
fanID ++;
}
Set<Integer> fanIDs = HashFactory.createSet();
//check that each vertex is in only one fan
for(Entry<Vertex, Collection<Triangle>> e: map.entrySet())
{
for(Triangle t:e.getValue())
fanIDs.add(checkMap.get(t));
//assert fanIDs.size() == 1: e.getKey()+" "+fanIDs+" "+mainVertex;
fanIDs.clear();
}
}
/** Debug method to check that half edge sym() is valid */
public static void checkHalfEdge(Mesh mesh, Vertex v)
{
for(Triangle t : mesh.getTriangles())
{
AbstractHalfEdge e = t.getAbstractHalfEdge();
for(int i = 0; i < 3; i++)
{
if(e.hasSymmetricEdge() && e.origin() != mesh.outerVertex &&
e.destination() != mesh.outerVertex)
{
assert e.origin() == e.sym().destination();
assert e.destination() == e.sym().origin(): e.sym()+" "+v;
}
e = e.next();
}
}
}
/** Convert non manifold edges around v to boundary edges */
public void unmerge(Mesh mesh, Vertex v)
{
edgeToClear.clear();
addedTriangles.clear();
map.clear();
trianglesByFan.clear();
if(v.isManifold())
return;
for(Triangle t: (Triangle[])v.getLink())
{
ArrayList<Triangle> fan = new ArrayList<Triangle>();
AbstractHalfEdge current = v.getIncidentAbstractHalfEdge(t, null);
Vertex startV = current.destination();
do
{
if(!current.hasAttributes(AbstractHalfEdge.OUTER))
{
assert current.getTri().getV0() == v ||
current.getTri().getV1() == v ||
current.getTri().getV2() == v;
fan.add(current.getTri());
}
assert current.getTri() != null;
edgeToClear.add(current);
edgeToClear.add(current.prev());
current = current.nextOriginLoop();
}
while(current.destination() != startV);
trianglesByFan.add(fan);
}
clearAdjacency(mesh);
boolean first = true;
for(Collection<Triangle> fan: trianglesByFan)
{
assert fan.size() == HashFactory.createSet(fan).size();
Vertex newV = first ? v : mesh.createVertex(v);
first = false;
for(Triangle t:fan)
{
assert t.getV0() == v || t.getV1() == v || t.getV2() == v;
for(int i = 0; i < 3; i++)
{
if(t.getV(i) == v)
t.setV(i, newV);
}
newV.setLink(t);
}
indexify3(mesh, fan);
}
mesh.glueSymmetricHalfEdges(map, addedTriangles);
connectBoundaryTriangles(mesh, edgeToClear, addedTriangles);
for(Triangle t:addedTriangles)
mesh.add(t);
indexify2(mesh, v);
Mesh.rebuildVertexLinks(map);
}
public void merge(Mesh mesh, Location target, Vertex ... vertices)
{
map.clear();
addedTriangles.clear();
edgeToClear.clear();
for(Vertex v:vertices)
indexify(v);
clearAdjacency(mesh);
Vertex targetV = vertices[vertices.length - 1];
Collection<Triangle> targetL = map.get(targetV);
for(int i = 0; i < vertices.length - 1; i++)
{
Collection<Triangle> l = map.get(vertices[i]);
for(Triangle t:l)
{
for(int j = 0; j < 3; j++)
{
assert t.getV(j) != targetV: "Cannot merge "+vertices[i]+" to "+targetV;
if(t.getV(j) == vertices[i])
t.setV(j, targetV);
}
}
map.remove(vertices[i]);
targetL.addAll(l);
}
mesh.glueSymmetricHalfEdges(map, addedTriangles);
connectBoundaryTriangles(mesh, edgeToClear, addedTriangles);
for(Triangle t:addedTriangles)
mesh.add(t);
indexify2(mesh, targetV);
// Mesh.rebuildVertexLinks only support manifold link
if(!targetV.isManifold())
targetV.setLink(((Triangle[])targetV.getLink())[0]);
Mesh.rebuildVertexLinks(map);
targetV.moveTo(target);
}
}