From d75835796900cac602f7e5789601ffba0a27efe2 Mon Sep 17 00:00:00 2001 From: Sven Gothel 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 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 ++++++++++++++++----- src/jogl/classes/com/jogamp/graph/geom/AABBox.java | 66 +++-- .../classes/com/jogamp/graph/geom/Outline.java | 189 +++++++++----- src/jogl/classes/com/jogamp/graph/geom/Vertex.java | 29 ++- .../com/jogamp/graph/geom/opengl/SVertex.java | 116 ++++----- .../classes/com/jogamp/graph/math/VectorUtil.java | 19 +- 6 files changed, 480 insertions(+), 222 deletions(-) (limited to 'src/jogl/classes/com/jogamp/graph') 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 { /** - * 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 outlines = new ArrayList(3); + private ArrayList outlines; + private AABBox bbox; + private boolean dirtyBBox; /** Create a new Outline based Shape */ public OutlineShape(Vertex.Factory factory) { - vertexFactory = factory; - outlines.add(new Outline()); - outlineState = VerticesState.ORIGINAL; + this.vertexFactory = factory; + this.outlines = new ArrayList(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 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. + *

A clean tail is ensured, ie no double empty Outlines.

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

If the {@code position} indicates the end of this list, + * a clean tail is ensured, ie no double empty Outlines.

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

Sets 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); + dirtyBBox = true; + } + + /** 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 { + 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 newOutlines = new ArrayList(3); - - /**loop over the outlines and make sure no - * adj off-curve vertices - */ - for(Outline outline:outlines){ - Outline newOutline = new Outline(); - - ArrayList vertices = outline.getVertices(); - int size =vertices.size()-1; - for(int i=0;i= 0 ) { + outline.removeVertex(vertexNumberLessOne); + } } - outlines = newOutlines; outlineState = VerticesState.QUADRATIC_NURBS; } private void generateVertexIds(){ int maxVertexId = 0; - for(Outline outline:outlines){ - ArrayList vertices = outline.getVertices(); + for(int i=0; i 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 getVertices(){ ArrayList vertices = new ArrayList(); - for(Outline polyline:outlines){ - vertices.addAll(polyline.getVertices()); + for(int i=0; i newSize){ + return 1; + } + return 0; + } + + private final void validateBoundingBox() { + dirtyBBox = false; + 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 + */ + 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(outlines.size()); + for(int i=0; ihigh[0]){ return false; } @@ -196,7 +205,7 @@ public class AABBox { * @return true if x belong to (low.x, high.x) and * y belong to (low.y, high.y) and z belong to (low.z, high.z) */ - public boolean contains(float x, float y, float z) { + public final boolean contains(float x, float y, float z) { if(xhigh[0]){ return false; } @@ -217,7 +226,7 @@ public class AABBox { * @param h hight * @return true if this AABBox might have a common region with this 2D region */ - public boolean intersects(float x, float y, float w, float h) { + public final boolean intersects(float x, float y, float w, float h) { if (w <= 0 || h <= 0) { return false; } @@ -241,21 +250,21 @@ public class AABBox { * length of the vector between low and high. * @return a float representing the size of the AABBox */ - public float getSize() { + public final float getSize() { return VectorUtil.computeLength(low, high); } /**Get the Center of the AABBox * @return the xyz-coordinates of the center of the AABBox */ - public float[] getCenter() { + public final float[] getCenter() { return center; } /** Scale the AABBox by a constant * @param size a constant float value */ - public void scale(float size) { + public final void scale(float size) { float[] diffH = new float[3]; diffH[0] = high[0] - center[0]; diffH[1] = high[1] - center[1]; @@ -274,30 +283,43 @@ public class AABBox { low = VectorUtil.vectorAdd(center, diffL); } - public float getMinX() { + public final float getMinX() { return low[0]; } - public float getMinY() { + public final float getMinY() { return low[1]; } - public float getWidth(){ + public final float getWidth(){ return high[0] - low[0]; } - public float getHeight() { + public final float getHeight() { return high[1] - low[1]; } - public float getDepth() { + public final float getDepth() { return high[2] - low[2]; } - public AABBox clone() { + + public final AABBox clone() { return new AABBox(this.low, this.high); } - public String toString() { + public final boolean equals(Object obj) { + if( obj == this ) { + return true; + } + if( null == obj || !(obj instanceof AABBox) ) { + return false; + } + final AABBox other = (AABBox) obj; + return VectorUtil.checkEquality(low, other.low) && + VectorUtil.checkEquality(high, other.high) ; + } + + public final String toString() { return "[ "+low[0]+"/"+low[1]+"/"+low[1]+" .. "+high[0]+"/"+high[0]+"/"+high[0]+", ctr "+ center[0]+"/"+center[1]+"/"+center[1]+" ]"; } diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 315be002f..e0c29cb84 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -43,110 +43,116 @@ import com.jogamp.graph.math.VectorUtil; * * @see OutlineShape, Region */ -public class Outline implements Comparable { +public class Outline implements Cloneable, Comparable { private ArrayList vertices = new ArrayList(3); private boolean closed = false; - private AABBox box = new AABBox(); + private AABBox bbox = new AABBox(); + private boolean dirtyBBox = false; /**Create an outline defined by control vertices. * An outline can contain off Curve vertices which define curved * regions in the outline. */ - public Outline(){ + public Outline() { + } + public final int getVertexNumber() { + return vertices.size(); } - /** Add a vertex to the outline. The {@link Vertex} is added at the - * end of the outline loop/strip. + /** Appends a vertex to the outline loop/strip. * @param vertex Vertex to be added + * @throws NullPointerException if the {@link Vertex} element is null */ - public final void addVertex(Vertex vertex) { - vertices.add(vertex); - box.resize(vertex.getX(), vertex.getY(), vertex.getZ()); + public final void addVertex(Vertex vertex) throws NullPointerException { + addVertex(vertices.size(), vertex); } - /** Add a {@link Vertex} by specifying its 2D attributes to the outline. - * The {@link Vertex} is added at the - * end of the outline loop/strip. - * @param factory a {@link Factory} to get the required Vertex impl - * @param x the x coordinate - * @param y the y coordinate - * @param onCurve flag if this vertex is on the final curve or defines a curved region - * of the shape around this vertex. + /** Insert the {@link Vertex} element at the given {@code position} to the outline loop/strip. + * @param position of the added Vertex + * @param vertex Vertex object to be added + * @throws NullPointerException if the {@link Vertex} element is null + * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getVertexNumber()) */ - public final void addVertex(Vertex.Factory factory, float x, float y, boolean onCurve) { - addVertex(factory, x, y, 0f, onCurve); + public final void addVertex(int position, Vertex vertex) throws NullPointerException, IndexOutOfBoundsException { + if (null == vertex) { + throw new NullPointerException("vertex is null"); + } + vertices.add(position, vertex); + if(!dirtyBBox) { + bbox.resize(vertex.getX(), vertex.getY(), vertex.getZ()); + } } - - /** Add a {@link Vertex} by specifying its 3D attributes to the outline. - * The {@link Vertex} is added at the - * end of the outline loop/strip. - * @param factory a {@link Factory} to get the required Vertex impl - * @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. + + /** Replaces the {@link Vertex} 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 replaced Vertex + * @param vertex replacement Vertex object + * @throws NullPointerException if the {@link Outline} element is null + * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getVertexNumber()) */ - public final void addVertex(Vertex.Factory factory, float x, float y, float z, boolean onCurve) { - Vertex v = factory.create(x, y, z); - v.setOnCurve(onCurve); - addVertex(v); + public final void setVertex(int position, Vertex vertex) throws NullPointerException, IndexOutOfBoundsException { + if (null == vertex) { + throw new NullPointerException("vertex is null"); + } + vertices.set(position, vertex); + dirtyBBox = true; } - /** Add a vertex to the 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 factory a {@link Factory} to get the required Vertex impl - * @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(Vertex.Factory factory, float[] coordsBuffer, int offset, int length, boolean onCurve) { - Vertex v = factory.create(coordsBuffer, offset, length); - v.setOnCurve(onCurve); - addVertex(v); + public final Vertex getVertex(int index){ + return vertices.get(index); } - public Vertex getVertex(int index){ - return vertices.get(index); + /** Removes the {@link Vertex} 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 Vertex + * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getVertexNumber()) + */ + public final Vertex removeVertex(int position) throws IndexOutOfBoundsException { + dirtyBBox = true; + return vertices.remove(position); } - public boolean isEmpty(){ + public final boolean isEmpty(){ return (vertices.size() == 0); } - public Vertex getLastVertex(){ + + public final Vertex getLastVertex(){ if(isEmpty()){ return null; } return vertices.get(vertices.size()-1); } - public ArrayList getVertices() { + public final ArrayList getVertices() { return vertices; } - public void setVertices(ArrayList vertices) { + + /** + * Use the given outline loop/strip. + *

Validates the bounding box.

+ * + * @param vertices the new outline loop/strip + */ + public final void setVertices(ArrayList vertices) { this.vertices = vertices; + validateBoundingBox(); } - public AABBox getBox() { - return box; - } - public boolean isClosed() { + + public final boolean isClosed() { return closed; } - + /** define if this outline is closed or not. * if set to closed, checks if the last vertex is * equal to the first vertex. If not Equal adds a * vertex at the end to the list. * @param closed */ - public void setClosed(boolean closed) { + public final void setClosed(boolean closed) { this.closed = closed; if(closed){ Vertex first = vertices.get(0); @@ -162,9 +168,9 @@ public class Outline implements Comparable { * as criteria. * @see java.lang.Comparable#compareTo(java.lang.Object) */ - public int compareTo(Outline outline) { - float size = box.getSize(); - float newSize = outline.getBox().getSize(); + public final int compareTo(Outline outline) { + float size = getBounds().getSize(); + float newSize = outline.getBounds().getSize(); if(size < newSize){ return -1; } @@ -173,4 +179,61 @@ public class Outline implements Comparable { } return 0; } + + private final void validateBoundingBox() { + dirtyBBox = false; + bbox.reset(); + for (int i=0; i=0; i--) { + if( ! getVertex(i).equals( o.getVertex(i) ) ) { + return false; + } + } + return true; + } + + /** + * @return deep clone of this Outline + */ + public Outline clone() { + Outline o; + try { + o = (Outline) super.clone(); + } catch (CloneNotSupportedException e) { return null; /* never, ever */ } + o.bbox = bbox.clone(); + o.vertices = new ArrayList(vertices.size()); + for(int i=0; i, Cloneable { +public interface Vertex extends Cloneable { public static interface Factory { T create(); - T create(float x, float y); + T create(float x, float y, float z, boolean onCurve); - T create(float x, float y, float z); - - T create(float[] coordsBuffer, int offset, int length); + T create(float[] coordsBuffer, int offset, int length, boolean onCurve); } - void setCoord(float x, float y); - void setCoord(float x, float y, float z); + /** + * @see System#arraycopy(Object, int, Object, int, int) for thrown IndexOutOfBoundsException + */ void setCoord(float[] coordsBuffer, int offset, int length); float[] getCoord(); @@ -70,11 +69,23 @@ public interface Vertex extends Comparable, Cloneable { void setId(int id); - int compareTo(Vertex p); - float[] getTexCoord(); void setTexCoord(float s, float t); + /** + * @see System#arraycopy(Object, int, Object, int, int) for thrown IndexOutOfBoundsException + */ + void setTexCoord(float[] texCoordsBuffer, int offset, int length); + + /** + * @param obj the Object to compare this Vertex with + * @return true if {@code obj} is a Vertex and not null, on-curve flag is equal and has same vertex- and tex-coords. + */ + boolean equals(Object obj); + + /** + * @return deep clone of this Vertex + */ Vertex clone(); } diff --git a/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java b/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java index 6241d60df..9dade17e9 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java +++ b/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java @@ -27,7 +27,6 @@ */ package com.jogamp.graph.geom.opengl; - import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.math.VectorUtil; @@ -38,7 +37,7 @@ import com.jogamp.graph.math.VectorUtil; public class SVertex implements Vertex { private int id = Integer.MAX_VALUE; protected float[] coord = new float[3]; - protected boolean onCurve = true; + protected boolean onCurve; private float[] texCoord = new float[2]; static final Factory factory = new Factory(); @@ -46,133 +45,130 @@ public class SVertex implements Vertex { public static Factory factory() { return factory; } public static class Factory implements Vertex.Factory { - @Override public SVertex create() { return new SVertex(); } - @Override - public SVertex create(float x, float y) { - return new SVertex(x, y); - } - - @Override - public SVertex create(float x, float y, float z) { - return new SVertex(x, y, z); + public SVertex create(float x, float y, float z, boolean onCurve) { + return new SVertex(x, y, z, onCurve); } - @Override - public SVertex create(float[] coordsBuffer, int offset, int length) { - return new SVertex(coordsBuffer, offset, length); + public SVertex create(float[] coordsBuffer, int offset, int length, boolean onCurve) { + return new SVertex(coordsBuffer, offset, length, onCurve); } } public SVertex() { } - public SVertex(float x, float y) { - setCoord(x, y); - } - public SVertex(float x, float y, float z) { + public SVertex(float x, float y, float z, boolean onCurve) { setCoord(x, y, z); - } - public SVertex(float[] coordsBuffer, int offset, int length) { + setOnCurve(onCurve); + } + + public SVertex(float[] coordsBuffer, int offset, int length, boolean onCurve) { setCoord(coordsBuffer, offset, length); + setOnCurve(onCurve); } - public void setCoord(float x, float y) { - this.coord[0] = x; - this.coord[1] = y; - this.coord[2] = 0f; + public SVertex(float[] coordsBuffer, int offset, int length, + float[] texCoordsBuffer, int offsetTC, int lengthTC, boolean onCurve) { + setCoord(coordsBuffer, offset, length); + setTexCoord(texCoordsBuffer, offsetTC, lengthTC); + setOnCurve(onCurve); } - - public void setCoord(float x, float y, float z) { + + public final void setCoord(float x, float y, float z) { this.coord[0] = x; this.coord[1] = y; this.coord[2] = z; } - public void setCoord(float[] coordsBuffer, int offset, int length) { - if(length > coordsBuffer.length - offset) { - throw new IndexOutOfBoundsException("coordsBuffer too small: "+coordsBuffer.length+" - "+offset+" < "+length); - } - if(length > 3) { - throw new IndexOutOfBoundsException("length too big: "+length+" > "+3); - } - int i=0; - while(i