/* * 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 2012, by EADS France */ package org.jcae.mesh.amibe.algos3d; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; 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.Vertex; import org.jcae.mesh.amibe.metrics.MetricSupport.AnalyticMetricInterface; import org.jcae.mesh.amibe.projection.MeshLiaison; import org.jcae.mesh.xmldata.MeshReader; import org.jcae.mesh.xmldata.MeshWriter; /** * * @author Jerome Robert */ public class RemeshSkeleton { private final Mesh mesh; private final double angle; /** tolerance for point insertion */ private final double tolerance; private final AnalyticMetricInterface metric; private final MeshLiaison liaison; private final VertexSwapper vertexSwapper; private double swapVolume; public RemeshSkeleton(MeshLiaison liaison, double angle, double tolerance, final double size) { this(liaison, angle, tolerance, new AnalyticMetricInterface() { public double getTargetSize(double x, double y, double z, int groupId) { return size; } public double getTargetSizeTopo(Mesh mesh, Vertex v) { return size; } }); } public RemeshSkeleton(MeshLiaison liaison, double angle, double tolerance, AnalyticMetricInterface metric) { this.liaison = liaison; this.mesh = liaison.getMesh(); this.angle = angle; this.tolerance = tolerance * tolerance; this.metric = metric; vertexSwapper = new VertexSwapper(liaison) { @Override protected boolean isQualityImproved(AbstractHalfEdge.Quality quality) { return super.isQualityImproved(quality) && quality.swappedVolume() < swapVolume; } }; } private AbstractHalfEdge getEdge(List<Vertex> polyline, int segId) { return getEdge(polyline.get(segId), polyline.get(segId+1)); } private AbstractHalfEdge getEdge(Vertex v1, Vertex v2) { Iterator<AbstractHalfEdge> it = v1.getNeighbourIteratorAbstractHalfEdge(); while(it.hasNext()) { AbstractHalfEdge e = it.next(); if(e.destination() == v2 && !e.hasAttributes(AbstractHalfEdge.OUTER)) return e; } //TODO in some cases v1.getNeighbourIteratorAbstractHalfEdge does not //return all HE. Check why. Star with vertex with more than one outer //triangle (more than one group/group frontier). it = v2.getNeighbourIteratorAbstractHalfEdge(); while(it.hasNext()) { AbstractHalfEdge e = it.next(); if(e.destination() == v1 && !e.sym().hasAttributes(AbstractHalfEdge.OUTER)) return e.sym(); } throw new NoSuchElementException(v1+" "+v2+" "+v1.getLink()+" "+v2.getLink()); } public void compute() { Skeleton skeleton = new Skeleton(mesh, angle); EdgesCollapserNG edgeCollapser = new EdgesCollapserNG(mesh); main: for(List<Vertex> polyline: skeleton.getPolylinesVertices()) { RemeshPolyline rp = new RemeshPolyline(mesh, polyline, metric); rp.setBuildBackgroundLink(true); List<Vertex> toInsert = rp.compute(); if(polyline.size() == 2 && toInsert.size() == 2) continue; List<Integer> bgLink = rp.getBackgroundLink(); ArrayList<AbstractHalfEdge> edgeIndex = new ArrayList<AbstractHalfEdge>(polyline.size()-1); for(int i = 0; i < polyline.size() - 1; i++) { AbstractHalfEdge e = getEdge(polyline, i); if(e.hasAttributes(AbstractHalfEdge.IMMUTABLE)) continue main; edgeIndex.add(e); } for(int k = 0; k < toInsert.size(); k++) { Vertex v = toInsert.get(k); int segId = bgLink.get(k); AbstractHalfEdge toSplit = edgeIndex.get(segId); double od = v.sqrDistance3D(toSplit.origin()); double dd = v.sqrDistance3D(toSplit.destination()); if(od <= dd && od <= tolerance) { toInsert.set(k, toSplit.origin()); } else if(dd <= tolerance) { toInsert.set(k, toSplit.destination()); } else { Vertex oldDestination = toSplit.destination(); mesh.vertexSplit(toSplit, v); liaison.addVertex(v, toSplit.getTri()); //TODO this will be slow as as toSplit.getTri() may be far //from the wanted triangle so we will loop on all triangles liaison.move(v, v, true); AbstractHalfEdge e = getEdge(v, oldDestination); edgeIndex.set(segId, e); double m2 = metric.getTargetSizeTopo(mesh, v); swapVolume = m2 * m2 * m2 / 64; vertexSwapper.swap(v); } } if(toInsert.size() == 2) { int n = Math.max(1, polyline.size() / 2); if(toInsert.get(0) != toInsert.get(1)) edgeCollapser.collapse(toInsert.get(0), toInsert.get(1), polyline.get(n)); } else { for(int k = 0; k < toInsert.size() - 1; k++) edgeCollapser.collapse(toInsert.get(k), toInsert.get(k+1), null); } for(Vertex v:toInsert) v.setMutable(false); } } public static void main(final String[] args) { try { Mesh mesh = new Mesh(); MeshReader.readObject3D(mesh, "/home/robert/ast-a319-neo/demo-anabelle/demo/amibe.dir"); MeshLiaison liaison = MeshLiaison.create(mesh); liaison.getMesh().buildGroupBoundaries(); new RemeshSkeleton(liaison, 0, 1.0, 300).compute(); MeshWriter.writeObject3D(liaison.getMesh(), "/home/robert/ast-a319-neo/demo-anabelle/demo/amibe2.amibe", null); } catch (IOException ex) { Logger.getLogger(RemeshSkeleton.class.getName()).log(Level.SEVERE, null, ex); } } }