/* * Copyright (c) 2003-2009 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package automenta.spacenet.space.geom.text3d.math; import com.ardor3d.math.FastMath; import com.ardor3d.math.Vector3; import java.util.logging.Logger; /** * Used to do a triangulation of a complex polygon. * Please note that you should make sure all of these vertices are two-manifold, * if they are not the triangulation will fail with nullpointers. * * @author emanuel */ public class TriangulationVertex extends PlanarVertex { private static final Logger logger = Logger .getLogger(TriangulationVertex.class.getName()); // Easy access pointers //TriangulationVertex prev_vert,next_vert; //TriangulationEdge ingoing_edge,outgoing_edge; public enum VertexType { START, END, SPLIT, MERGE, REGULAR_RIGHT, REGULAR_LEFT, UNSET } VertexType vert_type = VertexType.UNSET; public boolean is_left_chain = false; TriangulationVertex(int i, Vector3 p) { super(i, p); } boolean yLessThan(PlanarVertex vertex) { return (point.getY() == vertex.point.getY() ? point.getX() > vertex.point.getX() : point.getY() < vertex.point.getY()); } VertexType getType() { if(vert_type == VertexType.UNSET) { //logger.info("VertexType not set!"); } return vert_type; } @Override public String toString() { return "[indx:"+index+",("+point.getX()+","+point.getY()+"),type:"+vert_type+"]"; } public void initializeType() { // Find the previous and next vertex TriangulationEdge outgoing_edge = getOutGoingEdge(); TriangulationEdge ingoing_edge = getInGoingEdge(); TriangulationVertex prev_vert = (TriangulationVertex) ingoing_edge.getOrigin(); TriangulationVertex next_vert = (TriangulationVertex) outgoing_edge.getTwin().getOrigin(); if(prev_vert.yLessThan(this) && next_vert.yLessThan(this)) { // Are we at the top but are we start/split Vector3 v1 = prev_vert.point; Vector3 v2 = point; Vector3 v = next_vert.point; float turnang = (v2.getXf() - v1.getXf()) * (v.getYf() - v1.getYf()) - (v.getXf() - v1.getXf()) * (v2.getYf() - v1.getYf()); if(turnang > 0) { vert_type = VertexType.START; } else { vert_type = VertexType.SPLIT; } } else if(yLessThan(prev_vert) && yLessThan(next_vert)) { // We are at the bottom, but are we end/merge ? Vector3 v1 = prev_vert.point; Vector3 v2 = point; Vector3 v = next_vert.point; float turnang = (v2.getXf() - v1.getXf()) * (v.getYf() - v1.getYf()) - (v.getXf() - v1.getXf()) * (v2.getYf() - v1.getYf()); if(turnang > 0) { vert_type = VertexType.END; } else { vert_type = VertexType.MERGE; } } else if(prev_vert.yLessThan(this)) { // Regular on the right side vert_type = VertexType.REGULAR_RIGHT; } else if(next_vert.yLessThan(this)) { // Regular on the right side vert_type = VertexType.REGULAR_LEFT; } else { //logger.info("PNIX: we are none of the above types !!!!"); //logger.info("GetType: (prev:"+prev_vert+",this:"+this+",next:"+next_vert); } } public boolean checkAllEdges() { // This one is used for sanity (HACK: hardcoded value) int sanity_check = 10000; int edgecount = 0; // Walk around our-selves with tmp = outgoing; tmp = tmp.prev.twin (clockwise) PlanarEdge tmp = getFirstEdge(); float anglesum = 0; float anglimit = PlanarEdge.TWO_PI + PlanarEdge.FLT_EPSILON*2; edgecount = 0; if(tmp != null) { sanity_check = 10000; do { // Test that tmp has us as origin if(tmp.getOrigin() != this) { throw new GeometricException("edge "+tmp+" does not have a correct origin"); } // Test surface links (clockwise) PlanarEdge tmp2 = tmp; //String debugString = "["; do { // //debugString += " -> "+tmp2; if(tmp2.isRealEdge() != tmp2.getPrev().isRealEdge()) { //logger.info("VERT: "+tmp2.getOrigin()); //logger.info("Edge1:"+tmp2); //logger.info("Edge2:"+tmp2.getPrev()); //logger.info("Tour: "+debugString+" -> "+tmp2.getPrev()); throw new GeometricException("Bound two edges, one real one unreal, that is not possible in a closed polygon"); } tmp2 = tmp2.getPrev(); if(sanity_check-- <= 0) throw new GeometricException("Sanity check !"); } while(tmp2 != tmp); anglesum += tmp.getTwin().getNext().angleCounterClockWise(tmp); edgecount++; if(anglesum > anglimit) { //logger.info("HERE ARE MY EDGES"); //printEdges(); throw new GeometricException("The sum of angles between edges exceeded 2 PI ("+anglesum+" > "+anglimit+") on this vert: "+this); } tmp = tmp.getTwin().getNext(); } while(tmp != getFirstEdge()); //logger.info("anglesum:"+anglesum+",edgecount:"+edgecount); } // Walk around our-selves with tmp = outgoing; tmp = tmp.twin.next(counter-clockwise) tmp = getFirstEdge(); anglesum = 0; edgecount = 0; if(tmp != null) { sanity_check = 10000; edgecount = 0; do { // Test that tmp has us as origin if(tmp.getOrigin() != this) { throw new GeometricException("edge "+tmp+" does not have a correct origin"); } // Test surface links (counter-clockwise) PlanarEdge tmp2 = tmp; do { if(tmp2.isRealEdge() != tmp2.getNext().isRealEdge()) { //logger.info("VERT: "+tmp2.getOrigin()); //logger.info("Edge1:"+tmp2); //logger.info("Edge2:"+tmp2.getNext()); throw new GeometricException("Bound two edges, one real one unreal, that is not possible in a closed polygon"); } tmp2 = tmp2.getNext(); if(sanity_check-- <= 0) throw new GeometricException("Sanity check !"); } while(tmp2 != tmp); anglesum += tmp.angleCounterClockWise(tmp.getPrev().getTwin()); edgecount++; if(anglesum > anglimit) { throw new GeometricException("The sum of angles between edges exceeded 2 PI ("+anglesum+" > "+anglimit+") on this vert: "+this); } tmp = tmp.getPrev().getTwin(); } while(tmp != getFirstEdge()); //logger.info("anglesum:"+anglesum+",edgecount:"+edgecount); } return true; } /** * This method returns the first and best real edge going out of this vertex, there should be only one before the triangulation. * * @return */ public TriangulationEdge getOutGoingEdge() { if(getFirstEdge() == null) return null; TriangulationEdge next = (TriangulationEdge) getFirstEdge(); do { if(next.isRealEdge()) return next; next = (TriangulationEdge) next.getTwin().getNext(); } while(next != getFirstEdge()); // Only one round return null; } /** * This method returns the first and best real edge going in to this vertex, there should be only one before the triangulation. * * @return */ public TriangulationEdge getInGoingEdge() { if(getFirstEdge() == null) return null; TriangulationEdge next = (TriangulationEdge) getFirstEdge(); do { if(next.getTwin().isRealEdge()) return (TriangulationEdge) next.getTwin(); next = (TriangulationEdge) next.getTwin().getNext(); } while(next != getFirstEdge()); // Only one round return null; } }