/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD modeler, Finite element mesher, Plugin architecture. Copyright (C) 2008, 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 java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.jcae.mesh.MeshOEMMIndex; import org.jcae.mesh.amibe.ds.Mesh; import org.jcae.mesh.amibe.ds.Vertex; import org.jcae.mesh.amibe.metrics.Matrix3D; import org.jcae.mesh.oemm.MeshReader; import org.jcae.mesh.oemm.OEMM; import org.jcae.mesh.oemm.Storage; import org.junit.Ignore; /** * Create a regular mesh of a sphere. We first generate an icosahedron, * this is the Platonic solid which is the best approximation of a sphere * by equilateral triangles. Then each triangle is refined and new vertices * are projected onto a sphere. Triangles are no more equilateral, but * deviation is very low. Valence of the 12 initial vertices is 5, and all * other vertices have a valence of 6. * * @author Denis Barbier */ @Ignore("Utility class") public class SphereBuilder { private final static double TAU = 0.5 * (1.0 + Math.sqrt(5.0)); private final static double [] ICO = new double[] { 0.0, 1.0, TAU, 0.0, -1.0, TAU, 0.0, 1.0, -TAU, 0.0, -1.0, -TAU, 1.0, TAU, 0.0, -1.0, TAU, 0.0, 1.0, -TAU, 0.0, -1.0, -TAU, 0.0, TAU, 0.0, 1.0, -TAU, 0.0, 1.0, TAU, 0.0, -1.0, -TAU, 0.0, -1.0 }; private final static double SCALE = 1.0 / Math.sqrt(1.0 + TAU*TAU); private final static int [] FACES = new int[] { 0, 1, 8, 8, 1, 6, 6, 1, 7, 7, 1, 9, 9, 1, 0, 2, 5, 4, 4, 5, 0, 0, 5, 9, 9, 5, 11, 11, 5, 2, 2, 3, 11, 11, 3, 7, 7, 3, 6, 6, 3, 10, 10, 3, 2, 9, 11, 7, 8, 4, 0, 6, 10, 8, 4, 8, 10, 4, 10, 2 }; private final Set<UnindexedTriangle> triangles = new HashSet<UnindexedTriangle>(); @Ignore("Inner class") private static class UnindexedTriangle { final double [] coord = new double[9]; int group; UnindexedTriangle() { } public UnindexedTriangle(double [] v0, double [] v1, double [] v2, int group) { this.group = group; System.arraycopy(v0, 0, coord, 0, 3); System.arraycopy(v1, 0, coord, 3, 3); System.arraycopy(v2, 0, coord, 6, 3); } } private void createIcosahedron() { for (int i = 0; i < 20; i++) { UnindexedTriangle t = new UnindexedTriangle(); t.group = i + 1; for (int j = 0; j < 3; j++) { int index = FACES[3*i+j]; t.coord[3*j] = SCALE * ICO[3*index]; t.coord[3*j+1] = SCALE * ICO[3*index+1]; t.coord[3*j+2] = SCALE * ICO[3*index+2]; } triangles.add(t); } } private void refine() { double [] x = new double[3]; double [][] vertex = new double[3][3]; double [][] middle = new double[3][3]; for (UnindexedTriangle t : new ArrayList<UnindexedTriangle>(triangles)) { int last = 2; for (int i = 0; i < 3; i++) { vertex[i][0] = t.coord[3*i]; vertex[i][1] = t.coord[3*i+1]; vertex[i][2] = t.coord[3*i+2]; for (int j = 0; j < 3; j++) x[j] = 0.5 * (t.coord[3*i+j] + t.coord[3*last+j]); double invNorm = 1.0 / Matrix3D.norm(x); middle[i][0] = x[0] * invNorm; middle[i][1] = x[1] * invNorm; middle[i][2] = x[2] * invNorm; last = i; } triangles.add(new UnindexedTriangle(vertex[0], middle[1], middle[0], t.group)); triangles.add(new UnindexedTriangle(vertex[1], middle[2], middle[1], t.group)); triangles.add(new UnindexedTriangle(vertex[2], middle[0], middle[2], t.group)); triangles.add(new UnindexedTriangle(middle[0], middle[1], middle[2], t.group)); triangles.remove(t); } } private boolean writeSoup(String dirname) throws IOException { File dir = new File(dirname); if (!dir.isDirectory()) dir.mkdirs(); DataOutputStream out = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(dirname+File.separator+"soup"))); for (UnindexedTriangle t : triangles) { for (int i = 0; i < 9; i++) out.writeDouble(t.coord[i]); out.writeInt(t.group); out.writeInt(0); } out.close(); return true; } private static Mesh createSphereMesh(int level) { SphereBuilder sphere = new SphereBuilder(); // Create icosahedron sphere.createIcosahedron(); // Refine icosahedron for (int i = level; i > 0; i--) sphere.refine(); // Write triangle soup and oemm into the same temporary dir String tmpdir; try { File tempFile = File.createTempFile("oemm", ".dir"); tmpdir = tempFile.getAbsolutePath(); tempFile.delete(); File tempDir = new File(tmpdir); tempDir.mkdir(); sphere.writeSoup(tmpdir); } catch (IOException ex) { Logger.getLogger(SphereBuilder.class.getName()).log(Level.SEVERE, null, ex); return null; } // Build oemm String [] mainArgs = new String[4]; mainArgs[0] = tmpdir; mainArgs[1] = tmpdir; mainArgs[2] = "4"; mainArgs[3] = "50000"; MeshOEMMIndex.main(mainArgs); // Read oemm into a Mesh OEMM oemm = Storage.readOEMMStructure(mainArgs[1]); MeshReader mr = new MeshReader(oemm); Mesh toReturn = mr.buildWholeMesh(); // Clean up return toReturn; } // Move vertices public static Mesh createShuffledSphereMesh(int level) { Mesh mesh = createSphereMesh(level); for (Vertex v : mesh.getNodes()) { if (v.getRef() > 0) continue; double c = Math.cos(Math.PI * v.getZ()); if (v.getZ() >= 0.0) v.moveTo(v.getX(), v.getY(), (1.0 - c*c*c*c*c) / 2.0); else v.moveTo(v.getX(), v.getY(), - (1.0 - c*c*c*c*c) / 2.0); double r = Math.sqrt(v.getX()*v.getX() + v.getY()*v.getY() + v.getZ()*v.getZ()); v.scale(1/r); } return mesh; } }