/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD modeler, Finite element mesher, Plugin architecture. Copyright (C) 2008,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.algos3d; 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.projection.MeshLiaison; import org.jcae.mesh.amibe.traits.MeshTraitsBuilder; import org.jcae.mesh.amibe.validation.MinAngleFace; import org.jcae.mesh.amibe.validation.QualityFloat; import org.jcae.mesh.xmldata.MeshReader; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import static org.junit.Assert.*; import org.junit.Test; import org.junit.Ignore; public class SmoothNodes3DBgTest { private Mesh mesh; private Vertex [] v; private Triangle [] T; // m Vertex on rows, n Vertex on columns private void createMxNCylinder(int m, int n) { /* v3 v4 v5 * +---------+---------+ * | \ | \ | * | \ T1 | \ T3 | * | \ | \ | * | T0 \ | T2 \ | * +---------+---------+ * v0 v1 v2 */ v = new Vertex[m*n]; for (int j = 0; j < n; j++) { double dtheta = 2.0 * Math.PI / m; double z = 2.0 * Math.PI * j / (n - 1.0); for (int i = 0; i < m; i++) { v[m*j+i] = mesh.createVertex( Math.cos(i*dtheta), Math.sin(i*dtheta), z); } } for (int i = 0; i < v.length; i++) v[i].setLabel(i); T = createMxNTriangles(m, n, v); } // m Vertex on rows, n Vertex on columns private void createMxNShell(int m, int n) { /* v3 v4 v5 * +---------+---------+ * | \ | \ | * | \ T1 | \ T3 | * | \ | \ | * | T0 \ | T2 \ | * +---------+---------+ * v0 v1 v2 */ 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); for (int i = 0; i < v.length; i++) v[i].setLabel(i); T = createMxNTriangles(m, n, v); } private Triangle [] createMxNTriangles(int m, int n, Vertex [] vv) { Triangle [] tt = new Triangle[2*(m-1)*(n-1)]; for (int j = 0; j < n-1; j++) { 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]); } // 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]); for (Triangle t: tt) mesh.add(t); return tt; } private void testShell(int m, int n) { final Map<String, String> options = new HashMap<String, String>(); options.put("iterations", "10"); mesh = new Mesh(); createMxNShell(m, n); mesh.buildAdjacency(); assertTrue("Mesh is not valid", mesh.isValid()); Mesh smoothedMesh = new SmoothNodes3DBg(MeshLiaison.create(mesh), options).compute().getOutputMesh(); assertTrue("Mesh is not valid", smoothedMesh.isValid()); } @Test public void testShellLarge() { testShell(3, 3); } @Test public void testSphere() { mesh = SphereBuilder.createShuffledSphereMesh(3); assertTrue("Mesh is not valid", mesh.isValid()); final Map<String, String> options = new HashMap<String, String>(); options.put("iterations", "20"); options.put("check", "false"); options.put("refresh", "true"); options.put("relaxation", "1.0"); Mesh smoothedMesh = new SmoothNodes3DBg(MeshLiaison.create(mesh), options).compute().getOutputMesh(); assertTrue("Mesh is not valid", smoothedMesh.isValid()); MinAngleFace qproc = new MinAngleFace(); QualityFloat data = new QualityFloat(1000); data.setQualityProcedure(qproc); data.setTarget((float) Math.PI/3.0f); for (Triangle f: smoothedMesh.getTriangles()) data.compute(f); data.finish(); double qmin = data.getValueByPercent(0.0); assertTrue("Min. angle too small: "+(qmin*60.0), qmin > 0.85); } @Test public void test4Neighbors() { /* v2 v3 * +---------+ * | \ | * | \ T1 | * | \ | * | T0 \ | * +---------+ * v0 v1 */ mesh = new Mesh(); createMxNShell(2, 2); mesh.buildAdjacency(); // Add a middle point Vertex mid = mesh.createVertex(0.3, 0.1, 0.0); mesh.vertexSplit(T[0].getAbstractHalfEdge(), mid); assertTrue("Mesh is not valid", mesh.isValid()); final Map<String, String> options = new HashMap<String, String>(); options.put("iterations", "1"); options.put("relaxation", "1.0"); Mesh smoothedMesh = new SmoothNodes3DBg(MeshLiaison.create(mesh), options).compute().getOutputMesh(); assertTrue("Mesh is not valid", smoothedMesh.isValid()); MinAngleFace qproc = new MinAngleFace(); QualityFloat data = new QualityFloat(10); data.setQualityProcedure(qproc); data.setTarget((float) Math.PI/4.0f); for (Triangle f: smoothedMesh.getTriangles()) { if (f.isWritable()) data.compute(f); } data.finish(); double qmin = data.getValueByPercent(0.0); assertTrue("Min. angle too small: "+(qmin*45.0), qmin > 0.98); } @Ignore("Inner class") private static class CheckSmoothNodes3DBg extends SmoothNodes3DBg { public CheckSmoothNodes3DBg(MeshLiaison liaison, Map<String, String> options) { super(liaison, options); } public final boolean hasMoved() { return processed > 0; } } @Test public void testSingularQuadric() { /* * This case was found in practice */ mesh = new Mesh(); v = new Vertex[5]; v[0] = mesh.createVertex(-0.62187508381251378, -0.34751501841852017, 0.085315828205300027); v[1] = mesh.createVertex(-0.63117823882666904, -0.31365340768558072, 0.053534273359757991); v[2] = mesh.createVertex(-0.61407841179235211, -0.34672837373181228, 0.055636259430818010); v[3] = mesh.createVertex(-0.59344399181142545, -0.38181921866005472, 0.058020011330282950); v[4] = mesh.createVertex(-0.60926256265657037, -0.34866382633430167, 0.034357448682585102); T = new Triangle[4]; 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[3], v[2], v[4]); T[3] = mesh.createTriangle(v[4], v[2], v[1]); v[0].setLink(T[0]); v[1].setLink(T[0]); v[2].setLink(T[0]); v[3].setLink(T[1]); v[4].setLink(T[2]); for (Triangle t : T) mesh.add(t); mesh.buildAdjacency(); assertTrue("Mesh is not valid", mesh.isValid()); final Map<String, String> options = new HashMap<String, String>(); options.put("iterations", "1"); options.put("relaxation", "1.0"); CheckSmoothNodes3DBg algo = new CheckSmoothNodes3DBg(MeshLiaison.create(mesh), options); Mesh smoothedMesh = algo.compute().getOutputMesh(); assertTrue("Mesh is not valid", smoothedMesh.isValid()); assertTrue("Singular quadric detected", algo.hasMoved()); } @Test public void testTorus() { MeshTraitsBuilder mtb = MeshTraitsBuilder.getDefault3D(); mtb.addNodeList(); mesh = new Mesh(mtb); try { MeshReader.readObject3D(mesh, "test"+File.separator+"input"+File.separator+"torus1426"); } catch (IOException ex) { Logger.getLogger(SmoothNodes3DTest.class.getName()).log(Level.SEVERE, null, ex); throw new RuntimeException(); } assertTrue("Mesh is not valid", mesh.isValid()); SmoothNodes3DTest.shuffleTorus(mesh, 0.3, 1.0); final Map<String, String> options = new HashMap<String, String>(); options.put("iterations", "50"); options.put("check", "false"); options.put("refresh", "true"); options.put("relaxation", "0.9"); Mesh smoothedMesh = new SmoothNodes3DBg(MeshLiaison.create(mesh, mtb), options).compute().getOutputMesh(); assertTrue("Mesh is not valid", smoothedMesh.isValid()); MinAngleFace qproc = new MinAngleFace(); QualityFloat data = new QualityFloat(1000); data.setQualityProcedure(qproc); data.setTarget((float) Math.PI/3.0f); for (Triangle f: smoothedMesh.getTriangles()) data.compute(f); data.finish(); double qmin = data.getValueByPercent(0.0); assertTrue("Min. angle too small: "+(qmin*60.0), qmin > 0.25); } }