/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD modeler, Finite element mesher, Plugin architecture. Copyright (C) 2003,2004,2005, by EADS CRC Copyright (C) 2007,2008,2009, 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.xmldata; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.DoubleBuffer; import java.nio.IntBuffer; import java.nio.channels.FileChannel; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.jcae.mesh.cad.CADShapeFactory; import org.jcae.mesh.cad.CADShapeEnum; import org.jcae.mesh.cad.CADExplorer; import org.jcae.mesh.cad.CADGeomSurface; import org.jcae.mesh.cad.CADShape; import org.jcae.mesh.cad.CADFace; import org.w3c.dom.Document; import org.w3c.dom.Node; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.list.array.TIntArrayList; import java.util.logging.Logger; public class MeshToSoupConvert implements FilterInterface, JCAEXMLData { private static final Logger logger=Logger.getLogger(MeshToSoupConvert.class.getName()); private int nrTriangles = 0; private int nrIntNodes = 0; private int nrNodes = 0; private int nrRefs = 0; private final String xmlDir; private final String soupFile; private File rawFile; private TIntIntHashMap xrefs; private final TIntObjectHashMap<CADFace> mapFaces = new TIntObjectHashMap<CADFace>(); private double [] coordRefs; // Must be a multiple of 8*2, 4*3 and 8*10 private static final int bufferSize = 15 << 12; private final ByteBuffer bb = ByteBuffer.allocate(bufferSize); private final IntBuffer bbI = bb.asIntBuffer(); private final DoubleBuffer bbD = bb.asDoubleBuffer(); /** * @param args mesh directory, brep path */ public static void main(String[] args) { meshToSoup(args[0], CADShapeFactory.getFactory().newShape(args[1])); } /** * Compute raw 3D mesh * @param xmlDir mesh directory * @param shape */ public static void meshToSoup(String xmlDir, CADShape shape) { CADExplorer expF = CADShapeFactory.getFactory().newExplorer(); int numFace=Integer.getInteger("org.jcae.mesh.Mesher.meshFace", 0).intValue(); int minFace=Integer.getInteger("org.jcae.mesh.Mesher.minFace", 0).intValue(); int maxFace=Integer.getInteger("org.jcae.mesh.Mesher.maxFace", 0).intValue(); int iFace = 0; MeshToSoupConvert m2dTo3D = new MeshToSoupConvert("soup", xmlDir, shape); logger.info("Read informations on boundary nodes"); TIntArrayList listOfFaces = new TIntArrayList(); for (expF.init(shape, CADShapeEnum.FACE); expF.more(); expF.next()) { iFace++; if (numFace != 0 && iFace != numFace) continue; if (minFace != 0 && iFace < minFace) continue; if (maxFace != 0 && iFace > maxFace) continue; listOfFaces.add(iFace); } m2dTo3D.collectBoundaryNodes(listOfFaces.toArray()); m2dTo3D.beforeProcessingAllShapes(false); iFace = 0; for (expF.init(shape, CADShapeEnum.FACE); expF.more(); expF.next()) { CADFace F = (CADFace) expF.current(); iFace++; if (numFace != 0 && iFace != numFace) continue; if (minFace != 0 && iFace < minFace) continue; if (maxFace != 0 && iFace > maxFace) continue; logger.info("Importing face "+iFace); m2dTo3D.processOneShape(iFace, ""+iFace, iFace); } m2dTo3D.afterProcessingAllShapes(); } private MeshToSoupConvert (String file, String dir, CADShape shape) { xmlDir = dir; soupFile = file; CADExplorer expF = CADShapeFactory.getFactory().newExplorer(); int iFace = 0; for (expF.init(shape, CADShapeEnum.FACE); expF.more(); expF.next()) { iFace++; mapFaces.put(iFace, (CADFace) expF.current()); } } public final void collectBoundaryNodes(int[] faces) { for (int iFace : faces) { Document document; File xmlFile2d = null; try { xmlFile2d = new File(xmlDir, JCAEXMLData.xml2dFilename+iFace); document = XMLHelper.parseXML(xmlFile2d); } catch(FileNotFoundException ex) { continue; } catch(Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } XPath xpath = XPathFactory.newInstance().newXPath(); try { String formatVersion = xpath.evaluate("/jcae/@version", document); if (formatVersion != null && formatVersion.length() > 0) throw new RuntimeException("File "+xmlFile2d+" has been written by a newer version of jCAE and cannot be re-read"); Node submeshElement = (Node) xpath.evaluate("/jcae/mesh/submesh", document, XPathConstants.NODE); Node submeshNodes = (Node) xpath.evaluate("nodes", submeshElement, XPathConstants.NODE); int numberOfReferences = Integer.parseInt( xpath.evaluate("references/number/text()", submeshNodes)); nrRefs += numberOfReferences; int numberOfNodes = Integer.parseInt( xpath.evaluate("number/text()", submeshNodes)); nrIntNodes += numberOfNodes - numberOfReferences; } catch(Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } logger.fine("Total: "+nrRefs+" references"); } } public final void beforeProcessingAllShapes(boolean writeNormal) { coordRefs = new double[3*nrRefs]; xrefs = new TIntIntHashMap(nrRefs); rawFile = new File(xmlDir, soupFile); rawFile.delete(); } public final void afterProcessingAllShapes() { logger.info("Total number of nodes: "+(nrNodes+nrIntNodes)); logger.info("Total number of triangles: "+nrTriangles); } public final void processOneShape(int groupId, String groupName, int iFace) { Document documentIn; try { documentIn = XMLHelper.parseXML(new File(xmlDir, JCAEXMLData.xml2dFilename+iFace)); } catch(FileNotFoundException ex) { return; } catch(Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } CADFace F = mapFaces.get(iFace); XPath xpath = XPathFactory.newInstance().newXPath(); CADGeomSurface surface = F.getGeomSurface(); surface.dinit(0); try { Node submeshElement = (Node) xpath.evaluate("/jcae/mesh/submesh", documentIn, XPathConstants.NODE); Node submeshNodes = (Node) xpath.evaluate("nodes", submeshElement, XPathConstants.NODE); Node submeshFaces = (Node) xpath.evaluate("triangles", submeshElement, XPathConstants.NODE); String refFile = xpath.evaluate("references/file/@location", submeshNodes); FileChannel fcR = new FileInputStream(xmlDir+File.separator+refFile).getChannel(); int numberOfReferences = Integer.parseInt( xpath.evaluate("references/number/text()", submeshNodes)); int [] refs = new int[numberOfReferences]; logger.fine("Reading "+numberOfReferences+" references"); bb.clear(); bbI.clear(); int remaining = numberOfReferences; int index = 0; int nf = bufferSize / 12; for (int nblock = (remaining * 12) / bufferSize; nblock >= 0; --nblock) { if (remaining <= 0) break; else if (remaining < nf) nf = remaining; remaining -= nf; bb.rewind(); fcR.read(bb); bbI.rewind(); for(int nr = 0; nr < nf; nr ++) { refs[index] = bbI.get(); index++; } } assert index == numberOfReferences; int numberOfNodes = Integer.parseInt(xpath.evaluate( "number/text()", submeshNodes)); logger.fine("Reading " + numberOfNodes + " nodes"); String nodesFile = xpath.evaluate("file/@location", submeshNodes); FileChannel fcN = new FileInputStream(xmlDir+File.separator+nodesFile).getChannel(); double [] coord = new double[3*numberOfNodes]; bb.clear(); bbD.clear(); index = 0; remaining = numberOfNodes; nf = bufferSize / 16; for (int nblock = (remaining * 16) / bufferSize; nblock >= 0; --nblock) { if (remaining <= 0) break; else if (remaining < nf) nf = remaining; remaining -= nf; bb.rewind(); fcN.read(bb); bbD.rewind(); for(int nr = 0; nr < nf; nr ++) { double u = bbD.get(); double v = bbD.get(); if (index < numberOfNodes - numberOfReferences) { double [] p3 = surface.value(u, v); System.arraycopy(p3, 0, coord, 3 * index, 3); } else { int ref = refs[index - numberOfNodes + numberOfReferences]; if (!xrefs.contains(ref)) { double [] p3 = surface.value(u, v); xrefs.put(ref, nrNodes); System.arraycopy(p3, 0, coordRefs, 3 * nrNodes, 3); nrNodes++; } ref = xrefs.get(ref); System.arraycopy(coordRefs, 3 * ref, coord, 3 * index, 3); } index++; } } assert index == numberOfNodes; fcN.close(); int numberOfFaces = Integer.parseInt(xpath.evaluate( "number/text()", submeshFaces)); logger.fine("Reading " + numberOfFaces + " faces"); String trianglesFile = xpath.evaluate("file/@location", submeshFaces); FileChannel fcT = new FileInputStream(xmlDir+File.separator+trianglesFile).getChannel(); bb.clear(); bbI.clear(); FileChannel fcO = new FileOutputStream(rawFile, true).getChannel(); ByteBuffer bbo = ByteBuffer.allocate(bufferSize * 80 / 12); DoubleBuffer bboD = bbo.asDoubleBuffer(); int ind [] = new int[3]; int indRef [] = new int[3]; double [] c = new double[9]; remaining = numberOfFaces; nf = bufferSize / 12; for (int nblock = (remaining * 12) / bufferSize; nblock >= 0; --nblock) { if (remaining <= 0) break; else if (remaining < nf) nf = remaining; remaining -= nf; bb.clear(); fcT.read(bb); bbI.rewind(); bbo.clear(); bboD.rewind(); for(int nr = 0; nr < nf; nr ++) { bbI.get(ind); if (ind[0] < 0 || ind[1] < 0 || ind[2] < 0) { // Outer triangle continue; } for (int j = 0; j < 3; j++) { if (ind[j] < numberOfNodes - numberOfReferences) indRef[j] = - j - 1; else indRef[j] = xrefs.get(refs[ind[j] - numberOfNodes + numberOfReferences]); } if (indRef[0] == indRef[1] || indRef[1] == indRef[2] || indRef[2] == indRef[0]) { // Triangle bound from a degenerated edge continue; } nrTriangles++; for (int j = 0; j < 3; j++) System.arraycopy(coord, 3 * ind[j], c, 3 * j, 3); if (F.isOrientationForward()) { for (int j = 0; j < 3; j++) for (int k = 0; k < 3; k++) bboD.put(c[3*j+k]); } else { for (int j = 2; j >= 0; j--) for (int k = 0; k < 3; k++) bboD.put(c[3*j+k]); } // Align on 64bit bbo.position(8*bboD.position()); bbo.putInt(iFace); bbo.putInt(0); bboD.position(1+bboD.position()); } bbo.flip(); fcO.write(bbo); } fcT.close(); fcO.close(); logger.fine("End reading"); } catch(Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } } }