/** * Copyright 2010 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. 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. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package jogamp.graph.curve.tess; import java.util.ArrayList; import java.util.List; import com.jogamp.graph.curve.tess.Triangulator; import com.jogamp.graph.geom.Outline; import com.jogamp.graph.geom.Triangle; import com.jogamp.graph.geom.Vertex; import com.jogamp.opengl.math.VectorUtil; import jogamp.opengl.Debug; /** * Constrained Delaunay Triangulation * implementation of a list of Outlines that define a set of * Closed Regions with optional n holes. */ public class CDTriangulator2D implements Triangulator { protected static final boolean DEBUG = Debug.debug("graph.curve.Triangulation"); private static final boolean TEST_LINE_AA = Debug.debug("graph.curve.triangulation.LINE_AA"); private static final boolean TEST_MARK_LINE = Debug.debug("graph.curve.triangulation.MARK_AA"); private static final boolean TEST_ENABLED = TEST_LINE_AA || TEST_MARK_LINE; private final ArrayList<Loop> loops = new ArrayList<Loop>(); private int addedVerticeCount; private int maxTriID; /** Constructor for a new Delaunay triangulator */ public CDTriangulator2D() { reset(); } @Override public final void reset() { maxTriID = 0; addedVerticeCount = 0; loops.clear(); } @Override public final int getAddedVerticeCount() { return addedVerticeCount; } @Override public final void addCurve(final List<Triangle> sink, final Outline polyline, final float sharpness) { Loop loop = null; if(!loops.isEmpty()) { loop = getContainerLoop(polyline); } if(loop == null) { final GraphOutline outline = new GraphOutline(polyline); final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, false, sharpness); // vertices.addAll(polyline.getVertices()); loop = new Loop(innerPoly, VectorUtil.Winding.CCW); loops.add(loop); } else { final GraphOutline outline = new GraphOutline(polyline); final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, true, sharpness); // vertices.addAll(innerPoly.getVertices()); loop.addConstraintCurve(innerPoly); } } @Override public final void generate(final List<Triangle> sink) { final int loopsSize = loops.size(); for(int i=0;i<loopsSize;i++) { final Loop loop = loops.get(i); int numTries = 0; int size = loop.computeLoopSize(); while(!loop.isSimplex()){ final Triangle tri; if(numTries > size){ tri = loop.cut(false); } else{ tri = loop.cut(true); } numTries++; if(tri != null) { numTries = 0; size--; tri.setId(maxTriID++); sink.add(tri); if(DEBUG){ System.err.println("CDTri.gen["+i+"].0: "+tri); } } if(numTries > size*2){ if(DEBUG){ System.err.println("CDTri.gen["+i+"].X: Triangulation not complete!"); } break; } } final Triangle tri = loop.cut(true); if(tri != null) { sink.add(tri); if(DEBUG){ System.err.println("CDTri.gen["+i+"].1: "+tri); } } } if( TEST_ENABLED ) { final float[] tempV2 = new float[2]; final CDTriangulator2DExpAddOn addOn = new CDTriangulator2DExpAddOn(); final int sinkSize = sink.size(); if( TEST_MARK_LINE ) { for(int i=0; i<sinkSize; i++) { final Triangle t0 = sink.get(i); addOn.markLineInTriangle(t0, tempV2); } } else if ( TEST_LINE_AA ){ for(int i=0; i<sinkSize-1; i+=2) { final Triangle t0 = sink.get(i); final Triangle t1 = sink.get(i+1); /* final float[] rect = */ addOn.processLineAA(i, t0, t1, tempV2); } } } } private GraphOutline extractBoundaryTriangles(final List<Triangle> sink, final GraphOutline outline, final boolean hole, final float sharpness) { final GraphOutline innerOutline = new GraphOutline(); final ArrayList<GraphVertex> outVertices = outline.getGraphPoint(); final int size = outVertices.size(); for(int i=0; i < size; i++) { final GraphVertex gv1 = outVertices.get(i); // currentVertex final GraphVertex gv0 = outVertices.get((i+size-1)%size); // -1 final GraphVertex gv2 = outVertices.get((i+1)%size); // +1 if( !gv1.getPoint().isOnCurve() ) { final Vertex v0 = gv0.getPoint().clone(); final Vertex v2 = gv2.getPoint().clone(); final Vertex v1 = gv1.getPoint().clone(); addedVerticeCount += 3; final boolean[] boundaryVertices = { true, true, true }; gv0.setBoundaryContained(true); gv1.setBoundaryContained(true); gv2.setBoundaryContained(true); final Triangle t; final boolean holeLike; if(VectorUtil.ccw(v0,v1,v2)) { holeLike = false; t = new Triangle(v0, v1, v2, boundaryVertices); } else { holeLike = true; t = new Triangle(v2, v1, v0, boundaryVertices); } t.setId(maxTriID++); sink.add(t); if(DEBUG){ System.err.println(t); } if( hole || holeLike ) { v0.setTexCoord(0.0f, -0.1f, 0f); v2.setTexCoord(1.0f, -0.1f, 0f); v1.setTexCoord(0.5f, -sharpness-0.1f, 0f); innerOutline.addVertex(gv1); } else { v0.setTexCoord(0.0f, 0.1f, 0f); v2.setTexCoord(1.0f, 0.1f, 0f); v1.setTexCoord(0.5f, sharpness+0.1f, 0f); } if(DEBUG) { System.err.println("CDTri.ebt["+i+"].0: hole "+(hole || holeLike)+" "+gv1+", "+t); } } else { if( !gv2.getPoint().isOnCurve() || !gv0.getPoint().isOnCurve() ) { gv1.setBoundaryContained(true); } innerOutline.addVertex(gv1); if(DEBUG) { System.err.println("CDTri.ebt["+i+"].1: "+gv1); } } } return innerOutline; } private Loop getContainerLoop(final Outline polyline) { final ArrayList<Vertex> vertices = polyline.getVertices(); for(int i=0; i < loops.size(); i++) { final Loop loop = loops.get(i); for(int j=0; j < vertices.size(); j++) { if( loop.checkInside( vertices.get(j) ) ) { return loop; } } } return null; } }