/**
* 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.curve.tess.Triangulation;
import com.jogamp.graph.curve.tess.Triangulator;
import com.jogamp.graph.geom.AABBox;
import com.jogamp.graph.geom.Outline;
import com.jogamp.graph.geom.Triangle;
import com.jogamp.graph.geom.Vertex;
import com.jogamp.graph.math.VectorUtil;
/** 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.
*
*
* Example to creating an Outline Shape:
*
addVertex(...) addVertex(...) addVertex(...) addEmptyOutline() addVertex(...) addVertex(...) addVertex(...) ** * 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. *
addVertex(0,0, true); addVertex(0,1, false); addVertex(1,1, false); addVertex(1,0, true); ** * The above snippet defines a cubic nurbs curve where (0,1 and 1,1) * do not belong to the final rendered shape. * * Implementation Notes:
If the {@link #getLastOutline()} is empty already, no new one will be added.
* * After a call to this function all new vertices added * will belong to the new outline */ public void addEmptyOutline() { if( !getLastOutline().isEmpty() ) { outlines.add(new Outline()); } } /** Appends the {@link Outline} element to the end, * ensuring a clean tail. * *A clean tail is ensured, no double empty Outlines are produced * and a pre-existing empty outline will be replaced with the given one.
* * @param outline Outline object to be added * @throws NullPointerException if the {@link Outline} element is null */ public void addOutline(Outline outline) throws NullPointerException { addOutline(outlines.size(), outline); } /** Insert the {@link Outline} element at the given {@code position}. * *If the {@code position} indicates the end of this list, * a clean tail is ensured, no double empty Outlines are produced * and a pre-existing empty outline will be replaced with the given one.
* * @param position of the added Outline * @param outline Outline object to be added * @throws NullPointerException if the {@link Outline} element is null * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber()) */ public void addOutline(int position, Outline outline) throws NullPointerException, IndexOutOfBoundsException { if (null == outline) { throw new NullPointerException("outline is null"); } if( outlines.size() == position ) { final Outline lastOutline = getLastOutline(); if( outline.isEmpty() && lastOutline.isEmpty() ) { return; } if( lastOutline.isEmpty() ) { outlines.set(position-1, outline); if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { bbox.resize(outline.getBounds()); } return; } } outlines.add(position, outline); if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { bbox.resize(outline.getBounds()); } } /** Insert the {@link OutlineShape} elements of type {@link Outline}, .. at the end of this shape, * using {@link #addOutline(Outline)} for each element. *Closes the current last outline via {@link #closeLastOutline()} before adding the new ones.
* @param outlineShape OutlineShape elements to be added. * @throws NullPointerException if the {@link OutlineShape} is null * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber()) */ public void addOutlineShape(OutlineShape outlineShape) throws NullPointerException { if (null == outlineShape) { throw new NullPointerException("OutlineShape is null"); } closeLastOutline(); for(int i=0; iSets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.
* * @param position of the to be removed Outline * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber()) */ public final Outline removeOutline(int position) throws IndexOutOfBoundsException { dirtyBits |= DIRTY_BOUNDS; return outlines.remove(position); } /** 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); } /** @return the {@code Outline} at {@code position} * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber()) */ public Outline getOutline(int position) throws IndexOutOfBoundsException { return outlines.get(position); } /** 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) { final Outline lo = getLastOutline(); lo.addVertex(v); if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { bbox.resize(lo.getBounds()); } } /** Adds a vertex to the last open outline in the shape. * at {@code position} * @param position indx at which the vertex will be added * @param v the vertex to be added to the OutlineShape */ public final void addVertex(int position, Vertex v) { final Outline lo = getLastOutline(); lo.addVertex(position, v); if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { bbox.resize(lo.getBounds()); } } /** 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) { addVertex(vertexFactory.create(x, y, 0f, 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 coordinate * @param z the z coordinate * @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) { addVertex(vertexFactory.create(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) { addVertex(vertexFactory.create(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); } /** * @return the outline's vertices state, {@link OutlineShape.VerticesState} */ public final VerticesState getOutlineState() { return outlineState; } /** Ensure the outlines represent * the specified destinationType. * and removes all overlaps in boundary triangles * @param destinationType the target outline's vertices state. Currently only * {@link OutlineShape.VerticesState#QUADRATIC_NURBS} are supported. */ public void transformOutlines(VerticesState destinationType) { if(outlineState != destinationType){ if(destinationType == VerticesState.QUADRATIC_NURBS){ transformOutlines2Quadratic(); checkOverlaps(); } else { throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")"); } } } private void subdivideTriangle(final Outline outline, Vertex a, Vertex b, Vertex c, int index){ float[] v1 = VectorUtil.mid(a.getCoord(), b.getCoord()); float[] v3 = VectorUtil.mid(b.getCoord(), c.getCoord()); float[] v2 = VectorUtil.mid(v1, v3); //drop off-curve vertex to image on the curve b.setCoord(v2, 0, 3); b.setOnCurve(true); outline.addVertex(index, vertexFactory.create(v1, 0, 3, false)); outline.addVertex(index+2, vertexFactory.create(v3, 0, 3, false)); } /** Check overlaps between curved triangles * first check if any vertex in triangle a is in triangle b * second check if edges of triangle a intersect segments of triangle b * if any of the two tests is true we divide current triangle * and add the other to the list of overlaps * * Loop until overlap array is empty. (check only in first pass) */ private void checkOverlaps() { ArrayList