/* * 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 2008, by EADS France */ package org.jcae.vtk; import gnu.trove.list.array.TFloatArrayList; import gnu.trove.list.array.TIntArrayList; import gnu.trove.set.hash.TIntHashSet; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.iterator.TIntObjectIterator; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.channels.FileChannel; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.jcae.mesh.amibe.ds.AbstractHalfEdge; import org.jcae.mesh.amibe.traits.MeshTraitsBuilder; import org.jcae.mesh.amibe.traits.TriangleTraitsBuilder; import org.jcae.mesh.oemm.MeshReader; import org.jcae.mesh.oemm.OEMM; 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.oemm.FakeNonReadVertex; /** * This class serves to two things : * <p>In MeshVisuBuilder it reads a Mesh from the standard OEMM and build a Mesh Visu. * This can be down using buildPreparationMeshVisu. * </p> * <p> * In ViewableOEMM it reads directly in specialised *V files that contains directly the beams * representing the mesh. This can be down using buildMeshVisu. Be careful, the edges in the boundaries * of the octants are not loaded if the two octants that contains the boundarie are not loaded ! * </p> * @author Julian Ibarz */ public class MeshVisuReader extends MeshReader { private static Logger LOGGER = Logger.getLogger(MeshVisuReader.class.getName()); /** * This is the structure of the mesh of a leaf */ class MeshVisu { public int[] edges; public int[] freeEdges; public float[] nodes; // The nodes of the other leaves duplicated } // This contains the coordinates for the quads of the octree private float[] nodesQuads; private TIntObjectHashMap<MeshVisu> mapLeafToMeshVisu = new TIntObjectHashMap<MeshVisu>(); public MeshVisuReader(OEMM o) { super(o); } public float[] getNodesQuad() { if (nodesQuads == null) nodesQuads = ArrayUtils.doubleToFloat(oemm.getCoords(true)); return nodesQuads; } public int[] getLeavesLoaded() { return mapLeafToMeshVisu.keys(); } MeshVisu[] getMeshes() { MeshVisu[] toReturn = new MeshVisu[mapLeafToMeshVisu.size()]; mapLeafToMeshVisu.values(toReturn); return toReturn; } public void buildMeshVisu(int[] leaves) { if (LOGGER.isLoggable(Level.CONFIG)) LOGGER.log(Level.CONFIG, "Loading nodes: "+leaves); TIntArrayList sortedLeaves = new TIntArrayList(leaves); sortedLeaves.sort(); // Unload the mesh not used for (int leaf : mapLeafToMeshVisu.keys()) { if (sortedLeaves.binarySearch(leaf) < 0) mapLeafToMeshVisu.remove(leaf); } for (int i = 0, n = sortedLeaves.size(); i < n; i++) { int leaf = sortedLeaves.get(i); // If already loaded skip if (mapLeafToMeshVisu.containsKey(leaf)) continue; MeshVisu mesh = new MeshVisu(); readVerticesForVisu(mesh, oemm.leaves[leaf]); readEdges(mesh, oemm.leaves[leaf]); mapLeafToMeshVisu.put(leaf, mesh); } } private void readVerticesForVisu(MeshVisu mesh, OEMM.Node current) { try { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Reading " + current.vn + " vertices from " + getVerticesFile(oemm, current)); double[] xyz = new double[3]; mesh.nodes = new float[current.vn * 3]; FileChannel fc = new FileInputStream(getVerticesFile(oemm, current)).getChannel(); buffer.clear(); DoubleBuffer bbD = buffer.asDoubleBuffer(); int remaining = current.vn; int offset = 0; for (int nblock = (remaining * VERTEX_SIZE) / BUFFER_SIZE; nblock >= 0; --nblock) { buffer.rewind(); fc.read(buffer); bbD.rewind(); int nf = BUFFER_SIZE / VERTEX_SIZE; if (remaining < nf) nf = remaining; remaining -= nf; for (int nr = 0; nr < nf; nr++) { bbD.get(xyz); mesh.nodes[offset++] = (float) xyz[0]; mesh.nodes[offset++] = (float) xyz[1]; mesh.nodes[offset++] = (float) xyz[2]; } } fc.close(); } catch (IOException ex) { LOGGER.severe("I/O error when reading file " + getVerticesFile(oemm, current)); ex.printStackTrace(); throw new RuntimeException(ex); } } /** * Reads triangle file, create Triangle instances and store them into mesh. */ private void readEdges(MeshVisu mesh, OEMM.Node current) { try { FileChannel fc = new FileInputStream(MeshVisuBuilder.getEdgesFile(oemm, current)).getChannel(); String [] label = new String[] { "Reading edges", "Reading free edges" }; for (int i = 0; i < 2; ++i) { LOGGER.fine(label[i]); // Read the number of edges components ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.SIZE / 8); IntBuffer bufferInteger = byteBuffer.asIntBuffer(); byteBuffer.rewind(); fc.read(byteBuffer); bufferInteger.rewind(); int nbrOfEdgesComponents = bufferInteger.get(0); if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Reading " + (nbrOfEdgesComponents / 2) + " edges from " + MeshVisuBuilder.getEdgesFile(oemm, current)); // Read the edges byteBuffer = ByteBuffer.allocate((Integer.SIZE / 8) * nbrOfEdgesComponents); bufferInteger = byteBuffer.asIntBuffer(); byteBuffer.rewind(); fc.read(byteBuffer); bufferInteger.rewind(); int[] temp = new int[nbrOfEdgesComponents]; bufferInteger.get(temp); if (i == 0) mesh.edges = temp; else mesh.freeEdges = temp; } // Read the number of fake vertices ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.SIZE / 8); IntBuffer bufferInteger = byteBuffer.asIntBuffer(); byteBuffer.rewind(); fc.read(byteBuffer); bufferInteger.rewind(); int nbrOfFakeVerticesComponent = bufferInteger.get(0); // Read fake vertices if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Reading " + (nbrOfFakeVerticesComponent / 3) + " fake vertices from " + MeshVisuBuilder.getEdgesFile(oemm, current)); byteBuffer = ByteBuffer.allocate((Float.SIZE / 8) * nbrOfFakeVerticesComponent); FloatBuffer bufferFloat = byteBuffer.asFloatBuffer(); byteBuffer.rewind(); fc.read(byteBuffer); bufferFloat.rewind(); float[] fakeVertices = new float[nbrOfFakeVerticesComponent]; bufferFloat.get(fakeVertices); // Merging vertices and fake vertices float[] vertices = mesh.nodes; if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Merging " + fakeVertices.length + " into " + vertices.length + " vertices."); mesh.nodes = new float[vertices.length + fakeVertices.length]; System.arraycopy(vertices, 0, mesh.nodes, 0, vertices.length); System.arraycopy(fakeVertices, 0, mesh.nodes, vertices.length, fakeVertices.length); } catch (IOException ex) { LOGGER.severe("I/O error when reading indexed file " + getTrianglesFile(oemm, current)); ex.printStackTrace(); throw new RuntimeException(ex); } } MeshVisu buildMeshVisu(int leaf) { MeshTraitsBuilder mtb = new MeshTraitsBuilder(); mtb.addNodeList(); mtb.addTriangleList(); // Mesh need adjancy informations to compute the edges TriangleTraitsBuilder ttb = mtb.getTriangleTraitsBuilder(); ttb.addHalfEdge(); mapNodeToNonReadVertexList = new TIntObjectHashMap<List<FakeNonReadVertex>>(); Mesh mesh = buildMesh(mtb, new TIntHashSet(new int[]{ leaf })); return constructEdges(mesh, leaf); } private int getNbrOfFakeVertices() { int nbrOfFakeVertices = 0; for (TIntObjectIterator<List<FakeNonReadVertex>> it = mapNodeToNonReadVertexList.iterator(); it.hasNext(); ) { it.advance(); nbrOfFakeVertices += it.value().size(); } return nbrOfFakeVertices; } private MeshVisu constructEdges(Mesh mesh, int leave) { MeshVisu toReturn = new MeshVisu(); TIntArrayList edges = new TIntArrayList(mesh.getTriangles().size() * 3); // This is empiric allocation, in general freeEdges dont are very numerous TIntArrayList freeEdges = new TIntArrayList(mesh.getTriangles().size()); TFloatArrayList nodes = new TFloatArrayList(); // offset is the number of non fake vertices // (because we will append later real and fake vertices) int offset = mesh.getNodes().size() - getNbrOfFakeVertices(); LOGGER.finer("offset of fake vertices " + offset); // Compute offset for (Triangle tri : mesh.getTriangles()) { if (!tri.isReadable()) continue; AbstractHalfEdge edge = tri.getAbstractHalfEdge(); for (int i = 0; i < 3; ++i) { edge = edge.next(); // Conditions if (edge.hasAttributes(AbstractHalfEdge.MARKED)) continue; if (!edge.origin().isReadable() || !edge.destination().isReadable()) continue; // Mark the edge edge.setAttributes(AbstractHalfEdge.MARKED); if (edge.hasSymmetricEdge()) edge.sym().setAttributes(AbstractHalfEdge.MARKED); TIntArrayList fakeEdges = null; // Save the edge if (edge.hasAttributes(AbstractHalfEdge.BOUNDARY)) fakeEdges = freeEdges; else fakeEdges = edges; Vertex vertex = edge.origin(); int ID = -1; for (int j = 0; j < 2; ++j) { if (j == 0) vertex = edge.origin(); else vertex = edge.destination(); if (vertex instanceof FakeNonReadVertex) { ID = offset; nodes.add((float) vertex.getX()); nodes.add((float) vertex.getY()); nodes.add((float) vertex.getZ()); ++offset; } else ID = vertex.getLabel() - oemm.leaves[leave].minIndex; fakeEdges.add(ID); } } } toReturn.edges = edges.toArray(); toReturn.freeEdges = freeEdges.toArray(); toReturn.nodes = nodes.toArray(); return toReturn; } }