/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD modeler, Finite element mesher, Plugin architecture. Copyright (C) 2007,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; import org.jcae.mesh.oemm.OEMM; import org.jcae.mesh.oemm.Storage; import org.jcae.mesh.oemm.MeshReader; import org.jcae.mesh.oemm.TraversalProcedure; import org.jcae.mesh.oemm.OEMM.Node; import org.jcae.mesh.amibe.ds.Mesh; import org.jcae.mesh.amibe.algos3d.AbstractAlgoHalfEdge; import org.jcae.mesh.amibe.algos3d.QEMDecimateHalfEdge; import org.jcae.mesh.amibe.traits.MeshTraitsBuilder; import org.jcae.mesh.amibe.traits.TriangleTraitsBuilder; import java.util.HashMap; import java.util.Map; import gnu.trove.set.hash.TIntHashSet; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.logging.Logger; /** * Decimates all cells of an OEMM. */ public class MeshOEMMDecimate { private static final Logger logger=Logger.getLogger(MeshOEMMDecimate.class.getName()); private static final int SIZE_OF_BUFFER = 20000024; private final byte[] buffer = new byte[SIZE_OF_BUFFER]; public static void main(String[] args) throws IOException { if (args.length < 2) { System.out.println("Usage: MeshOEMMDecimate oemm scaleTriangles [minimal number for decimation [output directory]]"); System.exit(0); } String dir = args[0]; int scale = Integer.valueOf(args[1]).intValue(); int minimalNumberOfTriangles = 0; if (args.length >= 3) { // If an octree node contains less than minimalNumberOfTriangles triangles, // it is not decimated. minimalNumberOfTriangles = Integer.parseInt(args[2]); } if (args.length >= 4) { logger.info("Copy OEMM directory from "+args[0]+" into "+args[3]); dir = args[3]; File decDir = new File(dir); if (decDir.exists()) { deleteFiles(decDir); } new MeshOEMMDecimate().copyFiles(new File(args[0]), decDir); } logger.info("Read OEMM structure"); OEMM oemm = Storage.readOEMMStructure(dir); // Count triangles in non-leaf nodes CountProcedure c_proc = new CountProcedure(); oemm.walk(c_proc); logger.info("Decimate all octree nodes"); MeshReader reader = new MeshReader(oemm); DecimateProcedure d_proc = new DecimateProcedure(reader, scale, minimalNumberOfTriangles); oemm.walk(d_proc); } private static void deleteFiles(File decDir) { if (decDir.isDirectory()) { for (File subFile: decDir.listFiles()) { deleteFiles(subFile); } decDir.delete(); } else { decDir.delete(); } } private void copyFiles(File src, File dest) throws IOException { if (src.isDirectory()) { dest.mkdirs(); String[] list = src.list(); for (String strFile: list) copyFiles(new File(src, strFile), new File(dest, strFile)); } else { FileInputStream fin = new FileInputStream(src); FileOutputStream fout = new FileOutputStream(dest); int c; while ((c = fin.read(buffer)) >= 0) fout.write(buffer, 0, c); fin.close(); fout.close(); } } private static class CountProcedure extends TraversalProcedure { // Add a public constructor to avoid synthetic access public CountProcedure() { } @Override public final int action(OEMM oemm, OEMM.Node current, int octant, int visit) { if (visit != POSTORDER) return OK; current.tn = 0; for (OEMM.Node node: current.child) { if (node != null) current.tn += node.tn; } return OK; } } private static class DecimateProcedure extends TraversalProcedure { private final MeshReader reader; private final int scale; private final int minTN; private final TIntHashSet leaves = new TIntHashSet(); private final Map<String, String> options = new HashMap<String, String>(); private final MeshTraitsBuilder mtb = new MeshTraitsBuilder(); public DecimateProcedure(MeshReader mr, int s, int minimalNumberOfTriangles) { reader = mr; scale = s; minTN = minimalNumberOfTriangles; TriangleTraitsBuilder ttb = new TriangleTraitsBuilder(); ttb.addHalfEdge(); mtb.add(ttb); mtb.addTriangleSet(); mtb.addNodeSet(); } @Override public final int action(OEMM oemm, OEMM.Node current, int octant, int visit) { if (current.tn <= minTN) return SKIPCHILD; if (visit == LEAF) { leaves.clear(); leaves.add(current.leafIndex); process(oemm); return OK; } else if (visit != POSTORDER ) return OK; // Children may have been decimated, update current.tn current.tn = 0; for (OEMM.Node node: current.child) { if (node != null) current.tn += node.tn; } if (current.tn <= minTN) return OK; leaves.clear(); getChildLeaves(current); process(oemm); return OK; } private void getChildLeaves(Node current) { for (OEMM.Node node: current.child) { if (node == null) continue; if (node.isLeaf) { leaves.add(node.leafIndex); } else { getChildLeaves(node); } } } private void process(OEMM oemm) { Mesh amesh = reader.buildMesh(mtb, leaves); int nrT = AbstractAlgoHalfEdge.countInnerTriangles(amesh); options.clear(); options.put("maxtriangles", ""+(nrT / scale)); new QEMDecimateHalfEdge(amesh, options).compute(); Storage.saveNodes(oemm, amesh, leaves); } } }