/* * 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 2014, by EADS France */ package org.jcae.mesh.amibe.algos3d; import java.io.IOException; import java.io.PrintWriter; import java.lang.ProcessBuilder.Redirect; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; 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.amibe.projection.AbstractLocaleRemesher; import org.jcae.mesh.xmldata.MeshWriter; /** * A wrapper for TriMultPoly. * See: * <ul> * <li><a href="http://www.cse.wustl.edu/~zoum/projects/TriMultPoly/">http://www.cse.wustl.edu/~zoum/projects/TriMultPoly/</a></li> * <li><a href="https://bitbucket.org/jeromerobert/trimultpoly/">https://bitbucket.org/jeromerobert/trimultpoly/</a></li> * </ul> * Run with java -Dorg.jcae.amibe.trimultpoly.path=/path/to/trimultpoly.exe with * to enable this algorithm. * @author Jerome Robert */ public class TriMultPoly extends AbstractLocaleRemesher { private final static Logger LOGGER = Logger.getLogger(TriMultPoly.class.getName()); public final static String EXE_PATH = "org.jcae.amibe.trimultpoly.path"; private final String executable; private boolean initDone; private Process process; private boolean delauneyTetra = true, minSet = false, normal = false; private double areaWeight = 0, edgeWeight = 0, dihedralWeight = 1.0; private boolean minMaxDihedral = true; private ReadableByteChannel stderr; private WritableByteChannel stdin; private final ByteBuffer buffer = ByteBuffer.allocate(3 + 4 * 8 + 4 + 4); public TriMultPoly() { this(System.getProperty(EXE_PATH)); } public TriMultPoly(String executable) { this.executable = executable; buffer.order(ByteOrder.nativeOrder()); } public void setDelauneyTetra(boolean delauneyTetra) { this.delauneyTetra = delauneyTetra; } public void setMinSet(boolean minSet) { this.minSet = minSet; } public void setAreaWeight(double areaWeight) { this.areaWeight = areaWeight; } public void setDihedralWeight(double dihedralWeight) { this.dihedralWeight = dihedralWeight; } public void setMinMaxDihedral(boolean minMaxDihedral) { this.minMaxDihedral = minMaxDihedral; } public void setEdgeWeight(double edgeWeight) { this.edgeWeight = edgeWeight; } public boolean isAvailable() { initExe(); return process != null; } private void initExe() { if(initDone) return; initDone = true; if(executable == null) return; ProcessBuilder pb = new ProcessBuilder(executable, "-d"); try { pb.redirectOutput(Redirect.INHERIT); process = pb.start(); stdin = Channels.newChannel(process.getOutputStream()); stderr = Channels.newChannel(process.getErrorStream()); } catch (IOException ex) { LOGGER.log(Level.SEVERE, "Cannot run "+executable, ex); } } /** Save in the native TriMultPoly file format for debugging */ private static void saveCurve(Collection<List<Vertex>> vertices, String fileName) throws IOException { PrintWriter pw = new PrintWriter(fileName); int nbVert = 0; for(List<Vertex> vs: vertices) nbVert += vs.size(); pw.println(vertices.size()); pw.println(nbVert); for(List<Vertex> pl: vertices) { pw.println(pl.size()); for(Vertex v:pl) pw.println(v.getX() + " " + v.getY() + " " + v.getZ()); } pw.close(); } private int sendVertices(Collection<List<Vertex>> vertices) throws IOException { buffer.put((byte) (delauneyTetra ? 1 : 0)); buffer.put((byte) (minSet ? 1 : 0)); buffer.put((byte) (normal ? 1 : 0)); buffer.putDouble(areaWeight); buffer.putDouble(edgeWeight); buffer.putDouble(dihedralWeight); buffer.putDouble(minMaxDihedral ? 1.0 : 0); buffer.putInt(vertices.size()); int nbVert = 0; for(List<Vertex> vs: vertices) nbVert += vs.size(); buffer.putInt(nbVert); initExe(); buffer.rewind(); stdin.write(buffer); buffer.rewind(); for(List<Vertex> vs: vertices) { buffer.putInt(0, vs.size()); buffer.rewind(); buffer.limit(4); stdin.write(buffer); buffer.rewind(); buffer.limit(3*8); for(Vertex v:vs) { buffer.putDouble(v.getX()); buffer.putDouble(v.getY()); buffer.putDouble(v.getZ()); buffer.rewind(); stdin.write(buffer); buffer.rewind(); } } process.getOutputStream().flush(); return nbVert; } protected void triangulate(Mesh mesh, Collection<List<Vertex>> vertices) throws IOException { buffer.limit(4); stderr.read(buffer); int nbTria = buffer.getInt(0); if(nbTria == 0) throw new RuntimeException("trimulpoly was not able to create a triangulation"); buffer.limit(12); buffer.rewind(); for(int i = 0; i < nbTria; i++) { stderr.read(buffer); addTriangle(mesh, buffer.getInt(0), buffer.getInt(4), buffer.getInt(8)); buffer.rewind(); } buffer.clear(); } public void close() { process.destroy(); } @Override protected void finalize() throws Throwable { close(); } public static List<Vertex> createVertices(Mesh mesh, double[] array) { List<Vertex> r = new ArrayList<Vertex>(); for(int i = 0; i < array.length / 3; i++) r.add(mesh.createVertex( array[3 * i], array[3 * i + 1], array[3 * i + 2])); return r; } public static void main(final String[] args) { TriMultPoly tmp = new TriMultPoly( "/home/robert/tetramesh/TriMultPoly_src_v1.1.0/TriMultPoly"); double[] points = { 4.502539, -1.103106, 0.548982, 2.502539, -1.103106, 0.548982, 2.362264, -1.014786, 0.548982, 2.258357, -0.853729, 0.548982, 2.242771, -0.604352, 0.548982, 2.398632, -0.308217, 0.548982, 2.845433, -0.053644, 0.548982, 3.396141, -0.017276, 0.548982, 3.941654, -0.038058, 0.548982, 4.372869, 0.097022, 0.548982, 4.585879, 0.455501, 0.548982, 4.497558, 0.658120, 0.548982, 4.081929, 0.855544, 0.548982, 3.650714, 0.954256, 0.548982, 3.048052, 0.969842, 0.548982, 2.299920, 0.881521, 0.548982, 2.299920, 0.881521, -1.451018, 3.048052, 0.969842, -1.451018, 3.650714, 0.954256, -1.451018, 4.081929, 0.855544, -1.451018, 4.497558, 0.658120, -1.451018, 4.585879, 0.455501, -1.451018, 4.372869, 0.097022, -1.451018, 3.941654, -0.038058, -1.451018, 3.396141, -0.017276, -1.451018, 2.845433, -0.053644, -1.451018, 2.398632, -0.308217, -1.451018, 2.242771, -0.604352, -1.451018, 2.258357, -0.853729, -1.451018, 2.362264, -1.014786, -1.451018, 2.502539, -1.103106, -1.451018, 4.502539, -1.103106, -1.4510180}; Mesh mesh = new Mesh(); List<Vertex> vertices = createVertices(mesh, points); try { tmp.triangulate(mesh, Collections.singleton(vertices)); for(Triangle t: tmp.getNewTriangles()) mesh.add(t); MeshWriter.writeObject3D(mesh, "/tmp/pbl/bug.zebra/trimultpoly.amibe", null); } catch (IOException ex) { LOGGER.log(Level.SEVERE, null, ex); } mesh = new Mesh(); List<Vertex> vertices1 = createVertices(mesh, new double[]{ 0,0,0, 1,0,0, 1,1,0, 0,1,0 }); List<Vertex> vertices2 = createVertices(mesh, new double[]{ 0.3, 0.2, 0, 0.8, 0.9, 0, (0.3+0.8) / 2, (0.2+0.9) / 2, 0 }); tmp = new TriMultPoly("/home/robert/tetramesh/TriMultPoly_src_v1.1.0/TriMultPoly"); //tmp.setDelauneyTetra(false); try { List<List<Vertex>> input = Arrays.asList(vertices1, vertices2); saveCurve(input, "/tmp/trimultpoly.txt"); tmp.triangulate(mesh, input); for(Triangle t: tmp.getNewTriangles()) mesh.add(t); MeshWriter.writeObject3D(mesh, "/tmp/pbl/bug.zebra/trimultpoly2.amibe", null); } catch (IOException ex) { LOGGER.log(Level.SEVERE, null, ex); } } }