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