From d75835796900cac602f7e5789601ffba0a27efe2 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Fri, 6 May 2011 14:39:17 +0200
Subject: Graph: More std. functionality (equals, clone) / Better in-place
 transformation (cubic -> quadratic)

Impl. more of John Pritchard <jdp@syntelos.org> proposal
  https://github.com/syntelos/jogl/commit/05a7ec92d30e1e688b1eb7cc317cad83a0e8fd60

+++

More std. functionality (equals, deep clone) of AABBox, Vertex, Outline and OutlineShape.

Simplify Vertex:
  - Remove 2 component constructor
  - Add on-curve in Vertex.Factory / Constructor
  - Adding equals(Object)
  - Remove Comparable/compareTo, since we only can make an equals statement

Outline/OutlineShape: Handle dirty flag for boundary (new set/remove operation)

OutlineShape: Better in-place transformation (cubic -> quadratic)
---
 .../com/jogamp/graph/curve/OutlineShape.java       | 283 ++++++++++++++++-----
 1 file changed, 218 insertions(+), 65 deletions(-)

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

diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
index d0413b1e4..fa8494d4e 100755
--- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
@@ -30,6 +30,7 @@ package com.jogamp.graph.curve;
 import java.util.ArrayList;
 import java.util.Collections;
 
+import com.jogamp.graph.geom.AABBox;
 import com.jogamp.graph.geom.Outline;
 import com.jogamp.graph.geom.Triangle;
 import com.jogamp.graph.geom.Vertex;
@@ -91,12 +92,12 @@ import com.jogamp.graph.curve.tess.CDTriangulator2D;
  * @see Outline
  * @see Region
  */
-public class OutlineShape {
+public class OutlineShape implements Comparable<OutlineShape> {
   /**
-    * Outline has original user state (vertices) until transformed.
+    * Outline's vertices have undefined state until transformed.
     */
     public enum VerticesState {
-        ORIGINAL(0), QUADRATIC_NURBS(1);
+        UNDEFINED(0), QUADRATIC_NURBS(1);
 
         public final int state;
 
@@ -111,14 +112,19 @@ public class OutlineShape {
     /** The list of {@link Outline}s that are part of this 
      *  outline shape.
      */
-    private ArrayList<Outline> outlines = new ArrayList<Outline>(3);
+    private ArrayList<Outline> outlines;
+    private AABBox bbox;
+    private boolean dirtyBBox;    
 
     /** Create a new Outline based Shape
      */
     public OutlineShape(Vertex.Factory<? extends Vertex> factory) {
-        vertexFactory = factory;
-        outlines.add(new Outline());
-        outlineState = VerticesState.ORIGINAL;
+        this.vertexFactory = factory;
+        this.outlines = new ArrayList<Outline>(3);
+        this.outlines.add(new Outline());
+        this.outlineState = VerticesState.UNDEFINED;
+        this.bbox = new AABBox();
+        this.dirtyBBox = false;    
     }
 
     /** Returns the associated vertex factory of this outline shape
@@ -126,6 +132,10 @@ public class OutlineShape {
      */
     public final Vertex.Factory<? extends Vertex> vertexFactory() { return vertexFactory; }
 
+    public int getOutlineNumber() {
+        return outlines.size();
+    }
+    
     /** Add a new empty {@link Outline} 
      * to the shape, this new outline will
      * be placed at the end of the outline list.
@@ -137,28 +147,98 @@ public class OutlineShape {
         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
+    /** Appends the {@link Outline} element to the end,
+     * ensuring a clean tail.
+     * <p>A clean tail is ensured, ie no double empty Outlines. </p>
+     * @param outline Outline object to be added
+     * @throws NullPointerException if the  {@link Outline} element is null 
      */
-    public void addOutline(Outline outline){
-        if(outline.isEmpty()){
-            return;
+    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, ie no double empty Outlines. </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(getLastOutline().isEmpty()){
-            outlines.remove(getLastOutline());
+        if(outlines.size() == position) {
+            if(outline.isEmpty()) {
+                return;
+            }
+            if(getLastOutline().isEmpty()) {
+                outlines.set(position-1, outline);
+                if(!dirtyBBox) {
+                    bbox.resize(outline.getBounds());
+                }
+            }
+        } else {
+            outlines.add(position, outline);
+            if(!dirtyBBox) {
+                bbox.resize(outline.getBounds());
+            }
         }
-        outlines.add(outline);
     }
-
+        
+    /** 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);
+        dirtyBBox = true;
+    }
+    
+    /** 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 {
+        dirtyBBox = true;        
+        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){
-        getLastOutline().addVertex(v);
+        final Outline lo = getLastOutline();
+        lo.addVertex(v);
+        if(!dirtyBBox) {
+            bbox.resize(lo.getBounds());
+        }
     }
 
     /** Add a 2D {@link Vertex} to the last outline by defining the coordniate attribute
@@ -170,7 +250,7 @@ public class OutlineShape {
      * of the shape around this vertex.
      */
     public final void addVertex(float x, float y, boolean onCurve) {
-        getLastOutline().addVertex(vertexFactory, x, y, onCurve);
+        addVertex(vertexFactory.create(x, y, 0f, onCurve));
     }
 
     /** Add a 3D {@link Vertex} to the last outline by defining the coordniate attribute
@@ -182,7 +262,7 @@ public class OutlineShape {
      * 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);
+        addVertex(vertexFactory.create(x, y, z, onCurve));
     }
 
     /** Add a vertex to the last outline by passing a float array and specifying the 
@@ -197,7 +277,7 @@ public class OutlineShape {
      * 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);
+        addVertex(vertexFactory.create(coordsBuffer, offset, length, onCurve));
     }    
 
     /** Closes the last outline in the shape
@@ -209,62 +289,59 @@ public class OutlineShape {
         getLastOutline().setClosed(true);
     }
 
-    /** Get the last added outline to the list
-     * of outlines that define the shape
-     * @return the last outline
+    /**
+     * @return the outline's vertices state, {@link OutlineShape.VerticesState}
      */
-    public final Outline getLastOutline(){
-        return outlines.get(outlines.size()-1);
+    public final VerticesState getOutlineState() {
+        return outlineState;
     }
-    /** Make sure that the outlines represent
-     * the specified destinationType, if not
-     * transform outlines to destination type.
-     * @param destinationType TODO
+        
+    /** 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){
-                transformOutlinesQuadratic();
+                transformOutlines2Quadratic();
             } else {
                 throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")");
             }
         }
     }
 
-    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);
-                }
+    private void transformOutlines2Quadratic(){
+        final int count = getOutlineNumber();
+        for (int cc = 0; cc < count; cc++){            
+            final Outline outline = getOutline(cc);
+            int vertexNumberLessOne = outline.getVertexNumber() - 1;
+            for(int i=0; i < vertexNumberLessOne; i++) {
+                final Vertex currentVertex = outline.getVertex(i);
+                final Vertex nextVertex = outline.getVertex(i+1);
+                if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) {
+                    final float[] newCoords = VectorUtil.mid(currentVertex.getCoord(), nextVertex.getCoord());
+                    final Vertex v = vertexFactory.create(newCoords, 0, 3, true);
+                    v.setOnCurve(true);                    
+                    i++;
+                    vertexNumberLessOne++;
+                    outline.addVertex(i, v);
+                }                
             }
-            newOutlines.add(newOutline);
+            // Cut off last vertex (which is on-curve)
+            // FIXME: original code skipped the last element (it _is_ unrelated to the xform above)
+            // FIXME: understand why the last element produces artifacts in rendering
+            if( vertexNumberLessOne >= 0 ) {
+                outline.removeVertex(vertexNumberLessOne);
+            }                
         }
-        outlines = newOutlines;
         outlineState = VerticesState.QUADRATIC_NURBS;
     }
 
     private void generateVertexIds(){
         int maxVertexId = 0;
-        for(Outline outline:outlines){
-            ArrayList<Vertex> vertices = outline.getVertices();
+        for(int i=0; i<outlines.size(); i++) {
+            final ArrayList<Vertex> vertices = outlines.get(i).getVertices();
             for(Vertex vert:vertices){
                 vert.setId(maxVertexId);
                 maxVertexId++;
@@ -272,18 +349,17 @@ public class OutlineShape {
         }
     }
 
-    /** @return the list of vertices associated with the 
-     * {@code Outline} list of this object
+    /** @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(Outline polyline:outlines){
-            vertices.addAll(polyline.getVertices());
+        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
@@ -314,4 +390,81 @@ public class OutlineShape {
         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() {
+        dirtyBBox = false;
+        bbox.reset();
+        for (int i=0; i<outlines.size(); i++) {
+            bbox.resize(outlines.get(i).getBounds());
+        }
+    }
+         
+    public final AABBox getBounds() {
+        if (dirtyBBox) {
+            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
+     */
+    public OutlineShape clone() {
+        OutlineShape o;
+        try {
+            o = (OutlineShape) super.clone();
+        } catch (CloneNotSupportedException e) { return null; /* never, ever */ }
+        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