From 83e82c3f72901a062cd2e73f4fc53353addcb337 Mon Sep 17 00:00:00 2001
From: Rami Santina <rami.santina@gmail.com>
Date: Tue, 14 Jun 2011 11:10:27 +0300
Subject: Added handling for offcurve triangle overlaps.

subdivde overlaping triangles for the case of vertex inside
a off-curve boundary triangle.

added vertex in triangle test (using barycentric coordinates)
---
 .../com/jogamp/graph/curve/OutlineShape.java       | 925 +++++++++++----------
 1 file changed, 490 insertions(+), 435 deletions(-)

(limited to 'src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java')

diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
index e4f80ab76..7d0aa0a18 100755
--- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
@@ -93,439 +93,494 @@ import com.jogamp.graph.math.VectorUtil;
  * @see Region
  */
 public class OutlineShape implements Comparable<OutlineShape> {
-  /**
-    * Outline's vertices have undefined state until transformed.
-    */
-    public enum VerticesState {
-        UNDEFINED(0), QUADRATIC_NURBS(1);
-
-        public final int state;
-
-        VerticesState(int state){
-            this.state = state;
-        }
-    } 
-    
-    public static final int DIRTY_BOUNDS = 1 << 0;
-    
-    private final Vertex.Factory<? extends Vertex> vertexFactory;
-    private VerticesState outlineState;
-
-    /** The list of {@link Outline}s that are part of this 
-     *  outline shape.
-     */
-    private ArrayList<Outline> outlines;
-    private AABBox bbox;
-
-    /** dirty bits DIRTY_BOUNDS */
-    private int dirtyBits;  
-    
-    /** Create a new Outline based Shape
-     */
-    public OutlineShape(Vertex.Factory<? extends Vertex> factory) {
-        this.vertexFactory = factory;
-        this.outlines = new ArrayList<Outline>(3);
-        this.outlines.add(new Outline());
-        this.outlineState = VerticesState.UNDEFINED;
-        this.bbox = new AABBox();
-        this.dirtyBits = 0;    
-    }
-
-    /** Clears all data and reset all states as if this instance was newly created */
-    public void clear() {
-        outlines.clear();
-        outlines.add(new Outline());
-        outlineState = VerticesState.UNDEFINED;
-        bbox.reset();
-        dirtyBits = 0;    
-    }
-    
-    /** Returns the associated vertex factory of this outline shape
-     * @return Vertex.Factory object
-     */
-    public final Vertex.Factory<? extends Vertex> vertexFactory() { return vertexFactory; }
-
-    public int getOutlineNumber() {
-        return outlines.size();
-    }
-    
-    /** Add a new empty {@link Outline} 
-     * to the end of this shape's outline list.
-     * <p>If the {@link #getLastOutline()} is empty already, no new one will be added.</p>
-     * 
-     * 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.
-     * 
-     * <p>A clean tail is ensured, no double empty Outlines are produced
-     * and a pre-existing empty outline will be replaced with the given one. </p>
-     * 
-     * @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}.
-     * 
-     * <p>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. </p>
-     * 
-     * @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.
-     * <p>Closes the current last outline via {@link #closeLastOutline()} before adding the new ones.</p>
-     * @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; i<outlineShape.getOutlineNumber(); i++) {
-            addOutline(outlineShape.getOutline(i));
-        }
-    }
-        
-    /** Replaces the {@link Outline} element at the given {@code position}.
-     * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
-     * 
-     * @param position of the replaced Outline
-     * @param outline replacement Outline object 
-     * @throws NullPointerException if the  {@link Outline} element is null 
-     * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber())
-     */
-    public void setOutline(int position, Outline outline) throws NullPointerException, IndexOutOfBoundsException {
-        if (null == outline) {
-            throw new NullPointerException("outline is null");
-        }
-        outlines.set(position, outline);
-        dirtyBits |= DIRTY_BOUNDS;
-    }
-    
-    /** Removes the {@link Outline} element at the given {@code position}.
-     * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
-     * 
-     * @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.
-     * <p>If last vertex is not equal to first vertex.
-     * A new temp vertex is added at the end which 
-     * is equal to the first.</p>
-     */
-    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.
-     * 
-     * @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();
-            } else {
-                throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")");
-            }
-        }
-    }
-
-    private void transformOutlines2Quadratic() {
-        int count = getOutlineNumber();
-        for (int cc = 0; cc < count; cc++) {            
-            final Outline outline = getOutline(cc);
-            int vertexCount = outline.getVertexCount();
-            
-            for(int i=0; i < vertexCount; i++) {
-                final Vertex currentVertex = outline.getVertex(i);
-                final Vertex nextVertex = outline.getVertex((i+1)%vertexCount);
-                if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) {
-                    final float[] newCoords = VectorUtil.mid(currentVertex.getCoord(), 
-                            nextVertex.getCoord());
-                    final Vertex v = vertexFactory.create(newCoords, 0, 3, true);
-                    i++;
-                    vertexCount++;
-                    outline.addVertex(i, v);
-                }                
-            }
-            if(vertexCount <= 0) {
-                outlines.remove(outline);
-                cc--;
-                count--;
-                continue;
-            }
-            
-            if( vertexCount > 0 ) {
-                if(VectorUtil.checkEquality(outline.getVertex(0).getCoord(), 
-                        outline.getLastVertex().getCoord())) {
-                    outline.removeVertex(vertexCount-1);
-                }
-            }
-        }
-        outlineState = VerticesState.QUADRATIC_NURBS;
-    }
-
-    private void generateVertexIds() {
-        int maxVertexId = 0;
-        for(int i=0; i<outlines.size(); i++) {
-            final ArrayList<Vertex> vertices = outlines.get(i).getVertices();
-            for(int pos=0; pos<vertices.size(); pos++) {
-                Vertex vert = vertices.get(pos);
-                vert.setId(maxVertexId);
-                maxVertexId++;
-            }
-        }
-    }
-
-    /** @return the list of concatenated vertices associated with all 
-     * {@code Outline}s of this object
-     */
-    public ArrayList<Vertex> getVertices() {
-        ArrayList<Vertex> vertices = new ArrayList<Vertex>();
-        for(int i=0; i<outlines.size(); i++) {
-            vertices.addAll(outlines.get(i).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();
-
-        Triangulator triangulator2d = Triangulation.create();
-        for(int index = 0; index<outlines.size(); index++) {
-            triangulator2d.addCurve(outlines.get(index));
-        }
-        
-        ArrayList<Triangle> triangles = triangulator2d.generate();
-        triangulator2d.reset();
-
-        return triangles;
-    }
-
-    /** Sort the outlines from large
-     *  to small depending on the AABox
-     */
-    private void sortOutlines() {
-        Collections.sort(outlines);
-        Collections.reverse(outlines);
-    }
-    
-    /** Compare two outline shapes with Bounding Box area
-     * as criteria. 
-     * @see java.lang.Comparable#compareTo(java.lang.Object)
-     */
-    public final int compareTo(OutlineShape outline) {
-        float size = getBounds().getSize();
-        float newSize = outline.getBounds().getSize();
-        if(size < newSize){
-            return -1;
-        }
-        else if(size > newSize){
-            return 1;
-        }
-        return 0;
-    }
-    
-    private final void validateBoundingBox() {
-        dirtyBits &= ~DIRTY_BOUNDS;
-        bbox.reset();
-        for (int i=0; i<outlines.size(); i++) {
-            bbox.resize(outlines.get(i).getBounds());
-        }
-    }
-         
-    public final AABBox getBounds() {
-        if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
-            validateBoundingBox();
-        }
-        return bbox;
-    }    
-
-    /**
-     * @param obj the Object to compare this OutlineShape with
-     * @return true if {@code obj} is an OutlineShape, not null, 
-     *                 same outlineState, equal bounds and equal outlines in the same order 
-     */
-    public boolean equals(Object obj) {
-        if( obj == this) {
-            return true;
-        }
-        if( null == obj || !(obj instanceof OutlineShape) ) {
-            return false;
-        }        
-        final OutlineShape o = (OutlineShape) obj;
-        if(getOutlineState() != o.getOutlineState()) {
-            return false;
-        }
-        if(getOutlineNumber() != o.getOutlineNumber()) {
-            return false;
-        }
-        if( !getBounds().equals( o.getBounds() ) ) {
-            return false;
-        }
-        for (int i=getOutlineNumber()-1; i>=0; i--) {
-            if( ! getOutline(i).equals( o.getOutline(i) ) ) {
-                return false;
-            }
-        }
-        return true;
-    }
-    
-    /**
-     * @return deep clone of this OutlineShape w/o Region
-     */
-    public OutlineShape clone() {
-        OutlineShape o;
-        try {
-            o = (OutlineShape) super.clone();
-        } catch (CloneNotSupportedException e) { throw new InternalError(); }
-        o.bbox = bbox.clone();
-        o.outlines = new ArrayList<Outline>(outlines.size());
-        for(int i=0; i<outlines.size(); i++) {
-            o.outlines.add(outlines.get(i).clone());
-        }
-        return o;
-    }                
+	/**
+	 * Outline's vertices have undefined state until transformed.
+	 */
+	public enum VerticesState {
+		UNDEFINED(0), QUADRATIC_NURBS(1);
+
+		public final int state;
+
+		VerticesState(int state){
+			this.state = state;
+		}
+	} 
+
+	public static final int DIRTY_BOUNDS = 1 << 0;
+
+	private final Vertex.Factory<? extends Vertex> vertexFactory;
+	private VerticesState outlineState;
+
+	/** The list of {@link Outline}s that are part of this 
+	 *  outline shape.
+	 */
+	private ArrayList<Outline> outlines;
+	private AABBox bbox;
+
+	/** dirty bits DIRTY_BOUNDS */
+	private int dirtyBits;  
+
+	/** Create a new Outline based Shape
+	 */
+	public OutlineShape(Vertex.Factory<? extends Vertex> factory) {
+		this.vertexFactory = factory;
+		this.outlines = new ArrayList<Outline>(3);
+		this.outlines.add(new Outline());
+		this.outlineState = VerticesState.UNDEFINED;
+		this.bbox = new AABBox();
+		this.dirtyBits = 0;    
+	}
+
+	/** Clears all data and reset all states as if this instance was newly created */
+	public void clear() {
+		outlines.clear();
+		outlines.add(new Outline());
+		outlineState = VerticesState.UNDEFINED;
+		bbox.reset();
+		dirtyBits = 0;    
+	}
+
+	/** Returns the associated vertex factory of this outline shape
+	 * @return Vertex.Factory object
+	 */
+	public final Vertex.Factory<? extends Vertex> vertexFactory() { return vertexFactory; }
+
+	public int getOutlineNumber() {
+		return outlines.size();
+	}
+
+	/** Add a new empty {@link Outline} 
+	 * to the end of this shape's outline list.
+	 * <p>If the {@link #getLastOutline()} is empty already, no new one will be added.</p>
+	 * 
+	 * 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.
+	 * 
+	 * <p>A clean tail is ensured, no double empty Outlines are produced
+	 * and a pre-existing empty outline will be replaced with the given one. </p>
+	 * 
+	 * @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}.
+	 * 
+	 * <p>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. </p>
+	 * 
+	 * @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.
+	 * <p>Closes the current last outline via {@link #closeLastOutline()} before adding the new ones.</p>
+	 * @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; i<outlineShape.getOutlineNumber(); i++) {
+			addOutline(outlineShape.getOutline(i));
+		}
+	}
+
+	/** Replaces the {@link Outline} element at the given {@code position}.
+	 * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
+	 * 
+	 * @param position of the replaced Outline
+	 * @param outline replacement Outline object 
+	 * @throws NullPointerException if the  {@link Outline} element is null 
+	 * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber())
+	 */
+	public void setOutline(int position, Outline outline) throws NullPointerException, IndexOutOfBoundsException {
+		if (null == outline) {
+			throw new NullPointerException("outline is null");
+		}
+		outlines.set(position, outline);
+		dirtyBits |= DIRTY_BOUNDS;
+	}
+
+	/** Removes the {@link Outline} element at the given {@code position}.
+	 * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p>
+	 * 
+	 * @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.
+	 * <p>If last vertex is not equal to first vertex.
+	 * A new temp vertex is added at the end which 
+	 * is equal to the first.</p>
+	 */
+	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.
+	 * 
+	 * @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 boolean checkOverlaps() {
+		boolean edited = false;
+		int count = getOutlineNumber();
+		for (int cc = 0; cc < count; cc++) { 
+			final Outline outline = getOutline(cc);
+			int vertexCount = outline.getVertexCount();
+			for(int i=0; i < vertexCount; i++) {
+				final Vertex currentVertex = outline.getVertex(i);
+				if ( !currentVertex.isOnCurve()) {
+					final Vertex nextV = outline.getVertex((i+1)%vertexCount);
+					final Vertex prevV = outline.getVertex((i-1)%vertexCount);
+					if(checkTriOverlaps(prevV, currentVertex, nextV)) {
+						//subdivide triangle into two
+						float[] v1 = VectorUtil.mid(prevV.getCoord(), currentVertex.getCoord());
+						float[] v3 = VectorUtil.mid(currentVertex.getCoord(), nextV.getCoord());
+						float[] v2 = VectorUtil.mid(v1, v3);
+
+						//drop off-curve vertex to image on the curve
+						currentVertex.setCoord(v2, 0, 3); 
+						currentVertex.setOnCurve(true);
+
+						outline.addVertex(i, vertexFactory.create(v1, 0, 3, false));
+						i+=2;
+						vertexCount++;
+						outline.addVertex(i, vertexFactory.create(v3, 0, 3, false));
+						vertexCount++;
+						i++;
+
+						edited = true;
+					}
+				}
+			}
+		}
+		return edited;
+	}
+
+	private boolean checkTriOverlaps(Vertex a, Vertex b, Vertex c) {
+		int count = getOutlineNumber();
+		for (int cc = 0; cc < count; cc++) { 
+			final Outline outline = getOutline(cc);
+			int vertexCount = outline.getVertexCount();
+			for(int i=0; i < vertexCount; i++) {
+				final Vertex current = outline.getVertex(i);
+				if(current == a || current == b || current == c){
+					continue;
+				}
+				if(VectorUtil.vertexInTriangle(a.getCoord(), b.getCoord(), c.getCoord(), current.getCoord())){
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	private void transformOutlines2Quadratic() {
+		int count = getOutlineNumber();
+		for (int cc = 0; cc < count; cc++) {            
+			final Outline outline = getOutline(cc);
+			int vertexCount = outline.getVertexCount();
+
+			for(int i=0; i < vertexCount; i++) {
+				final Vertex currentVertex = outline.getVertex(i);
+				final Vertex nextVertex = outline.getVertex((i+1)%vertexCount);
+				if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) {
+					final float[] newCoords = VectorUtil.mid(currentVertex.getCoord(), 
+							nextVertex.getCoord());
+					final Vertex v = vertexFactory.create(newCoords, 0, 3, true);
+					i++;
+					vertexCount++;
+					outline.addVertex(i, v);
+				}                
+			}
+			if(vertexCount <= 0) {
+				outlines.remove(outline);
+				cc--;
+				count--;
+				continue;
+			}
+
+			if( vertexCount > 0 ) {
+				if(VectorUtil.checkEquality(outline.getVertex(0).getCoord(), 
+						outline.getLastVertex().getCoord())) {
+					outline.removeVertex(vertexCount-1);
+				}
+			}
+		}
+		outlineState = VerticesState.QUADRATIC_NURBS;
+	}
+
+	private void generateVertexIds() {
+		int maxVertexId = 0;
+		for(int i=0; i<outlines.size(); i++) {
+			final ArrayList<Vertex> vertices = outlines.get(i).getVertices();
+			for(int pos=0; pos<vertices.size(); pos++) {
+				Vertex vert = vertices.get(pos);
+				vert.setId(maxVertexId);
+				maxVertexId++;
+			}
+		}
+	}
+
+	/** @return the list of concatenated vertices associated with all 
+	 * {@code Outline}s of this object
+	 */
+	public ArrayList<Vertex> getVertices() {
+		ArrayList<Vertex> vertices = new ArrayList<Vertex>();
+		for(int i=0; i<outlines.size(); i++) {
+			vertices.addAll(outlines.get(i).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();
+
+		Triangulator triangulator2d = Triangulation.create();
+		for(int index = 0; index<outlines.size(); index++) {
+			triangulator2d.addCurve(outlines.get(index));
+		}
+
+		ArrayList<Triangle> triangles = triangulator2d.generate();
+		triangulator2d.reset();
+
+		return triangles;
+	}
+
+	/** Sort the outlines from large
+	 *  to small depending on the AABox
+	 */
+	private void sortOutlines() {
+		Collections.sort(outlines);
+		Collections.reverse(outlines);
+	}
+
+	/** Compare two outline shapes with Bounding Box area
+	 * as criteria. 
+	 * @see java.lang.Comparable#compareTo(java.lang.Object)
+	 */
+	public final int compareTo(OutlineShape outline) {
+		float size = getBounds().getSize();
+		float newSize = outline.getBounds().getSize();
+		if(size < newSize){
+			return -1;
+		}
+		else if(size > newSize){
+			return 1;
+		}
+		return 0;
+	}
+
+	private final void validateBoundingBox() {
+		dirtyBits &= ~DIRTY_BOUNDS;
+		bbox.reset();
+		for (int i=0; i<outlines.size(); i++) {
+			bbox.resize(outlines.get(i).getBounds());
+		}
+	}
+
+	public final AABBox getBounds() {
+		if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) {
+			validateBoundingBox();
+		}
+		return bbox;
+	}    
+
+	/**
+	 * @param obj the Object to compare this OutlineShape with
+	 * @return true if {@code obj} is an OutlineShape, not null, 
+	 *                 same outlineState, equal bounds and equal outlines in the same order 
+	 */
+	public boolean equals(Object obj) {
+		if( obj == this) {
+			return true;
+		}
+		if( null == obj || !(obj instanceof OutlineShape) ) {
+			return false;
+		}        
+		final OutlineShape o = (OutlineShape) obj;
+		if(getOutlineState() != o.getOutlineState()) {
+			return false;
+		}
+		if(getOutlineNumber() != o.getOutlineNumber()) {
+			return false;
+		}
+		if( !getBounds().equals( o.getBounds() ) ) {
+			return false;
+		}
+		for (int i=getOutlineNumber()-1; i>=0; i--) {
+			if( ! getOutline(i).equals( o.getOutline(i) ) ) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * @return deep clone of this OutlineShape w/o Region
+	 */
+	public OutlineShape clone() {
+		OutlineShape o;
+		try {
+			o = (OutlineShape) super.clone();
+		} catch (CloneNotSupportedException e) { throw new InternalError(); }
+		o.bbox = bbox.clone();
+		o.outlines = new ArrayList<Outline>(outlines.size());
+		for(int i=0; i<outlines.size(); i++) {
+			o.outlines.add(outlines.get(i).clone());
+		}
+		return o;
+	}                
 }
-- 
cgit v1.2.3