/* * 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.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; 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.Matrix3D; /** * Collapse all half edges between 2 vertices so they are linked between only * one half edge. * This algorithm only work if the vertices between the 2 input vertices are * almost aligned. * @author Jerome Robert */ public class EdgesCollapser { private final double[] vector1 = new double[3], vector2 = new double[3]; private final Mesh mesh; private final VertexSwapper vertexSwapper; private transient boolean someCollapseDone; public EdgesCollapser(Mesh mesh) { this.mesh = mesh; this.vertexSwapper = new VertexSwapper(mesh); } private AbstractHalfEdge collapseImpl(Vertex v1, Vertex v2, boolean swap) { v2.sub(v1, vector1); AbstractHalfEdge toCollapse = nextEdge(v1, vector1, v1); assert toCollapse != null : v1+"\n"+v2+"\n"+Arrays.toString(vector1); while(toCollapse.destination() != v2) { if(toCollapse.hasAttributes(AbstractHalfEdge.OUTER)) toCollapse = toCollapse.sym(); assert !toCollapse.hasAttributes(AbstractHalfEdge.OUTER): toCollapse; assert !toCollapse.hasAttributes(AbstractHalfEdge.IMMUTABLE): toCollapse; Vertex target = v1; if(swap && !mesh.canCollapseEdge(toCollapse, v1)) { vertexSwapper.swapOrigin(toCollapse.destination() == v1 ? toCollapse.origin() : toCollapse.destination()); } if(!mesh.canCollapseEdge(toCollapse, v1)) return null; collapsingEdge(toCollapse); someCollapseDone = true; mesh.edgeCollapse(toCollapse, target); toCollapse = nextEdge(v1, vector1, v1); } return toCollapse; } private AbstractHalfEdge collapseImpl(Vertex v1, Vertex v2) { someCollapseDone = false; AbstractHalfEdge r = collapseImpl(v1, v2, false); if(r == null) { // we failed to collapse from v2 to v1, let's try in the other side r = collapseImpl(v2, v1, false); if(r == null) { // we are still failing but the configuration may have change // so let's try forward again but with swap. r = collapseImpl(v1, v2, true); if(r == null) { r = collapseImpl(v2, v1, true); if(r != null) r = r.sym(); } } else r = r.sym(); } return r; } public AbstractHalfEdge collapse(Vertex v1, Vertex v2) { AbstractHalfEdge r = collapseImpl(v1, v2); while(r == null && someCollapseDone) r = collapseImpl(v1, v2); return r; } /** * Called before collapsing an edged. * To be overriden by subclassers. */ protected void collapsingEdge(AbstractHalfEdge edge) { } private AbstractHalfEdge nextEdge(Vertex v, double[] direction, Vertex notDirection) { Iterator<AbstractHalfEdge> it = v.getNeighbourIteratorAbstractHalfEdge(); double bestDot = Double.NEGATIVE_INFINITY; AbstractHalfEdge bestEdge = null; while(it.hasNext()) { AbstractHalfEdge e = it.next(); if((e.hasAttributes(AbstractHalfEdge.NONMANIFOLD) || e.hasAttributes( AbstractHalfEdge.BOUNDARY)) && notDirection != e.destination()) { e.destination().sub(e.origin(), vector2); double norm = Matrix3D.norm(vector2); for(int i = 0; i < 3; i++) vector2[i] /= norm; double dot = Matrix3D.prodSca(direction, vector2); if(dot > bestDot) { bestDot = dot; bestEdge = e; } } } return bestEdge; } }