/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD modeler, Finite element mesher, Plugin architecture. Copyright (C) 2007,2010, by EADS France This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jcae.mesh.amibe.ds; import java.util.Iterator; import static org.junit.Assert.*; import org.junit.Ignore; @Ignore("Utility class") public class AbstractHalfEdgeTest { Mesh mesh; Vertex [] v; private Triangle [] T; private int vertexLabel = 0; private int originalVertexCount = 0; private boolean alterned = true; private void create3x4Shell() { /* * v9 v10 v11 * +---------+---------+ * | \ | / | * | \ T9 | T10 / | * | \ | / | * | T8 \ | / T11 | *v6 +---------+---------+ v8 * | \ |v7 / | * | \ T5 | T6 / | * | \ | / | * | T4 \ | / T7 | *v3 +---------+---------+ v5 * | \ |v4 / | * | \ T1 | T2 / | * | \ | / | * | T0 \ | / T3 | * +---------+---------+ * v0 v1 v2 * */ v = new Vertex[12]; for (int i = 0; i < 4; i++) { v[3*i] = mesh.createVertex(-1.0, i, 0.0); v[3*i+1] = mesh.createVertex(0.0, i, 0.0); v[3*i+2] = mesh.createVertex(1.0, i, 0.0); } for (int i = 0; i < v.length; i++) v[i].setLabel(i); vertexLabel = v.length; originalVertexCount = v.length; T = new Triangle[12]; for (int i = 0; i < 3; i++) { T[4*i] = mesh.createTriangle(v[3*i], v[3*i+1], v[3*i+3]); T[4*i+1] = mesh.createTriangle(v[3*i+1], v[3*i+4], v[3*i+3]); T[4*i+2] = mesh.createTriangle(v[3*i+5], v[3*i+4], v[3*i+1]); T[4*i+3] = mesh.createTriangle(v[3*i+1], v[3*i+2], v[3*i+5]); v[3*i].setLink(T[4*i]); v[3*i+1].setLink(T[4*i]); v[3*i+2].setLink(T[4*i+3]); } v[9].setLink(T[9]); v[10].setLink(T[9]); v[11].setLink(T[11]); int cnt = 0; for (Triangle t: T) { mesh.add(t); t.setGroupId(cnt); cnt++; } } // m Vertex on rows, n Vertex on columns private void createMxNShell(int m, int n) { v = new Vertex[m*n]; for (int j = 0; j < n; j++) for (int i = 0; i < m; i++) v[m*j+i] = mesh.createVertex(i, j, 0.0); for (int i = 0; i < v.length; i++) { v[i].setLabel(vertexLabel); vertexLabel++; } vertexLabel = v.length; originalVertexCount = v.length; T = createMxNTriangles(m, n, v); } private Triangle [] createMxNTriangles(int m, int n, Vertex [] vv) { /* v3 v4 v5 * +---------+---------+ * | \ | \ | * | \ T1 | \ T3 | * | \ | \ | * | T0 \ | T2 \ | * +---------+---------+ * v0 v1 v2 * or * v3 v4 v5 * +---------+---------+ * | / | / | * | T0 / | T2 / | * | / | / | * | / T1 | / T3 | * +---------+---------+ * v0 v1 v2 */ Triangle [] tt = new Triangle[2*(m-1)*(n-1)]; for (int j = 0; j < n-1; j++) { if (j%2 == 0 || !alterned) { for (int i = 0; i < m-1; i++) { tt[2*(m-1)*j+2*i] = mesh.createTriangle(vv[m*j+i], vv[m*j+i+1], vv[m*(j+1)+i]); tt[2*(m-1)*j+2*i+1] = mesh.createTriangle(vv[m*j+i+1], vv[m*(j+1)+i+1], vv[m*(j+1)+i]); vv[m*j+i].setLink(tt[2*(m-1)*j+2*i]); } vv[m*j+m-1].setLink(tt[2*(m-1)*j+2*m-3]); } else { for (int i = 0; i < m-1; i++) { tt[2*(m-1)*j+2*i] = mesh.createTriangle(vv[m*j+i], vv[m*(j+1)+i+1], vv[m*(j+1)+i]); tt[2*(m-1)*j+2*i+1] = mesh.createTriangle(vv[m*j+i+1], vv[m*(j+1)+i+1], vv[m*j+i]); vv[m*j+i].setLink(tt[2*(m-1)*j+2*i]); } vv[m*j+m-1].setLink(tt[2*(m-1)*j+2*m-3]); } } // Last row for (int i = 0; i < m-1; i++) vv[m*(n-1)+i].setLink(tt[2*(m-1)*(n-2)+2*i]); vv[m*n-1].setLink(tt[2*(m-1)*(n-1)-1]); int cnt = mesh.getTriangles().size(); for (Triangle t: tt) { mesh.add(t); t.setGroupId(cnt); cnt++; } return tt; } final Triangle [] rotateMxNShellAroundY(int m, int n, double angle) { // Create new vertices and append them to current mesh assert originalVertexCount == m*n; double [] xyz = new double[3]; Vertex [] vy = new Vertex[originalVertexCount]; double ct = Math.cos(angle*Math.PI / 180.0); double st = Math.sin(angle*Math.PI / 180.0); for (int i = 0; i < vy.length; i++) { if (i%m == 0) vy[i] = v[i]; else { v[i].get(xyz); vy[i] = mesh.createVertex(ct*xyz[0]+st*xyz[2], xyz[1], -st*xyz[0]+ct*xyz[2]); vy[i].setLabel(vertexLabel); vertexLabel++; } } Vertex [] newV = new Vertex[v.length+vy.length]; System.arraycopy(v, 0, newV, 0, v.length); System.arraycopy(vy, 0, newV, v.length, vy.length); v = newV; return createMxNTriangles(m, n, vy); } private void invertTriangles(Triangle [] tArray) { for (Triangle t: tArray) { Vertex temp = t.getV1(); t.setV(1, t.getV2()); t.setV(2, temp); } } final void buildMesh() { createMxNShell(3, 2); mesh.buildAdjacency(); } final void buildMesh2() { create3x4Shell(); mesh.buildAdjacency(); assertTrue("Mesh is not valid", mesh.isValid()); } final void buildMeshTopo() { /* * This mesh is a prism, it can be unrolled as: * v4 v3 v2 v4 * +---------+---------+---------+ * | \ | / | / | * | \ T2 | T1 / | T4 / | * | \ | / | / | * | T3 \ | / T0 | / T5 | * +---------+---------+---------+ * v5 v0 v1 v5 * Edge (v0,v1) cannot be collapsed because T3 * and T5 would then be glued together and * edge (v4,v0) would become non-manifold. */ v = new Vertex[6]; v[0] = mesh.createVertex(0.0, 0.0, 0.0); v[1] = mesh.createVertex(1.0, 0.0, 0.0); v[2] = mesh.createVertex(1.0, 1.0, 0.0); v[3] = mesh.createVertex(0.0, 1.0, 0.0); v[4] = mesh.createVertex(0.0, 1.0, 1.0); v[5] = mesh.createVertex(0.0, 0.0, 1.0); for (int i = 0; i < v.length; i++) v[i].setLabel(i); T = new Triangle[6]; T[0] = mesh.createTriangle(v[0], v[1], v[2]); T[1] = mesh.createTriangle(v[2], v[3], v[0]); T[2] = mesh.createTriangle(v[4], v[0], v[3]); T[3] = mesh.createTriangle(v[5], v[0], v[4]); T[4] = mesh.createTriangle(v[4], v[2], v[1]); T[5] = mesh.createTriangle(v[5], v[4], v[1]); v[0].setLink(T[0]); v[1].setLink(T[0]); v[2].setLink(T[0]); v[3].setLink(T[2]); v[4].setLink(T[2]); v[5].setLink(T[3]); int cnt = 0; for (Triangle t: T) { mesh.add(t); t.setGroupId(cnt); cnt++; } mesh.buildAdjacency(); } final void buildMeshNM(int m, int n, boolean alt) { /* (2, 4, true) (4, 4, true) * v6 v7 v12 v13 v14 v15 * +---------+ +---------+---------+---------+ * | \ | | \ v5| \ v6| \ | * | \ T5 | | \ T13 | \ T15 | \ T17 | * | \ | | \ | \ | \ | * | T4 \ | | T12 \ | T14 \ | T16 \ | * v4+---------+ v5 v8+---------+---------+---------+v11 * | / | | / |v9 / |v10 / | * | T2 / | | T6 / | T8 / | T10 / | * | / | | / | / | / | * | / T3 | | / T7 | / T9 | / T11 | * v2+---------+ v3 v4+---------+---------+---------+v7 * | \ | | \ v5| \ v6| \ | * | \ T1 | | \ T1 | \ T3 | \ T5 | * | \ | | \ | \ | \ | * | T0 \ | | T0 \ | T2 \ | T4 \ | * +---------+ +---------+---------+---------+ * v0 v1 v0 v1 v2 v3 */ this.alterned = alt; createMxNShell(m, n); rotateMxNShellAroundY(m, n, 90); rotateMxNShellAroundY(m, n, 180); Triangle [] tt = rotateMxNShellAroundY(m, n, 270); // Invert a set of triangles so that triangles in fans have // different orientations. invertTriangles(tt); mesh.buildAdjacency(); assertTrue("Mesh is not valid", mesh.isValid()); } final void buildMesh3() { createMxNShell(3, 2); mesh.buildAdjacency(); assertTrue("Mesh is not valid", mesh.isValid()); } final void buildMesh3NM() { createMxNShell(3, 2); Vertex vTemp = T[0].getV0(); T[0].setV(0, T[0].getV1()); T[0].setV(1, vTemp); mesh.buildAdjacency(); assertTrue("Mesh is not valid", mesh.isValid()); } final AbstractHalfEdge find(Vertex v1, Vertex v2) { if (v1.isManifold()) { AbstractHalfEdge ret = findSameFan(v1, v2, (Triangle) v1.getLink()); if (ret == null) throw new RuntimeException(); return ret; } Triangle [] tArray = (Triangle []) v1.getLink(); for (Triangle start: tArray) { AbstractHalfEdge f = findSameFan(v1, v2, start); if (f != null) { if (f.hasAttributes(AbstractHalfEdge.OUTER)) return f.sym(); return f; } } throw new RuntimeException(); } private AbstractHalfEdge findSameFan(Vertex v1, Vertex v2, Triangle start) { AbstractHalfEdge ret = start.getAbstractHalfEdge(); if (ret == null) throw new RuntimeException(); if (ret.destination() == v1) ret = ret.next(); else if (ret.apex() == v1) ret = ret.prev(); assertTrue(ret.origin() == v1); Vertex d = ret.destination(); if (d == v2) return ret; do { ret = ret.nextOriginLoop(); if (ret.destination() == v2) return ret; } while (ret.destination() != d); return null; } void nextOriginLoop() { // Loop around v0 AbstractHalfEdge e = find(v[0], v[1]); assertTrue(e.origin() == v[0]); assertTrue(e.getTri() == T[0]); e = e.nextOriginLoop(); assertTrue(e.origin() == v[0]); assertTrue(e.destination() == v[3]); assertTrue(e.apex() == mesh.outerVertex); e = e.nextOriginLoop(); assertTrue(e.origin() == v[0]); assertTrue(e.destination() == mesh.outerVertex); assertTrue(e.apex() == v[1]); e = e.nextOriginLoop(); assertTrue(e.origin() == v[0]); assertTrue(e.getTri() == T[0]); // Loop around v1 e = find(v[1], v[2]); for (int i = 3; i >= 0; i--) { assertTrue(e.origin() == v[1]); assertTrue(e.getTri() == T[i]); e = e.nextOriginLoop(); } assertTrue(e.origin() == v[1]); assertTrue(e.destination() == v[0]); assertTrue(e.apex() == mesh.outerVertex); e = e.nextOriginLoop(); assertTrue(e.origin() == v[1]); assertTrue(e.destination() == mesh.outerVertex); assertTrue(e.apex() == v[2]); e = e.nextOriginLoop(); assertTrue(e.origin() == v[1]); assertTrue(e.getTri() == T[3]); // Loop around v4 e = e.nextOrigin().prev(); assertTrue(e.origin() == v[4]); assertTrue(e.getTri() == T[2]); for (int i = 7; i >= 4; i--) { e = e.nextOriginLoop(); assertTrue(e.origin() == v[4]); assertTrue(e.getTri() == T[i]); } e = e.nextOriginLoop(); assertTrue(e.origin() == v[4]); assertTrue(e.getTri() == T[1]); e = e.nextOriginLoop(); assertTrue(e.origin() == v[4]); assertTrue(e.getTri() == T[2]); } final void canCollapse(Vertex o, Vertex d, Vertex n, boolean expected) { AbstractHalfEdge e = find(o, d); assertTrue(expected == e.canCollapse(mesh, n)); } final AbstractHalfEdge collapse(Vertex o, Vertex d, Vertex n) { AbstractHalfEdge e = find(o, d); Vertex a = e.apex(); e = e.collapse(mesh, n); assertTrue("Mesh is not valid", mesh.isValid()); assertTrue("Error in origin", n == e.origin()); assertTrue("Error in destination", a == e.apex()); return e; } final void swap(Vertex o, Vertex d) { AbstractHalfEdge e = find(o, d); Vertex a = e.apex(); e = e.swap(mesh); assertTrue("Mesh is not valid", mesh.isValid()); assertTrue("Error in origin", o == e.origin()); assertTrue("Error in apex", a == e.apex()); } protected final AbstractHalfEdge split(Vertex o, Vertex d, Vertex n) { AbstractHalfEdge e = find(o, d); boolean reversed = (e.origin() != o); e = e.split(mesh, n); assertTrue("Mesh is not valid", mesh.isValid()); if (reversed) { assertTrue("Error in origin", d == e.origin()); assertTrue("Error in destination", n == e.destination()); } else { assertTrue("Error in origin", o == e.origin()); assertTrue("Error in destination", n == e.destination()); } return e; } final void countVertexLinks(Vertex o, int count) { int n = 0; if (o.isManifold()) n = 1; else n = ((Triangle []) o.getLink()).length; assertTrue("Found "+n+" links instead of "+count, n == count); } final void countEdgeLinks(Vertex o, Vertex d, int count) { AbstractHalfEdge e = find(o, d); int n = 0; if (!e.hasAttributes(AbstractHalfEdge.NONMANIFOLD)) { n++; if (!e.hasAttributes(AbstractHalfEdge.BOUNDARY)) n++; } else { if (e.hasAttributes(AbstractHalfEdge.OUTER)) e = e.sym(); for (Iterator<AbstractHalfEdge> it = e.fanIterator(); it.hasNext(); it.next()) n++; } assertTrue("Found "+n+" incident triangles instead of "+count, n == count); } final void countFanIterator(Vertex o, Vertex d, int count) { AbstractHalfEdge e = find(o, d); int n = 0; for (Iterator<AbstractHalfEdge> it = e.fanIterator(); it.hasNext(); it.next()) n++; assertTrue("Found "+n+" fans instead of "+count, n == count); } }