/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD
modeler, Finite element mesher, Plugin architecture.
Copyright (C) 2005,2006, 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.util.logging.Level;
import org.jcae.mesh.amibe.ds.Mesh;
import org.jcae.mesh.amibe.ds.Triangle;
import org.jcae.mesh.amibe.ds.AbstractHalfEdge;
import org.jcae.mesh.amibe.ds.Vertex;
import org.jcae.mesh.amibe.ds.MGroup3D;
import org.jcae.mesh.amibe.patch.Mesh2D;
import org.jcae.mesh.amibe.patch.Vertex2D;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.parsers.ParserConfigurationException;
import org.jcae.mesh.xmldata.AmibeReader.SubMesh;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import gnu.trove.map.hash.TIntIntHashMap;
import java.util.logging.Logger;
public class MeshReader
{
private static final Logger logger=Logger.getLogger(MeshReader.class.getName());
/**
* Loads an Amibe 2D XML file into an existing Mesh2D instance.
*
* @param mesh data structure updated when reading files
* @param xmlDir directory containing XML files
* @param iFace face number
*/
public static void readObject(Mesh2D mesh, String xmlDir, int iFace)
throws IOException
{
logger.fine("begin reading "+xmlDir+File.separator+JCAEXMLData.xml2dFilename+iFace);
XPath xpath = XPathFactory.newInstance().newXPath();
Document document;
File xmlFile2d = null;
try
{
xmlFile2d = new File(xmlDir, JCAEXMLData.xml2dFilename+iFace);
document = XMLHelper.parseXML(xmlFile2d);
}
catch (ParserConfigurationException ex)
{
throw new IOException(ex.getMessage());
}
catch (SAXException ex)
{
throw new IOException(ex.getMessage());
}
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);
String refFile = xpath.evaluate("references/file/@location", submeshNodes);
if (refFile.charAt(0) != File.separatorChar)
refFile = xmlDir + File.separator + refFile;
PrimitiveFileReaderFactory pfrf = new PrimitiveFileReaderFactory();
IntFileReader ifrR = pfrf.getIntReader(new File(refFile));
String nodesFile = xpath.evaluate("file/@location", submeshNodes);
if (nodesFile.charAt(0) != File.separatorChar) nodesFile = xmlDir
+ File.separator + nodesFile;
DoubleFileReader dfrN = pfrf.getDoubleReader(new File(nodesFile));
Node submeshTriangles = (Node) xpath.evaluate("triangles",
submeshElement, XPathConstants.NODE);
String trianglesFile = xpath.evaluate("file/@location",
submeshTriangles);
if (trianglesFile.charAt(0) != File.separatorChar)
trianglesFile = xmlDir+File.separator+trianglesFile;
IntFileReader ifrT = pfrf.getIntReader(new File(trianglesFile));
int numberOfReferences = Integer.parseInt(
xpath.evaluate("references/number/text()", submeshNodes));
logger.fine("Reading "+numberOfReferences+" references");
int [] refs = new int[numberOfReferences];
ifrR.get(refs);
int numberOfNodes = Integer.parseInt(
xpath.evaluate("number/text()", submeshNodes));
Vertex2D [] nodelist = new Vertex2D[numberOfNodes+1];
nodelist[numberOfNodes] = (Vertex2D) mesh.outerVertex;
int label;
double [] coord = new double[2];
logger.fine("Reading "+numberOfNodes+" nodes");
double [] bbmin = { Double.MAX_VALUE, Double.MAX_VALUE };
double [] bbmax = { Double.MIN_VALUE, Double.MIN_VALUE };
for (int i=0; i < numberOfNodes; i++)
{
dfrN.get(coord);
nodelist[i] = (Vertex2D) mesh.createVertex(coord[0], coord[1]);
if (i < numberOfNodes - numberOfReferences)
label = 0;
else
label = refs[i+numberOfReferences-numberOfNodes];
nodelist[i].setRef(label);
for (int k=0; k < 2; k++)
{
if (coord[k] > bbmax[k])
bbmax[k] = coord[k];
if (coord[k] < bbmin[k])
bbmin[k] = coord[k];
}
}
mesh.ensureCapacity(2*numberOfNodes);
mesh.resetKdTree(bbmin, bbmax);
for (int i=0; i < numberOfNodes; i++)
mesh.getKdTree().add(nodelist[i]);
if (mesh.hasNodes())
{
for (int i=0; i < numberOfNodes; i++)
mesh.add(nodelist[i]);
}
int numberOfTriangles = Integer.parseInt(
xpath.evaluate("number/text()", submeshTriangles));
logger.fine("Reading "+numberOfTriangles+" elements");
Triangle [] facelist = new Triangle[numberOfTriangles];
int [] ind = new int[3];
Vertex2D [] pts = new Vertex2D[3];
for (int i=0; i < numberOfTriangles; i++)
{
boolean outer = false;
for (int j = 0; j < 3; j++)
{
ind[j] = ifrT.get();
if (ind[j] < 0)
{
ind[j] = - ind[j];
outer = true;
}
pts[j] = nodelist[ind[j]];
}
facelist[i] = mesh.createTriangle(pts[0], pts[1], pts[2]);
if (outer)
{
facelist[i].setAttributes(AbstractHalfEdge.OUTER);
facelist[i].setReadable(false);
facelist[i].setWritable(false);
}
mesh.add(facelist[i]);
}
ifrT.close();
dfrN.close();
ifrR.close();
// Build adjacency relations
mesh.buildAdjacency();
}
catch(XPathExpressionException ex)
{
throw new IOException(ex.getMessage());
}
logger.fine("end reading "+JCAEXMLData.xml2dFilename+iFace);
}
public static void readObject3D(Mesh mesh, String xmlDir) throws IOException
{
readObject3D(mesh, xmlDir, true);
}
/**
* Loads an Amibe 3D XML file into an existing Mesh instance.
*
* @param mesh data structure updated when reading files
* @param xmlDir directory containing XML files
* @param buildAdjacency true to build adjacency after reading the mesh
*/
public static void readObject3D(Mesh mesh, String xmlDir, boolean buildAdjacency)
throws IOException
{
try {
AmibeReader.Dim3 reader = new AmibeReader.Dim3(xmlDir);
SubMesh subMesh = reader.getSubmeshes().get(0);
int numberOfReferences = subMesh.getNumberOfReferences();
int[] refs = null;
if (numberOfReferences > 0) {
refs = subMesh.getReferences();
mesh.setPersistentReferences(true);
}
DoubleFileReader dfrN = subMesh.getNodes();
int numberOfNodes = subMesh.getNumberOfNodes();
Vertex[] nodelist = new Vertex[numberOfNodes + 1];
nodelist[numberOfNodes] = mesh.outerVertex;
int label;
double[] coord = new double[3];
double[] bbmin = new double[3];
double[] bbmax = new double[3];
for (int j = 0; j < 3; j++) {
bbmin[j] = Double.MAX_VALUE;
bbmax[j] = Double.MIN_VALUE;
}
mesh.ensureCapacity(2 * numberOfNodes);
for (int i = 0; i < numberOfNodes; i++) {
dfrN.get(coord);
nodelist[i] = mesh.createVertex(coord[0], coord[1], coord[2]);
if (i < numberOfNodes - numberOfReferences) {
label = 0;
} else {
assert refs != null;
label = refs[i + numberOfReferences - numberOfNodes];
}
nodelist[i].setRef(label);
for (int j = 0; j < 3; j++) {
if (coord[j] > bbmax[j]) {
bbmax[j] = coord[j];
}
if (coord[j] < bbmin[j]) {
bbmin[j] = coord[j];
}
}
}
dfrN.close();
if (mesh.hasNodes()) {
for (int i = 0; i < numberOfNodes; i++) {
mesh.add(nodelist[i]);
}
}
int numberOfTriangles = subMesh.getNumberOfTrias();
Triangle[] facelist = new Triangle[numberOfTriangles];
if(numberOfTriangles > 0)
{
IntFileReader ifrT = subMesh.getTriangles();
int[] ind = new int[3];
Vertex[] pts = new Vertex[3];
for (int i = 0; i < numberOfTriangles; i++) {
boolean outer = false;
for (int j = 0; j < 3; j++) {
ind[j] = ifrT.get();
if (ind[j] < 0) {
ind[j] = -ind[j];
outer = true;
}
pts[j] = nodelist[ind[j]];
}
if (!outer) {
facelist[i] = mesh.createTriangle(pts[0], pts[1], pts[2]);
mesh.add(facelist[i]);
}
}
ifrT.close();
}
int numberOfBeams = subMesh.getNumberOfBeams();
if (numberOfBeams > 0)
{
IntFileReader ifrB = subMesh.getBeams();
for (int i = 0; i < numberOfBeams; i++)
mesh.addBeam(nodelist[ifrB.get()], nodelist[ifrB.get()], 0, false);
ifrB.close();
}
int i = 1;
for (AmibeReader.Group g : subMesh.getGroups()) {
int id = i++;
mesh.setGroupName(id, g.getName());
for (int j : g.readTria3Ids())
{
Triangle f = facelist[j];
int prevId = f.getGroupId();
if(prevId > 0)
logger.warning("Trying to tag triangle "+j+" as "+
g.getName()+"/"+id+" while it's alread tagged as "+
mesh.getGroupName(prevId)+"/"+prevId+".");
f.setGroupId(id);
}
for(int j : g.readBeamsIds())
{
if(mesh.getBeamGroup(j) > 0)
logger.warning("Trying to tag a beam as "+g.getName()
+" while it's already in "+
mesh.getGroupName(mesh.getBeamGroup(j)));
mesh.setBeamGroup(j, id);
}
for(int j : g.readNodesIds())
mesh.setVertexGroup(nodelist[j], g.getName());
}
// Build adjacency relations
if (mesh.hasAdjacency() && buildAdjacency) {
logger.fine("Build mesh adjacency");
mesh.buildAdjacency();
}
} catch (SAXException ex) {
throw new IOException(ex);
}
}
// Method previously in MMesh3DReader, remove it?
public static int [] getInfos(String xmlDir)
{
int [] ret = new int[3];
XPath xpath = XPathFactory.newInstance().newXPath();
try
{
Document document = XMLHelper.parseXML(new File(xmlDir, JCAEXMLData.xml3dFilename));
Node submeshElement = (Node) xpath.evaluate("/jcae/mesh/submesh",
document, XPathConstants.NODE);
Node submeshNodes = (Node) xpath.evaluate("nodes", submeshElement,
XPathConstants.NODE);
ret[0] = Integer.parseInt(xpath.evaluate("number/text()",
submeshNodes));
Node submeshTriangles = (Node) xpath.evaluate("triangles",
submeshElement, XPathConstants.NODE);
ret[1] = Integer.parseInt(xpath.evaluate("number/text()",
submeshTriangles));
Node groupsElement = (Node) xpath.evaluate("groups",
submeshElement, XPathConstants.NODE);
NodeList groupsList = (NodeList) xpath.evaluate("group",
groupsElement, XPathConstants.NODESET);
ret[2] = groupsList.getLength();
}
catch(FileNotFoundException ex)
{
// Do nothing if 3d was not processed
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException(ex);
}
return ret;
}
public static void mergeGroups(String xmlDir, String xmlGroupsFile)
{
XPath xpath = XPathFactory.newInstance().newXPath();
try
{
File oldXmlFile = new File(xmlDir, JCAEXMLData.xml3dFilename);
Document document = XMLHelper.parseXML(oldXmlFile);
Node submeshElement = (Node) xpath.evaluate("/jcae/mesh/submesh",
document, XPathConstants.NODE);
Node groupsElement = (Node) xpath.evaluate("groups",
submeshElement, XPathConstants.NODE);
NodeList groupsList = (NodeList) xpath.evaluate("group",
groupsElement, XPathConstants.NODESET);
int numberOfGroups = groupsList.getLength();
String groupsFileName = xpath.evaluate("file/@location", groupsList.item(0));
String groupsFileDir = null;
if (groupsFileName.charAt(0) != File.separatorChar)
groupsFileDir = xmlDir;
File oldGroupsFile = new File(groupsFileDir, groupsFileName);
IntFileReader ifrG = new PrimitiveFileReaderFactory().getIntReader(oldGroupsFile);
int maxId = -1;
MGroup3D [] groups = new MGroup3D[numberOfGroups];
TIntIntHashMap numGroups = new TIntIntHashMap(numberOfGroups);
for (int i=0; i < numberOfGroups; i++)
{
Element groupNode = (Element) groupsList.item(i);
int numberOfElements = Integer.parseInt(groupNode.getElementsByTagName("number").item(0).getTextContent());
int fileOffset = Integer.parseInt(((Element) groupNode.getElementsByTagName("file").item(0)).getAttribute("offset"));
int id = Integer.parseInt(groupNode.getAttribute("id"));
numGroups.put(id, i);
String name = groupNode.getElementsByTagName("name").item(0).getTextContent();
logger.fine("Group "+name+": reading "+numberOfElements+" elements");
maxId = Math.max(maxId, id);
Collection newfacelist = new ArrayList(numberOfElements);
for (int j=0; j < numberOfElements; j++)
newfacelist.add(Integer.valueOf(ifrG.get(fileOffset+j)));
groups[i] = new MGroup3D(id, name, newfacelist);
}
ifrG.close();
// Now merge groups
Document documentGroup = XMLHelper.parseXML(new File(xmlDir, xmlGroupsFile));
Node newGroupsElement = (Node) xpath.evaluate("/mergegroups",
documentGroup, XPathConstants.NODE);
NodeList newGroupsList = (NodeList) xpath.evaluate("newgroup",
newGroupsElement, XPathConstants.NODESET);
int numberOfNewGroups = newGroupsList.getLength();
MGroup3D [] tmpgroups = new MGroup3D[numberOfGroups+numberOfNewGroups];
System.arraycopy(groups, 0, tmpgroups, 0, groups.length);
groups = tmpgroups;
for (int i=0; i < numberOfNewGroups; i++)
{
Node newGroupNode = newGroupsList.item(i);
maxId++;
String name = xpath.evaluate("name/text()", newGroupNode);
groups[numberOfGroups+i] = new MGroup3D(maxId, name, new ArrayList());
numGroups.put(maxId, numberOfGroups+i);
NodeList oldGroupsList = (NodeList) xpath.evaluate("oldgroup",
newGroupNode, XPathConstants.NODESET);
int numberOfOldGroups = oldGroupsList.getLength();
logger.fine("Group "+name+": merging "+numberOfOldGroups+" groups");
for (int j=0; j < numberOfOldGroups; j++)
{
Node oldGroupNode = oldGroupsList.item(j);
int id = Integer.parseInt(xpath.evaluate("@id", oldGroupNode));
int k = numGroups.get(id);
if (k < 0 || k >= groups.length || groups[k] == null)
throw new RuntimeException("Group id "+id+" does not exist. Aborting.");
groups[numberOfGroups+i].merge(groups[k]);
groups[k] = null;
}
}
// Now write merged groups onto file
File newGroupsFile = new File(groupsFileDir, groupsFileName+"-tmp");
DataOutputStream out=new DataOutputStream(new BufferedOutputStream(new FileOutputStream(newGroupsFile)));
Element newGroups=document.createElement("groups");
for (int i=0; i < groups.length; i++)
{
if (groups[i] == null)
continue;
newGroups.appendChild(
XMLHelper.parseXMLString(document, "<group id=\""+groups[i].getId()+"\">"+
"<name>"+groups[i].getName()+"</name>"+
"<number>"+groups[i].numberOfFaces()+"</number>"+
"<file format=\"integerstream\" location=\""+
XMLHelper.canonicalize(xmlDir, oldGroupsFile.toString())+"\""+
" offset=\""+out.size()/4+"\"/></group>"));
Iterator it = groups[i].getFacesIterator();
while(it.hasNext())
out.writeInt(((Integer) it.next()).intValue());
}
out.close();
// Replace <groups> element
submeshElement.replaceChild(newGroups, groupsElement);
File newXmlFile = new File(xmlDir, JCAEXMLData.xml3dFilename+"-tmp");
XMLHelper.writeXML(document, newXmlFile);
if (!newXmlFile.renameTo(oldXmlFile))
throw new RuntimeException("Cannot rename "+newXmlFile+" into "+oldXmlFile);
if (!newGroupsFile.renameTo(oldGroupsFile))
throw new RuntimeException("Cannot rename "+newGroupsFile+" into "+oldGroupsFile);
}
catch(Exception ex)
{
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
public static void main(String [] args)
{
Mesh mesh = new Mesh();
try
{
readObject3D(mesh, args[0]);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}