From 83e82c3f72901a062cd2e73f4fc53353addcb337 Mon Sep 17 00:00:00 2001 From: Rami Santina 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 +++++++++++---------- .../classes/com/jogamp/graph/math/VectorUtil.java | 30 + 2 files changed, 520 insertions(+), 435 deletions(-) (limited to 'src/jogl/classes/com') 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 { - /** - * 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 vertexFactory; - private VerticesState outlineState; - - /** The list of {@link Outline}s that are part of this - * outline shape. - */ - private ArrayList outlines; - private AABBox bbox; - - /** dirty bits DIRTY_BOUNDS */ - private int dirtyBits; - - /** Create a new Outline based Shape - */ - public OutlineShape(Vertex.Factory factory) { - this.vertexFactory = factory; - this.outlines = new ArrayList(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 vertexFactory() { return vertexFactory; } - - public int getOutlineNumber() { - return outlines.size(); - } - - /** Add a new empty {@link Outline} - * to the end of this shape's outline list. - *

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 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}. - *

Sets 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. - * - * @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 vertices = outlines.get(i).getVertices(); - for(int pos=0; pos getVertices() { - ArrayList vertices = new ArrayList(); - for(int i=0; i triangulate() { - if(outlines.size() == 0){ - return null; - } - sortOutlines(); - generateVertexIds(); - - Triangulator triangulator2d = Triangulation.create(); - for(int index = 0; index 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=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(outlines.size()); - for(int i=0; i vertexFactory; + private VerticesState outlineState; + + /** The list of {@link Outline}s that are part of this + * outline shape. + */ + private ArrayList outlines; + private AABBox bbox; + + /** dirty bits DIRTY_BOUNDS */ + private int dirtyBits; + + /** Create a new Outline based Shape + */ + public OutlineShape(Vertex.Factory factory) { + this.vertexFactory = factory; + this.outlines = new ArrayList(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 vertexFactory() { return vertexFactory; } + + public int getOutlineNumber() { + return outlines.size(); + } + + /** Add a new empty {@link Outline} + * to the end of this shape's outline list. + *

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 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}. + *

Sets 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. + * + * @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 vertices = outlines.get(i).getVertices(); + for(int pos=0; pos getVertices() { + ArrayList vertices = new ArrayList(); + for(int i=0; i triangulate() { + if(outlines.size() == 0){ + return null; + } + sortOutlines(); + generateVertexIds(); + + Triangulator triangulator2d = Triangulation.create(); + for(int index = 0; index 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=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(outlines.size()); + for(int i=0; i 0) && (v > 0) && (u + v < 1); + } /** Check if points are in ccw order * @param a first vertex -- cgit v1.2.3