/** * 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 com.jogamp.graph.curve; import java.util.ArrayList; import java.util.Collections; import com.jogamp.graph.geom.Outline; import com.jogamp.graph.geom.Triangle; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.math.VectorUtil; import com.jogamp.graph.curve.tess.CDTriangulator2D; /** A Generic shape objects which is defined by a list of Outlines. * This Shape can be transformed to Triangulations. * The list of triangles generated are render-able by a Region object. * The triangulation produced by this Shape will define the * closed region defined by the outlines. * * One or more OutlineShape Object can be associated to a region * this is left as a high-level representation of the Objects. For * optimizations, flexibility requirements for future features. * * <br><br> * Example to creating an Outline Shape: * <pre> addVertex(...) addVertex(...) addVertex(...) addEnptyOutline() addVertex(...) addVertex(...) addVertex(...) * </pre> * * The above will create two outlines each with three vertices. By adding these two outlines to * the OutlineShape, we are stating that the combination of the two outlines represent the shape. * <br> * * To specify that the shape is curved at a region, the on-curve flag should be set to false * for the vertex that is in the middle of the curved region (if the curved region is defined by 3 * vertices (quadratic curve). * <br> * In case the curved region is defined by 4 or more vertices the middle vertices should both have * the on-curve flag set to false. * * <br>Example: <br> * <pre> addVertex(0,0, true); addVertex(0,1, false); addVertex(1,1, false); addVertex(1,0, true); * </pre> * * The above snippet defines a cubic nurbs curve where (0,1 and 1,1) * do not belong to the final rendered shape. * * <i>Implementation Notes:</i><br> * <ul> * <li> The first vertex of any outline belonging to the shape should be on-curve</li> * <li> Intersections between off-curved parts of the outline is not handled</li> * </ul> * * @see Outline * @see Region */ public class OutlineShape { public static final int QUADRATIC_NURBS = 10; private final Vertex.Factory<? extends Vertex> vertexFactory; /** The list of {@link Outline}s that are part of this * outline shape. */ private ArrayList<Outline> outlines = new ArrayList<Outline>(3); /** Create a new Outline based Shape */ public OutlineShape(Vertex.Factory<? extends Vertex> factory) { vertexFactory = factory; outlines.add(new Outline()); } /** Returns the associated vertex factory of this outline shape * @return Vertex.Factory object */ public final Vertex.Factory<? extends Vertex> vertexFactory() { return vertexFactory; } /** Add a new empty {@link Outline} * to the shape, this new outline will * be placed at the end of the outline list. * * After a call to this function all new vertices added * will belong to the new outline */ public void addEmptyOutline(){ outlines.add(new Outline()); } /** Adds an {@link Outline} to the OutlineShape object * if last outline of the shape is empty, it will replace * that last Outline with the new one. If outline is empty, * it will do nothing. * @param outline an Outline object */ public void addOutline(Outline outline){ if(outline.isEmpty()){ return; } if(getLastOutline().isEmpty()){ outlines.remove(getLastOutline()); } outlines.add(outline); } /** Adds a vertex to the last open outline in the * shape. * @param v the vertex to be added to the OutlineShape */ public final void addVertex(Vertex v){ getLastOutline().addVertex(v); } /** Add a 2D {@link Vertex} to the last outline by defining the coordniate attribute * of the vertex. The 2D vertex will be represented as Z=0. * * @param x the x coordinate * @param y the y coordniate * @param onCurve flag if this vertex is on the final curve or defines a curved region * of the shape around this vertex. */ public final void addVertex(float x, float y, boolean onCurve) { getLastOutline().addVertex(vertexFactory, x, y, onCurve); } /** Add a 3D {@link Vertex} to the last outline by defining the coordniate attribute * of the vertex. * @param x the x coordinate * @param y the y coordniate * @param z the z coordniate * @param onCurve flag if this vertex is on the final curve or defines a curved region * of the shape around this vertex. */ public final void addVertex(float x, float y, float z, boolean onCurve) { getLastOutline().addVertex(vertexFactory, x, y, z, onCurve); } /** Add a vertex to the last outline by passing a float array and specifying the * offset and length in which. The attributes of the vertex are located. * The attributes should be continuous (stride = 0). * Attributes which value are not set (when length less than 3) * are set implicitly to zero. * @param coordsBuffer the coordinate array where the vertex attributes are to be picked from * @param offset the offset in the buffer to the x coordinate * @param length the number of attributes to pick from the buffer (maximum 3) * @param onCurve flag if this vertex is on the final curve or defines a curved region * of the shape around this vertex. */ public final void addVertex(float[] coordsBuffer, int offset, int length, boolean onCurve) { getLastOutline().addVertex(vertexFactory, coordsBuffer, offset, length, onCurve); } /** Closes the last outline in the shape * if last vertex is not equal to first vertex. * A new temp vertex is added at the end which * is equal to the first. */ public void closeLastOutline(){ getLastOutline().setClosed(true); } /** Get the last added outline to the list * of outlines that define the shape * @return the last outline */ public final Outline getLastOutline(){ return outlines.get(outlines.size()-1); } /** Make sure that the outlines represent * the specified destinationType, if not * transform outlines to destination type. * @param destinationType The curve type needed */ public void transformOutlines(int destinationType){ if(destinationType == QUADRATIC_NURBS){ transformOutlinesQuadratic(); } } private void transformOutlinesQuadratic(){ ArrayList<Outline> newOutlines = new ArrayList<Outline>(3); /**loop over the outlines and make sure no * adj off-curve vertices */ for(Outline outline:outlines){ Outline newOutline = new Outline(); ArrayList<Vertex> vertices = outline.getVertices(); int size =vertices.size()-1; for(int i=0;i<size;i++){ Vertex currentVertex = vertices.get(i); Vertex nextVertex = vertices.get((i+1)%size); if(!(currentVertex.isOnCurve()) && !(nextVertex.isOnCurve())) { newOutline.addVertex(currentVertex); float[] newCoords = VectorUtil.mid(currentVertex.getCoord(), nextVertex.getCoord()); newOutline.addVertex(vertexFactory, newCoords, 0, 3, true); } else { newOutline.addVertex(currentVertex); } } newOutlines.add(newOutline); } outlines = newOutlines; } private void generateVertexIds(){ int maxVertexId = 0; for(Outline outline:outlines){ ArrayList<Vertex> vertices = outline.getVertices(); for(Vertex vert:vertices){ vert.setId(maxVertexId); maxVertexId++; } } } /** @return the list of vertices associated with the * {@code Outline} list of this object */ public ArrayList<Vertex> getVertices(){ ArrayList<Vertex> vertices = new ArrayList<Vertex>(); for(Outline polyline:outlines){ vertices.addAll(polyline.getVertices()); } return vertices; } /**Triangulate the {@link OutlineShape} generating a list of triangles * @return an arraylist of triangles representing the filled region * which is produced by the combination of the outlines */ public ArrayList<Triangle> triangulate(){ if(outlines.size() == 0){ return null; } sortOutlines(); generateVertexIds(); CDTriangulator2D triangulator2d = new CDTriangulator2D(); for(int index = 0; index< outlines.size();index++){ Outline outline = outlines.get(index); triangulator2d.addCurve(outline); } ArrayList<Triangle> triangles = triangulator2d.generateTriangulation(); triangulator2d.reset(); return triangles; } /** Sort the outlines from large * to small depending on the AABox */ private void sortOutlines() { Collections.sort(outlines); Collections.reverse(outlines); } }