/* * 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 2013, by EADS France */ package org.jcae.mesh.amibe.algos3d; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.DoubleBuffer; import java.nio.IntBuffer; import java.nio.channels.FileChannel; import java.util.Iterator; import java.util.logging.Level; import java.util.logging.Logger; import org.jcae.mesh.amibe.ds.AbstractHalfEdge; 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.metrics.MetricSupport; import org.jcae.mesh.amibe.projection.MeshLiaison; import org.jcae.mesh.amibe.projection.TriangleKdTree; import org.jcae.mesh.amibe.traits.MeshTraitsBuilder; import org.jcae.mesh.xmldata.Amibe2VTK; import org.jcae.mesh.xmldata.MeshReader; import org.jcae.mesh.xmldata.MeshWriter; /** * Insert beams on the border of groups. * The inserted half edges are then tagged as immutable * @author Jerome Robert */ public class BeamInsertion { private final static Logger LOGGER = Logger.getLogger(BeamInsertion.class.getName()); private class BorderTriangleIterator implements Iterator<Triangle> { private final Iterator<Triangle> delegate; private Triangle current; public BorderTriangleIterator(Mesh mesh) { this.delegate = mesh.getTriangles().iterator(); } private void nextImpl() { if(current == null) { while((current == null || !isTargetTriangle(current)) && delegate.hasNext()) current = delegate.next(); } } public boolean hasNext() { nextImpl(); return delegate.hasNext(); } public Triangle next() { nextImpl(); Triangle toReturn = current; current = null; return toReturn; } public void remove() { throw new UnsupportedOperationException(); } } private final Mesh mesh; private final TriangleKdTree kdTree; private final VertexSwapper swapper; //only for tolerance private final MetricSupport.AnalyticMetricInterface metric; private final EdgesCollapser edgesCollapser; public BeamInsertion(final Mesh mesh, final double edgeSize) { this(mesh, new MetricSupport.AnalyticMetricInterface() { public double getTargetSize(double x, double y, double z, int groupId) { return edgeSize; } public double getTargetSizeTopo(Mesh mesh, Vertex v) { return edgeSize; } }); } public BeamInsertion(final Mesh mesh, MetricSupport.AnalyticMetricInterface metric) { this.mesh = mesh; kdTree = new TriangleKdTree(new Iterable<Triangle>() { public Iterator<Triangle> iterator() { return new BorderTriangleIterator(mesh); } }); this.metric = metric; swapper = new VertexSwapper(mesh); swapper.setKdTree(kdTree); edgesCollapser = new EdgesCollapser(mesh) { @Override protected void collapsingEdge(AbstractHalfEdge edge) { Iterator<Triangle> it = edge.origin().getNeighbourIteratorTriangle(); while(it.hasNext()) { Triangle e = it.next(); if(isTargetTriangle(e)) kdTree.remove(e); } it = edge.destination().getNeighbourIteratorTriangle(); while(it.hasNext()) { Triangle e = it.next(); if(isTargetTriangle(e)) kdTree.remove(e); } } }; } protected boolean isTargetEdge(AbstractHalfEdge edge) { return !edge.hasAttributes(AbstractHalfEdge.OUTER) && (edge.hasAttributes(AbstractHalfEdge.BOUNDARY) || edge.hasAttributes(AbstractHalfEdge.NONMANIFOLD)); } protected boolean isTargetTriangle(Triangle t) { return !t.hasAttributes(AbstractHalfEdge.OUTER) && (t.hasAttributes(AbstractHalfEdge.BOUNDARY) || t.hasAttributes(AbstractHalfEdge.NONMANIFOLD)); } /** Debug method */ private void saveBeamAsVTP(DoubleBuffer vertices, IntBuffer beams) { try { Mesh m = new Mesh(); double[] va = new double[vertices.capacity()]; vertices.get(va); vertices.rewind(); int[] ia = new int[beams.capacity()]; beams.get(ia); beams.rewind(); m.pushGroup(va, null, ia); String p = "/tmp/amibe-beaminsertiondbg.amibe"; MeshWriter.writeObject3D(m, p, null); new Amibe2VTK(p).write("/tmp/amibe-beaminsertiondbg.vtp"); } catch (Exception ex) { Logger.getLogger(BeamInsertion.class.getName()).log(Level.SEVERE, null, ex); } } /** Insert a set of beams from binary files */ public void insert(String vertexFile, String beamFile) throws IOException { FileChannel vertexChannel = new FileInputStream(vertexFile).getChannel(); ByteBuffer bb = ByteBuffer.allocate((int)vertexChannel.size()); bb.order(ByteOrder.nativeOrder()); vertexChannel.read(bb); bb.rewind(); DoubleBuffer vertexBuffer = bb.asDoubleBuffer(); FileChannel beamChannel = new FileInputStream(beamFile).getChannel(); bb = ByteBuffer.allocate((int)beamChannel.size()); bb.order(ByteOrder.nativeOrder()); beamChannel.read(bb); bb.rewind(); IntBuffer beamBuffer = bb.asIntBuffer(); int nbBeams = beamBuffer.capacity() / 2; for(int i = 0; i < nbBeams; i++) { int i1 = beamBuffer.get(); int i2 = beamBuffer.get(); assert i1 != i2: "Beam number "+i+" on "+nbBeams+" is degenerated: "+i1+"-"+i2; Vertex v1 = createVertex(i1, vertexBuffer); Vertex v2 = createVertex(i2, vertexBuffer); setImmutable(insert(v1, v2)); } } private static void setImmutable(AbstractHalfEdge edge) { if(edge.hasAttributes(AbstractHalfEdge.NONMANIFOLD)) { Iterator<AbstractHalfEdge> it = edge.fanIterator(); while(it.hasNext()) { AbstractHalfEdge e = it.next(); e.setAttributes(AbstractHalfEdge.IMMUTABLE); e.sym().setAttributes(AbstractHalfEdge.IMMUTABLE); } } else { edge.setAttributes(AbstractHalfEdge.IMMUTABLE); edge.sym().setAttributes(AbstractHalfEdge.IMMUTABLE); } edge.origin().setMutable(false); edge.destination().setMutable(false); } private Vertex createVertex(int i, DoubleBuffer vertices) { int k = 3 * i; double x = vertices.get(k++); double y = vertices.get(k++); double z = vertices.get(k); return mesh.createVertex(x, y, z); } /** Insert v1-v2 as an AbstractHalfEdge and return it */ public AbstractHalfEdge insert(Vertex vv1, Vertex vv2) { Vertex v1 = insert(vv1); Vertex v2 = insert(vv2); AbstractHalfEdge toCollapse = edgesCollapser.collapse(v1, v2); if(toCollapse == null) throw new IllegalStateException("Cannot insert the beam:\n"+vv1+" - "+vv2+"\nas: "+v1+" - "+v2); Iterator<Triangle> it = toCollapse.origin().getNeighbourIteratorTriangle(); while(it.hasNext()) { Triangle e = it.next(); if(isTargetTriangle(e)) kdTree.addTriangle(e, true); } it = toCollapse.destination().getNeighbourIteratorTriangle(); while(it.hasNext()) { Triangle e = it.next(); if(isTargetTriangle(e)) kdTree.addTriangle(e, true); } swapper.swap(v1); swapper.swap(v2); return toCollapse; } private void addTriangleToKdTree(AbstractHalfEdge edge) { if(!edge.hasAttributes(AbstractHalfEdge.OUTER)) kdTree.addTriangle(edge.getTri()); } private void removeFromKdTree(AbstractHalfEdge edge) { if(edge.hasAttributes(AbstractHalfEdge.NONMANIFOLD)) { Iterator<AbstractHalfEdge> it = edge.fanIterator(); while(it.hasNext()) { AbstractHalfEdge e = it.next(); if(!e.hasAttributes(AbstractHalfEdge.OUTER)) kdTree.remove(e.getTri()); } } else { if(!edge.hasAttributes(AbstractHalfEdge.OUTER)) kdTree.remove(edge.getTri()); if(!edge.sym().hasAttributes(AbstractHalfEdge.OUTER)) kdTree.remove(edge.sym().getTri()); } } private Vertex insert(Vertex v) { Triangle t = kdTree.getClosestTriangle(v, null, -1); AbstractHalfEdge e = MeshLiaison.findNearestEdge(v, t); double localMetric = metric.getTargetSize( v.getX(), v.getY(), v.getZ(), -1); double localMetric2 = localMetric * localMetric; double tol2 = localMetric2 / (40 * 40); if(v.sqrDistance3D(e.origin()) < tol2) return e.origin(); if(v.sqrDistance3D(e.destination()) < tol2) return e.destination(); if(!isTargetEdge(e)) { throw new IllegalStateException("Cannot project "+v+ " to any boundary or non manifold edges of triangle\n"+t); } removeFromKdTree(e); mesh.vertexSplit(e, v); AbstractHalfEdge newEdge = e.next().sym().next(); if(e.hasAttributes(AbstractHalfEdge.NONMANIFOLD)) { Iterator<AbstractHalfEdge> it = e.fanIterator(); while(it.hasNext()) addTriangleToKdTree(it.next()); it = newEdge.fanIterator(); while(it.hasNext()) addTriangleToKdTree(it.next()); } else { addTriangleToKdTree(e); addTriangleToKdTree(newEdge); } return v; } public static void main(final String[] args) { try { Mesh mesh = new Mesh(MeshTraitsBuilder.getDefault3D()); MeshReader.readObject3D(mesh, "/tmp/AMIBE/Container.amibe"); mesh.buildGroupBoundaries(); MeshLiaison liaison = MeshLiaison.create(mesh); BeamInsertion bi = new BeamInsertion(liaison.getMesh(), 200); AbstractHalfEdge e = bi.insert( mesh.createVertex(1000, -500, 0), mesh.createVertex(1000, 100, 0)); setImmutable(e); bi = new BeamInsertion(liaison.getMesh(), 200); e = bi.insert( mesh.createVertex(500, 3000, 0), mesh.createVertex(-500, 3000, 0)); setImmutable(e); RemeshSkeleton rs = new RemeshSkeleton(liaison, -2, 1.0, 300); rs.compute(); MeshWriter.writeObject3D(liaison.getMesh(), "/tmp/AMIBE/c2.amibe", null); } catch (IOException ex) { Logger.getLogger(BeamInsertion.class.getName()).log(Level.SEVERE, null, ex); } } }