aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2011-03-25 03:30:21 +0100
committerSven Gothel <[email protected]>2011-03-25 03:30:21 +0100
commit89d1a2d2ecb414d62f44cbb2e3b9bd75746943cf (patch)
tree119cf2a5b793f8c33ed6e107fbfd876bc2970871 /src
parent5b627f588b1bb0e4cef57ffe1f9cd5efdf0f9e42 (diff)
Imported part of Harmony's awt.geom impl. 6.0-r991881, http://harmony.apache.org/ - Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
Diffstat (limited to 'src')
-rw-r--r--src/com/jogamp/graph/geom/plane/AffineTransform.java637
-rw-r--r--src/com/jogamp/graph/geom/plane/GeneralPath.java433
-rw-r--r--src/com/jogamp/graph/geom/plane/IllegalPathStateException.java34
-rw-r--r--src/com/jogamp/graph/geom/plane/NoninvertibleTransformException.java31
-rw-r--r--src/com/jogamp/graph/geom/plane/PathIterator.java44
-rw-r--r--src/jogamp/graph/math/plane/Crossing.java895
-rw-r--r--src/org/apache/harmony/misc/HashCode.java203
7 files changed, 2277 insertions, 0 deletions
diff --git a/src/com/jogamp/graph/geom/plane/AffineTransform.java b/src/com/jogamp/graph/geom/plane/AffineTransform.java
new file mode 100644
index 000000000..065ad94b2
--- /dev/null
+++ b/src/com/jogamp/graph/geom/plane/AffineTransform.java
@@ -0,0 +1,637 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ */
+package java.awt.geom;
+
+import java.awt.Shape;
+import java.io.IOException;
+import java.io.Serializable;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+import org.apache.harmony.misc.HashCode;
+
+public class AffineTransform implements Cloneable, Serializable {
+
+ private static final long serialVersionUID = 1330973210523860834L;
+
+ public static final int TYPE_IDENTITY = 0;
+ public static final int TYPE_TRANSLATION = 1;
+ public static final int TYPE_UNIFORM_SCALE = 2;
+ public static final int TYPE_GENERAL_SCALE = 4;
+ public static final int TYPE_QUADRANT_ROTATION = 8;
+ public static final int TYPE_GENERAL_ROTATION = 16;
+ public static final int TYPE_GENERAL_TRANSFORM = 32;
+ public static final int TYPE_FLIP = 64;
+ public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE;
+ public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION;
+
+ /**
+ * The <code>TYPE_UNKNOWN</code> is an initial type value
+ */
+ static final int TYPE_UNKNOWN = -1;
+
+ /**
+ * The min value equivalent to zero. If absolute value less then ZERO it considered as zero.
+ */
+ static final double ZERO = 1E-10;
+
+ /**
+ * The values of transformation matrix
+ */
+ double m00;
+ double m10;
+ double m01;
+ double m11;
+ double m02;
+ double m12;
+
+ /**
+ * The transformation <code>type</code>
+ */
+ transient int type;
+
+ public AffineTransform() {
+ type = TYPE_IDENTITY;
+ m00 = m11 = 1.0;
+ m10 = m01 = m02 = m12 = 0.0;
+ }
+
+ public AffineTransform(AffineTransform t) {
+ this.type = t.type;
+ this.m00 = t.m00;
+ this.m10 = t.m10;
+ this.m01 = t.m01;
+ this.m11 = t.m11;
+ this.m02 = t.m02;
+ this.m12 = t.m12;
+ }
+
+ public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12) {
+ this.type = TYPE_UNKNOWN;
+ this.m00 = m00;
+ this.m10 = m10;
+ this.m01 = m01;
+ this.m11 = m11;
+ this.m02 = m02;
+ this.m12 = m12;
+ }
+
+ public AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12) {
+ this.type = TYPE_UNKNOWN;
+ this.m00 = m00;
+ this.m10 = m10;
+ this.m01 = m01;
+ this.m11 = m11;
+ this.m02 = m02;
+ this.m12 = m12;
+ }
+
+ public AffineTransform(float[] matrix) {
+ this.type = TYPE_UNKNOWN;
+ m00 = matrix[0];
+ m10 = matrix[1];
+ m01 = matrix[2];
+ m11 = matrix[3];
+ if (matrix.length > 4) {
+ m02 = matrix[4];
+ m12 = matrix[5];
+ }
+ }
+
+ public AffineTransform(double[] matrix) {
+ this.type = TYPE_UNKNOWN;
+ m00 = matrix[0];
+ m10 = matrix[1];
+ m01 = matrix[2];
+ m11 = matrix[3];
+ if (matrix.length > 4) {
+ m02 = matrix[4];
+ m12 = matrix[5];
+ }
+ }
+
+ /*
+ * Method returns type of affine transformation.
+ *
+ * Transform matrix is
+ * m00 m01 m02
+ * m10 m11 m12
+ *
+ * According analytic geometry new basis vectors are (m00, m01) and (m10, m11),
+ * translation vector is (m02, m12). Original basis vectors are (1, 0) and (0, 1).
+ * Type transformations classification:
+ * TYPE_IDENTITY - new basis equals original one and zero translation
+ * TYPE_TRANSLATION - translation vector isn't zero
+ * TYPE_UNIFORM_SCALE - vectors length of new basis equals
+ * TYPE_GENERAL_SCALE - vectors length of new basis doesn't equal
+ * TYPE_FLIP - new basis vector orientation differ from original one
+ * TYPE_QUADRANT_ROTATION - new basis is rotated by 90, 180, 270, or 360 degrees
+ * TYPE_GENERAL_ROTATION - new basis is rotated by arbitrary angle
+ * TYPE_GENERAL_TRANSFORM - transformation can't be inversed
+ */
+ public int getType() {
+ if (type != TYPE_UNKNOWN) {
+ return type;
+ }
+
+ int type = 0;
+
+ if (m00 * m01 + m10 * m11 != 0.0) {
+ type |= TYPE_GENERAL_TRANSFORM;
+ return type;
+ }
+
+ if (m02 != 0.0 || m12 != 0.0) {
+ type |= TYPE_TRANSLATION;
+ } else
+ if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) {
+ type = TYPE_IDENTITY;
+ return type;
+ }
+
+ if (m00 * m11 - m01 * m10 < 0.0) {
+ type |= TYPE_FLIP;
+ }
+
+ double dx = m00 * m00 + m10 * m10;
+ double dy = m01 * m01 + m11 * m11;
+ if (dx != dy) {
+ type |= TYPE_GENERAL_SCALE;
+ } else
+ if (dx != 1.0) {
+ type |= TYPE_UNIFORM_SCALE;
+ }
+
+ if ((m00 == 0.0 && m11 == 0.0) ||
+ (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0)))
+ {
+ type |= TYPE_QUADRANT_ROTATION;
+ } else
+ if (m01 != 0.0 || m10 != 0.0) {
+ type |= TYPE_GENERAL_ROTATION;
+ }
+
+ return type;
+ }
+
+ public double getScaleX() {
+ return m00;
+ }
+
+ public double getScaleY() {
+ return m11;
+ }
+
+ public double getShearX() {
+ return m01;
+ }
+
+ public double getShearY() {
+ return m10;
+ }
+
+ public double getTranslateX() {
+ return m02;
+ }
+
+ public double getTranslateY() {
+ return m12;
+ }
+
+ public boolean isIdentity() {
+ return getType() == TYPE_IDENTITY;
+ }
+
+ public void getMatrix(double[] matrix) {
+ matrix[0] = m00;
+ matrix[1] = m10;
+ matrix[2] = m01;
+ matrix[3] = m11;
+ if (matrix.length > 4) {
+ matrix[4] = m02;
+ matrix[5] = m12;
+ }
+ }
+
+ public double getDeterminant() {
+ return m00 * m11 - m01 * m10;
+ }
+
+ public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) {
+ this.type = TYPE_UNKNOWN;
+ this.m00 = m00;
+ this.m10 = m10;
+ this.m01 = m01;
+ this.m11 = m11;
+ this.m02 = m02;
+ this.m12 = m12;
+ }
+
+ public void setTransform(AffineTransform t) {
+ type = t.type;
+ setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
+ }
+
+ public void setToIdentity() {
+ type = TYPE_IDENTITY;
+ m00 = m11 = 1.0;
+ m10 = m01 = m02 = m12 = 0.0;
+ }
+
+ public void setToTranslation(double mx, double my) {
+ m00 = m11 = 1.0;
+ m01 = m10 = 0.0;
+ m02 = mx;
+ m12 = my;
+ if (mx == 0.0 && my == 0.0) {
+ type = TYPE_IDENTITY;
+ } else {
+ type = TYPE_TRANSLATION;
+ }
+ }
+
+ public void setToScale(double scx, double scy) {
+ m00 = scx;
+ m11 = scy;
+ m10 = m01 = m02 = m12 = 0.0;
+ if (scx != 1.0 || scy != 1.0) {
+ type = TYPE_UNKNOWN;
+ } else {
+ type = TYPE_IDENTITY;
+ }
+ }
+
+ public void setToShear(double shx, double shy) {
+ m00 = m11 = 1.0;
+ m02 = m12 = 0.0;
+ m01 = shx;
+ m10 = shy;
+ if (shx != 0.0 || shy != 0.0) {
+ type = TYPE_UNKNOWN;
+ } else {
+ type = TYPE_IDENTITY;
+ }
+ }
+
+ public void setToRotation(double angle) {
+ double sin = Math.sin(angle);
+ double cos = Math.cos(angle);
+ if (Math.abs(cos) < ZERO) {
+ cos = 0.0;
+ sin = sin > 0.0 ? 1.0 : -1.0;
+ } else
+ if (Math.abs(sin) < ZERO) {
+ sin = 0.0;
+ cos = cos > 0.0 ? 1.0 : -1.0;
+ }
+ m00 = m11 = cos;
+ m01 = -sin;
+ m10 = sin;
+ m02 = m12 = 0.0;
+ type = TYPE_UNKNOWN;
+ }
+
+ public void setToRotation(double angle, double px, double py) {
+ setToRotation(angle);
+ m02 = px * (1.0 - m00) + py * m10;
+ m12 = py * (1.0 - m00) - px * m10;
+ type = TYPE_UNKNOWN;
+ }
+
+ public static AffineTransform getTranslateInstance(double mx, double my) {
+ AffineTransform t = new AffineTransform();
+ t.setToTranslation(mx, my);
+ return t;
+ }
+
+ public static AffineTransform getScaleInstance(double scx, double scY) {
+ AffineTransform t = new AffineTransform();
+ t.setToScale(scx, scY);
+ return t;
+ }
+
+ public static AffineTransform getShearInstance(double shx, double shy) {
+ AffineTransform m = new AffineTransform();
+ m.setToShear(shx, shy);
+ return m;
+ }
+
+ public static AffineTransform getRotateInstance(double angle) {
+ AffineTransform t = new AffineTransform();
+ t.setToRotation(angle);
+ return t;
+ }
+
+ public static AffineTransform getRotateInstance(double angle, double x, double y) {
+ AffineTransform t = new AffineTransform();
+ t.setToRotation(angle, x, y);
+ return t;
+ }
+
+ public void translate(double mx, double my) {
+ concatenate(AffineTransform.getTranslateInstance(mx, my));
+ }
+
+ public void scale(double scx, double scy) {
+ concatenate(AffineTransform.getScaleInstance(scx, scy));
+ }
+
+ public void shear(double shx, double shy) {
+ concatenate(AffineTransform.getShearInstance(shx, shy));
+ }
+
+ public void rotate(double angle) {
+ concatenate(AffineTransform.getRotateInstance(angle));
+ }
+
+ public void rotate(double angle, double px, double py) {
+ concatenate(AffineTransform.getRotateInstance(angle, px, py));
+ }
+
+ /**
+ * Multiply matrix of two AffineTransform objects
+ * @param t1 - the AffineTransform object is a multiplicand
+ * @param t2 - the AffineTransform object is a multiplier
+ * @return an AffineTransform object that is a result of t1 multiplied by matrix t2.
+ */
+ AffineTransform multiply(AffineTransform t1, AffineTransform t2) {
+ return new AffineTransform(
+ t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00
+ t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01
+ t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10
+ t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11
+ t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02
+ t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12
+ }
+
+ public void concatenate(AffineTransform t) {
+ setTransform(multiply(t, this));
+ }
+
+ public void preConcatenate(AffineTransform t) {
+ setTransform(multiply(this, t));
+ }
+
+ public AffineTransform createInverse() throws NoninvertibleTransformException {
+ double det = getDeterminant();
+ if (Math.abs(det) < ZERO) {
+ // awt.204=Determinant is zero
+ throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
+ }
+ return new AffineTransform(
+ m11 / det, // m00
+ -m10 / det, // m10
+ -m01 / det, // m01
+ m00 / det, // m11
+ (m01 * m12 - m11 * m02) / det, // m02
+ (m10 * m02 - m00 * m12) / det // m12
+ );
+ }
+
+ public Point2D transform(Point2D src, Point2D dst) {
+ if (dst == null) {
+ if (src instanceof Point2D.Double) {
+ dst = new Point2D.Double();
+ } else {
+ dst = new Point2D.Float();
+ }
+ }
+
+ double x = src.getX();
+ double y = src.getY();
+
+ dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
+ return dst;
+ }
+
+ public void transform(Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ Point2D srcPoint = src[srcOff++];
+ double x = srcPoint.getX();
+ double y = srcPoint.getY();
+ Point2D dstPoint = dst[dstOff];
+ if (dstPoint == null) {
+ if (srcPoint instanceof Point2D.Double) {
+ dstPoint = new Point2D.Double();
+ } else {
+ dstPoint = new Point2D.Float();
+ }
+ }
+ dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
+ dst[dstOff++] = dstPoint;
+ }
+ }
+
+ public void transform(double[] src, int srcOff, double[] dst, int dstOff, int length) {
+ int step = 2;
+ if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
+ srcOff = srcOff + length * 2 - 2;
+ dstOff = dstOff + length * 2 - 2;
+ step = -2;
+ }
+ while (--length >= 0) {
+ double x = src[srcOff + 0];
+ double y = src[srcOff + 1];
+ dst[dstOff + 0] = x * m00 + y * m01 + m02;
+ dst[dstOff + 1] = x * m10 + y * m11 + m12;
+ srcOff += step;
+ dstOff += step;
+ }
+ }
+
+ public void transform(float[] src, int srcOff, float[] dst, int dstOff, int length) {
+ int step = 2;
+ if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
+ srcOff = srcOff + length * 2 - 2;
+ dstOff = dstOff + length * 2 - 2;
+ step = -2;
+ }
+ while (--length >= 0) {
+ float x = src[srcOff + 0];
+ float y = src[srcOff + 1];
+ dst[dstOff + 0] = (float)(x * m00 + y * m01 + m02);
+ dst[dstOff + 1] = (float)(x * m10 + y * m11 + m12);
+ srcOff += step;
+ dstOff += step;
+ }
+ }
+
+ public void transform(float[] src, int srcOff, double[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ float x = src[srcOff++];
+ float y = src[srcOff++];
+ dst[dstOff++] = x * m00 + y * m01 + m02;
+ dst[dstOff++] = x * m10 + y * m11 + m12;
+ }
+ }
+
+ public void transform(double[] src, int srcOff, float[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ double x = src[srcOff++];
+ double y = src[srcOff++];
+ dst[dstOff++] = (float)(x * m00 + y * m01 + m02);
+ dst[dstOff++] = (float)(x * m10 + y * m11 + m12);
+ }
+ }
+
+ public Point2D deltaTransform(Point2D src, Point2D dst) {
+ if (dst == null) {
+ if (src instanceof Point2D.Double) {
+ dst = new Point2D.Double();
+ } else {
+ dst = new Point2D.Float();
+ }
+ }
+
+ double x = src.getX();
+ double y = src.getY();
+
+ dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
+ return dst;
+ }
+
+ public void deltaTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ double x = src[srcOff++];
+ double y = src[srcOff++];
+ dst[dstOff++] = x * m00 + y * m01;
+ dst[dstOff++] = x * m10 + y * m11;
+ }
+ }
+
+ public Point2D inverseTransform(Point2D src, Point2D dst) throws NoninvertibleTransformException {
+ double det = getDeterminant();
+ if (Math.abs(det) < ZERO) {
+ // awt.204=Determinant is zero
+ throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
+ }
+
+ if (dst == null) {
+ if (src instanceof Point2D.Double) {
+ dst = new Point2D.Double();
+ } else {
+ dst = new Point2D.Float();
+ }
+ }
+
+ double x = src.getX() - m02;
+ double y = src.getY() - m12;
+
+ dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det);
+ return dst;
+ }
+
+ public void inverseTransform(double[] src, int srcOff, double[] dst, int dstOff, int length)
+ throws NoninvertibleTransformException
+ {
+ double det = getDeterminant();
+ if (Math.abs(det) < ZERO) {
+ // awt.204=Determinant is zero
+ throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
+ }
+
+ while (--length >= 0) {
+ double x = src[srcOff++] - m02;
+ double y = src[srcOff++] - m12;
+ dst[dstOff++] = (x * m11 - y * m01) / det;
+ dst[dstOff++] = (y * m00 - x * m10) / det;
+ }
+ }
+
+ public Shape createTransformedShape(Shape src) {
+ if (src == null) {
+ return null;
+ }
+ if (src instanceof GeneralPath) {
+ return ((GeneralPath)src).createTransformedShape(this);
+ }
+ PathIterator path = src.getPathIterator(this);
+ GeneralPath dst = new GeneralPath(path.getWindingRule());
+ dst.append(path, false);
+ return dst;
+ }
+
+ @Override
+ public String toString() {
+ return
+ getClass().getName() +
+ "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ + m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ HashCode hash = new HashCode();
+ hash.append(m00);
+ hash.append(m01);
+ hash.append(m02);
+ hash.append(m10);
+ hash.append(m11);
+ hash.append(m12);
+ return hash.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof AffineTransform) {
+ AffineTransform t = (AffineTransform)obj;
+ return
+ m00 == t.m00 && m01 == t.m01 &&
+ m02 == t.m02 && m10 == t.m10 &&
+ m11 == t.m11 && m12 == t.m12;
+ }
+ return false;
+ }
+
+
+ /**
+ * Write AffineTrasform object to the output steam.
+ * @param stream - the output stream
+ * @throws IOException - if there are I/O errors while writing to the output strem
+ */
+ private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
+ stream.defaultWriteObject();
+ }
+
+
+ /**
+ * Read AffineTransform object from the input stream
+ * @param stream - the input steam
+ * @throws IOException - if there are I/O errors while reading from the input strem
+ * @throws ClassNotFoundException - if class could not be found
+ */
+ private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+ type = TYPE_UNKNOWN;
+ }
+
+}
+
diff --git a/src/com/jogamp/graph/geom/plane/GeneralPath.java b/src/com/jogamp/graph/geom/plane/GeneralPath.java
new file mode 100644
index 000000000..6d6282ef1
--- /dev/null
+++ b/src/com/jogamp/graph/geom/plane/GeneralPath.java
@@ -0,0 +1,433 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ */
+package java.awt.geom;
+
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.util.NoSuchElementException;
+
+import org.apache.harmony.awt.gl.Crossing;
+import org.apache.harmony.awt.internal.nls.Messages;
+
+public final class GeneralPath implements Shape, Cloneable {
+
+ public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
+ public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
+
+ /**
+ * The buffers size
+ */
+ private static final int BUFFER_SIZE = 10;
+
+ /**
+ * The buffers capacity
+ */
+ private static final int BUFFER_CAPACITY = 10;
+
+ /**
+ * The point's types buffer
+ */
+ byte[] types;
+
+ /**
+ * The points buffer
+ */
+ float[] points;
+
+ /**
+ * The point's type buffer size
+ */
+ int typeSize;
+
+ /**
+ * The points buffer size
+ */
+ int pointSize;
+
+ /**
+ * The path rule
+ */
+ int rule;
+
+ /**
+ * The space amount in points buffer for different segmenet's types
+ */
+ static int pointShift[] = {
+ 2, // MOVETO
+ 2, // LINETO
+ 4, // QUADTO
+ 6, // CUBICTO
+ 0}; // CLOSE
+
+ /*
+ * GeneralPath path iterator
+ */
+ class Iterator implements PathIterator {
+
+ /**
+ * The current cursor position in types buffer
+ */
+ int typeIndex;
+
+ /**
+ * The current cursor position in points buffer
+ */
+ int pointIndex;
+
+ /**
+ * The source GeneralPath object
+ */
+ GeneralPath p;
+
+ /**
+ * The path iterator transformation
+ */
+ AffineTransform t;
+
+ /**
+ * Constructs a new GeneralPath.Iterator for given general path
+ * @param path - the source GeneralPath object
+ */
+ Iterator(GeneralPath path) {
+ this(path, null);
+ }
+
+ /**
+ * Constructs a new GeneralPath.Iterator for given general path and transformation
+ * @param path - the source GeneralPath object
+ * @param at - the AffineTransform object to apply rectangle path
+ */
+ Iterator(GeneralPath path, AffineTransform at) {
+ this.p = path;
+ this.t = at;
+ }
+
+ public int getWindingRule() {
+ return p.getWindingRule();
+ }
+
+ public boolean isDone() {
+ return typeIndex >= p.typeSize;
+ }
+
+ public void next() {
+ typeIndex++;
+ }
+
+ public int currentSegment(double[] coords) {
+ if (isDone()) {
+ // awt.4B=Iterator out of bounds
+ throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
+ }
+ int type = p.types[typeIndex];
+ int count = GeneralPath.pointShift[type];
+ for (int i = 0; i < count; i++) {
+ coords[i] = p.points[pointIndex + i];
+ }
+ if (t != null) {
+ t.transform(coords, 0, coords, 0, count / 2);
+ }
+ pointIndex += count;
+ return type;
+ }
+
+ public int currentSegment(float[] coords) {
+ if (isDone()) {
+ // awt.4B=Iterator out of bounds
+ throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
+ }
+ int type = p.types[typeIndex];
+ int count = GeneralPath.pointShift[type];
+ System.arraycopy(p.points, pointIndex, coords, 0, count);
+ if (t != null) {
+ t.transform(coords, 0, coords, 0, count / 2);
+ }
+ pointIndex += count;
+ return type;
+ }
+
+ }
+
+ public GeneralPath() {
+ this(WIND_NON_ZERO, BUFFER_SIZE);
+ }
+
+ public GeneralPath(int rule) {
+ this(rule, BUFFER_SIZE);
+ }
+
+ public GeneralPath(int rule, int initialCapacity) {
+ setWindingRule(rule);
+ types = new byte[initialCapacity];
+ points = new float[initialCapacity * 2];
+ }
+
+ public GeneralPath(Shape shape) {
+ this(WIND_NON_ZERO, BUFFER_SIZE);
+ PathIterator p = shape.getPathIterator(null);
+ setWindingRule(p.getWindingRule());
+ append(p, false);
+ }
+
+ public void setWindingRule(int rule) {
+ if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
+ // awt.209=Invalid winding rule value
+ throw new java.lang.IllegalArgumentException(Messages.getString("awt.209")); //$NON-NLS-1$
+ }
+ this.rule = rule;
+ }
+
+ public int getWindingRule() {
+ return rule;
+ }
+
+ /**
+ * Checks points and types buffer size to add pointCount points. If necessary realloc buffers to enlarge size.
+ * @param pointCount - the point count to be added in buffer
+ */
+ void checkBuf(int pointCount, boolean checkMove) {
+ if (checkMove && typeSize == 0) {
+ // awt.20A=First segment should be SEG_MOVETO type
+ throw new IllegalPathStateException(Messages.getString("awt.20A")); //$NON-NLS-1$
+ }
+ if (typeSize == types.length) {
+ byte tmp[] = new byte[typeSize + BUFFER_CAPACITY];
+ System.arraycopy(types, 0, tmp, 0, typeSize);
+ types = tmp;
+ }
+ if (pointSize + pointCount > points.length) {
+ float tmp[] = new float[pointSize + Math.max(BUFFER_CAPACITY * 2, pointCount)];
+ System.arraycopy(points, 0, tmp, 0, pointSize);
+ points = tmp;
+ }
+ }
+
+ public void moveTo(float x, float y) {
+ if (typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_MOVETO) {
+ points[pointSize - 2] = x;
+ points[pointSize - 1] = y;
+ } else {
+ checkBuf(2, false);
+ types[typeSize++] = PathIterator.SEG_MOVETO;
+ points[pointSize++] = x;
+ points[pointSize++] = y;
+ }
+ }
+
+ public void lineTo(float x, float y) {
+ checkBuf(2, true);
+ types[typeSize++] = PathIterator.SEG_LINETO;
+ points[pointSize++] = x;
+ points[pointSize++] = y;
+ }
+
+ public void quadTo(float x1, float y1, float x2, float y2) {
+ checkBuf(4, true);
+ types[typeSize++] = PathIterator.SEG_QUADTO;
+ points[pointSize++] = x1;
+ points[pointSize++] = y1;
+ points[pointSize++] = x2;
+ points[pointSize++] = y2;
+ }
+
+ public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
+ checkBuf(6, true);
+ types[typeSize++] = PathIterator.SEG_CUBICTO;
+ points[pointSize++] = x1;
+ points[pointSize++] = y1;
+ points[pointSize++] = x2;
+ points[pointSize++] = y2;
+ points[pointSize++] = x3;
+ points[pointSize++] = y3;
+ }
+
+ public void closePath() {
+ if (typeSize == 0 || types[typeSize - 1] != PathIterator.SEG_CLOSE) {
+ checkBuf(0, true);
+ types[typeSize++] = PathIterator.SEG_CLOSE;
+ }
+ }
+
+ public void append(Shape shape, boolean connect) {
+ PathIterator p = shape.getPathIterator(null);
+ append(p, connect);
+ }
+
+ public void append(PathIterator path, boolean connect) {
+ while (!path.isDone()) {
+ float coords[] = new float[6];
+ switch (path.currentSegment(coords)) {
+ case PathIterator.SEG_MOVETO:
+ if (!connect || typeSize == 0) {
+ moveTo(coords[0], coords[1]);
+ break;
+ }
+ if (types[typeSize - 1] != PathIterator.SEG_CLOSE &&
+ points[pointSize - 2] == coords[0] &&
+ points[pointSize - 1] == coords[1])
+ {
+ break;
+ }
+ // NO BREAK;
+ case PathIterator.SEG_LINETO:
+ lineTo(coords[0], coords[1]);
+ break;
+ case PathIterator.SEG_QUADTO:
+ quadTo(coords[0], coords[1], coords[2], coords[3]);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
+ break;
+ case PathIterator.SEG_CLOSE:
+ closePath();
+ break;
+ }
+ path.next();
+ connect = false;
+ }
+ }
+
+ public Point2D getCurrentPoint() {
+ if (typeSize == 0) {
+ return null;
+ }
+ int j = pointSize - 2;
+ if (types[typeSize - 1] == PathIterator.SEG_CLOSE) {
+
+ for (int i = typeSize - 2; i > 0; i--) {
+ int type = types[i];
+ if (type == PathIterator.SEG_MOVETO) {
+ break;
+ }
+ j -= pointShift[type];
+ }
+ }
+ return new Point2D.Float(points[j], points[j + 1]);
+ }
+
+ public void reset() {
+ typeSize = 0;
+ pointSize = 0;
+ }
+
+ public void transform(AffineTransform t) {
+ t.transform(points, 0, points, 0, pointSize / 2);
+ }
+
+ public Shape createTransformedShape(AffineTransform t) {
+ GeneralPath p = (GeneralPath)clone();
+ if (t != null) {
+ p.transform(t);
+ }
+ return p;
+ }
+
+ public Rectangle2D getBounds2D() {
+ float rx1, ry1, rx2, ry2;
+ if (pointSize == 0) {
+ rx1 = ry1 = rx2 = ry2 = 0.0f;
+ } else {
+ int i = pointSize - 1;
+ ry1 = ry2 = points[i--];
+ rx1 = rx2 = points[i--];
+ while (i > 0) {
+ float y = points[i--];
+ float x = points[i--];
+ if (x < rx1) {
+ rx1 = x;
+ } else
+ if (x > rx2) {
+ rx2 = x;
+ }
+ if (y < ry1) {
+ ry1 = y;
+ } else
+ if (y > ry2) {
+ ry2 = y;
+ }
+ }
+ }
+ return new Rectangle2D.Float(rx1, ry1, rx2 - rx1, ry2 - ry1);
+ }
+
+ public Rectangle getBounds() {
+ return getBounds2D().getBounds();
+ }
+
+ /**
+ * Checks cross count according to path rule to define is it point inside shape or not.
+ * @param cross - the point cross count
+ * @return true if point is inside path, or false otherwise
+ */
+ boolean isInside(int cross) {
+ if (rule == WIND_NON_ZERO) {
+ return Crossing.isInsideNonZero(cross);
+ }
+ return Crossing.isInsideEvenOdd(cross);
+ }
+
+ public boolean contains(double px, double py) {
+ return isInside(Crossing.crossShape(this, px, py));
+ }
+
+ public boolean contains(double rx, double ry, double rw, double rh) {
+ int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
+ return cross != Crossing.CROSSING && isInside(cross);
+ }
+
+ public boolean intersects(double rx, double ry, double rw, double rh) {
+ int cross = Crossing.intersectShape(this, rx, ry, rw, rh);
+ return cross == Crossing.CROSSING || isInside(cross);
+ }
+
+ public boolean contains(Point2D p) {
+ return contains(p.getX(), p.getY());
+ }
+
+ public boolean contains(Rectangle2D r) {
+ return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ public boolean intersects(Rectangle2D r) {
+ return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
+ }
+
+ public PathIterator getPathIterator(AffineTransform t) {
+ return new Iterator(this, t);
+ }
+
+ public PathIterator getPathIterator(AffineTransform t, double flatness) {
+ return new FlatteningPathIterator(getPathIterator(t), flatness);
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ GeneralPath p = (GeneralPath) super.clone();
+ p.types = types.clone();
+ p.points = points.clone();
+ return p;
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+
+}
+
diff --git a/src/com/jogamp/graph/geom/plane/IllegalPathStateException.java b/src/com/jogamp/graph/geom/plane/IllegalPathStateException.java
new file mode 100644
index 000000000..9d667e510
--- /dev/null
+++ b/src/com/jogamp/graph/geom/plane/IllegalPathStateException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ */
+package java.awt.geom;
+
+public class IllegalPathStateException extends RuntimeException {
+
+ private static final long serialVersionUID = -5158084205220481094L;
+
+ public IllegalPathStateException() {
+ }
+
+ public IllegalPathStateException(String s) {
+ super(s);
+ }
+
+}
+
diff --git a/src/com/jogamp/graph/geom/plane/NoninvertibleTransformException.java b/src/com/jogamp/graph/geom/plane/NoninvertibleTransformException.java
new file mode 100644
index 000000000..eee170ec1
--- /dev/null
+++ b/src/com/jogamp/graph/geom/plane/NoninvertibleTransformException.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ */
+package java.awt.geom;
+
+public class NoninvertibleTransformException extends java.lang.Exception {
+
+ private static final long serialVersionUID = 6137225240503990466L;
+
+ public NoninvertibleTransformException(String s) {
+ super(s);
+ }
+
+}
+
diff --git a/src/com/jogamp/graph/geom/plane/PathIterator.java b/src/com/jogamp/graph/geom/plane/PathIterator.java
new file mode 100644
index 000000000..22b897841
--- /dev/null
+++ b/src/com/jogamp/graph/geom/plane/PathIterator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ */
+package java.awt.geom;
+
+public interface PathIterator {
+
+ public static final int WIND_EVEN_ODD = 0;
+ public static final int WIND_NON_ZERO = 1;
+
+ public static final int SEG_MOVETO = 0;
+ public static final int SEG_LINETO = 1;
+ public static final int SEG_QUADTO = 2;
+ public static final int SEG_CUBICTO = 3;
+ public static final int SEG_CLOSE = 4;
+
+ public int getWindingRule();
+
+ public boolean isDone();
+
+ public void next();
+
+ public int currentSegment(float[] coords);
+
+ public int currentSegment(double[] coords);
+
+}
+
diff --git a/src/jogamp/graph/math/plane/Crossing.java b/src/jogamp/graph/math/plane/Crossing.java
new file mode 100644
index 000000000..7da1c466e
--- /dev/null
+++ b/src/jogamp/graph/math/plane/Crossing.java
@@ -0,0 +1,895 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ */
+package org.apache.harmony.awt.gl;
+
+import java.awt.Shape;
+import java.awt.geom.PathIterator;
+
+public class Crossing {
+
+ /**
+ * Allowable tolerance for bounds comparison
+ */
+ static final double DELTA = 1E-5;
+
+ /**
+ * If roots have distance less then <code>ROOT_DELTA</code> they are double
+ */
+ static final double ROOT_DELTA = 1E-10;
+
+ /**
+ * Rectangle cross segment
+ */
+ public static final int CROSSING = 255;
+
+ /**
+ * Unknown crossing result
+ */
+ static final int UNKNOWN = 254;
+
+ /**
+ * Solves quadratic equation
+ * @param eqn - the coefficients of the equation
+ * @param res - the roots of the equation
+ * @return a number of roots
+ */
+ public static int solveQuad(double eqn[], double res[]) {
+ double a = eqn[2];
+ double b = eqn[1];
+ double c = eqn[0];
+ int rc = 0;
+ if (a == 0.0) {
+ if (b == 0.0) {
+ return -1;
+ }
+ res[rc++] = -c / b;
+ } else {
+ double d = b * b - 4.0 * a * c;
+ // d < 0.0
+ if (d < 0.0) {
+ return 0;
+ }
+ d = Math.sqrt(d);
+ res[rc++] = (- b + d) / (a * 2.0);
+ // d != 0.0
+ if (d != 0.0) {
+ res[rc++] = (- b - d) / (a * 2.0);
+ }
+ }
+ return fixRoots(res, rc);
+ }
+
+ /**
+ * Solves cubic equation
+ * @param eqn - the coefficients of the equation
+ * @param res - the roots of the equation
+ * @return a number of roots
+ */
+ public static int solveCubic(double eqn[], double res[]) {
+ double d = eqn[3];
+ if (d == 0) {
+ return solveQuad(eqn, res);
+ }
+ double a = eqn[2] / d;
+ double b = eqn[1] / d;
+ double c = eqn[0] / d;
+ int rc = 0;
+
+ double Q = (a * a - 3.0 * b) / 9.0;
+ double R = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0;
+ double Q3 = Q * Q * Q;
+ double R2 = R * R;
+ double n = - a / 3.0;
+
+ if (R2 < Q3) {
+ double t = Math.acos(R / Math.sqrt(Q3)) / 3.0;
+ double p = 2.0 * Math.PI / 3.0;
+ double m = -2.0 * Math.sqrt(Q);
+ res[rc++] = m * Math.cos(t) + n;
+ res[rc++] = m * Math.cos(t + p) + n;
+ res[rc++] = m * Math.cos(t - p) + n;
+ } else {
+// Debug.println("R2 >= Q3 (" + R2 + "/" + Q3 + ")");
+ double A = Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1.0 / 3.0);
+ if (R > 0.0) {
+ A = -A;
+ }
+// if (A == 0.0) {
+ if (-ROOT_DELTA < A && A < ROOT_DELTA) {
+ res[rc++] = n;
+ } else {
+ double B = Q / A;
+ res[rc++] = A + B + n;
+// if (R2 == Q3) {
+ double delta = R2 - Q3;
+ if (-ROOT_DELTA < delta && delta < ROOT_DELTA) {
+ res[rc++] = - (A + B) / 2.0 + n;
+ }
+ }
+
+ }
+ return fixRoots(res, rc);
+ }
+
+ /**
+ * Excludes double roots. Roots are double if they lies enough close with each other.
+ * @param res - the roots
+ * @param rc - the roots count
+ * @return new roots count
+ */
+ static int fixRoots(double res[], int rc) {
+ int tc = 0;
+ for(int i = 0; i < rc; i++) {
+ out: {
+ for(int j = i + 1; j < rc; j++) {
+ if (isZero(res[i] - res[j])) {
+ break out;
+ }
+ }
+ res[tc++] = res[i];
+ }
+ }
+ return tc;
+ }
+
+ /**
+ * QuadCurve class provides basic functionality to find curve crossing and calculating bounds
+ */
+ public static class QuadCurve {
+
+ double ax, ay, bx, by;
+ double Ax, Ay, Bx, By;
+
+ public QuadCurve(double x1, double y1, double cx, double cy, double x2, double y2) {
+ ax = x2 - x1;
+ ay = y2 - y1;
+ bx = cx - x1;
+ by = cy - y1;
+
+ Bx = bx + bx; // Bx = 2.0 * bx
+ Ax = ax - Bx; // Ax = ax - 2.0 * bx
+
+ By = by + by; // By = 2.0 * by
+ Ay = ay - By; // Ay = ay - 2.0 * by
+ }
+
+ int cross(double res[], int rc, double py1, double py2) {
+ int cross = 0;
+
+ for (int i = 0; i < rc; i++) {
+ double t = res[i];
+
+ // CURVE-OUTSIDE
+ if (t < -DELTA || t > 1 + DELTA) {
+ continue;
+ }
+ // CURVE-START
+ if (t < DELTA) {
+ if (py1 < 0.0 && (bx != 0.0 ? bx : ax - bx) < 0.0) {
+ cross--;
+ }
+ continue;
+ }
+ // CURVE-END
+ if (t > 1 - DELTA) {
+ if (py1 < ay && (ax != bx ? ax - bx : bx) > 0.0) {
+ cross++;
+ }
+ continue;
+ }
+ // CURVE-INSIDE
+ double ry = t * (t * Ay + By);
+ // ry = t * t * Ay + t * By
+ if (ry > py2) {
+ double rxt = t * Ax + bx;
+ // rxt = 2.0 * t * Ax + Bx = 2.0 * t * Ax + 2.0 * bx
+ if (rxt > -DELTA && rxt < DELTA) {
+ continue;
+ }
+ cross += rxt > 0.0 ? 1 : -1;
+ }
+ } // for
+
+ return cross;
+ }
+
+ int solvePoint(double res[], double px) {
+ double eqn[] = {-px, Bx, Ax};
+ return solveQuad(eqn, res);
+ }
+
+ int solveExtrem(double res[]) {
+ int rc = 0;
+ if (Ax != 0.0) {
+ res[rc++] = - Bx / (Ax + Ax);
+ }
+ if (Ay != 0.0) {
+ res[rc++] = - By / (Ay + Ay);
+ }
+ return rc;
+ }
+
+ int addBound(double bound[], int bc, double res[], int rc, double minX, double maxX, boolean changeId, int id) {
+ for(int i = 0; i < rc; i++) {
+ double t = res[i];
+ if (t > -DELTA && t < 1 + DELTA) {
+ double rx = t * (t * Ax + Bx);
+ if (minX <= rx && rx <= maxX) {
+ bound[bc++] = t;
+ bound[bc++] = rx;
+ bound[bc++] = t * (t * Ay + By);
+ bound[bc++] = id;
+ if (changeId) {
+ id++;
+ }
+ }
+ }
+ }
+ return bc;
+ }
+
+ }
+
+ /**
+ * CubicCurve class provides basic functionality to find curve crossing and calculating bounds
+ */
+ public static class CubicCurve {
+
+ double ax, ay, bx, by, cx, cy;
+ double Ax, Ay, Bx, By, Cx, Cy;
+ double Ax3, Bx2;
+
+ public CubicCurve(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2) {
+ ax = x2 - x1;
+ ay = y2 - y1;
+ bx = cx1 - x1;
+ by = cy1 - y1;
+ cx = cx2 - x1;
+ cy = cy2 - y1;
+
+ Cx = bx + bx + bx; // Cx = 3.0 * bx
+ Bx = cx + cx + cx - Cx - Cx; // Bx = 3.0 * cx - 6.0 * bx
+ Ax = ax - Bx - Cx; // Ax = ax - 3.0 * cx + 3.0 * bx
+
+ Cy = by + by + by; // Cy = 3.0 * by
+ By = cy + cy + cy - Cy - Cy; // By = 3.0 * cy - 6.0 * by
+ Ay = ay - By - Cy; // Ay = ay - 3.0 * cy + 3.0 * by
+
+ Ax3 = Ax + Ax + Ax;
+ Bx2 = Bx + Bx;
+ }
+
+ int cross(double res[], int rc, double py1, double py2) {
+ int cross = 0;
+ for (int i = 0; i < rc; i++) {
+ double t = res[i];
+
+ // CURVE-OUTSIDE
+ if (t < -DELTA || t > 1 + DELTA) {
+ continue;
+ }
+ // CURVE-START
+ if (t < DELTA) {
+ if (py1 < 0.0 && (bx != 0.0 ? bx : (cx != bx ? cx - bx : ax - cx)) < 0.0) {
+ cross--;
+ }
+ continue;
+ }
+ // CURVE-END
+ if (t > 1 - DELTA) {
+ if (py1 < ay && (ax != cx ? ax - cx : (cx != bx ? cx - bx : bx)) > 0.0) {
+ cross++;
+ }
+ continue;
+ }
+ // CURVE-INSIDE
+ double ry = t * (t * (t * Ay + By) + Cy);
+ // ry = t * t * t * Ay + t * t * By + t * Cy
+ if (ry > py2) {
+ double rxt = t * (t * Ax3 + Bx2) + Cx;
+ // rxt = 3.0 * t * t * Ax + 2.0 * t * Bx + Cx
+ if (rxt > -DELTA && rxt < DELTA) {
+ rxt = t * (Ax3 + Ax3) + Bx2;
+ // rxt = 6.0 * t * Ax + 2.0 * Bx
+ if (rxt < -DELTA || rxt > DELTA) {
+ // Inflection point
+ continue;
+ }
+ rxt = ax;
+ }
+ cross += rxt > 0.0 ? 1 : -1;
+ }
+ } //for
+
+ return cross;
+ }
+
+ int solvePoint(double res[], double px) {
+ double eqn[] = {-px, Cx, Bx, Ax};
+ return solveCubic(eqn, res);
+ }
+
+ int solveExtremX(double res[]) {
+ double eqn[] = {Cx, Bx2, Ax3};
+ return solveQuad(eqn, res);
+ }
+
+ int solveExtremY(double res[]) {
+ double eqn[] = {Cy, By + By, Ay + Ay + Ay};
+ return solveQuad(eqn, res);
+ }
+
+ int addBound(double bound[], int bc, double res[], int rc, double minX, double maxX, boolean changeId, int id) {
+ for(int i = 0; i < rc; i++) {
+ double t = res[i];
+ if (t > -DELTA && t < 1 + DELTA) {
+ double rx = t * (t * (t * Ax + Bx) + Cx);
+ if (minX <= rx && rx <= maxX) {
+ bound[bc++] = t;
+ bound[bc++] = rx;
+ bound[bc++] = t * (t * (t * Ay + By) + Cy);
+ bound[bc++] = id;
+ if (changeId) {
+ id++;
+ }
+ }
+ }
+ }
+ return bc;
+ }
+
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross line.
+ */
+ public static int crossLine(double x1, double y1, double x2, double y2, double x, double y) {
+
+ // LEFT/RIGHT/UP/EMPTY
+ if ((x < x1 && x < x2) ||
+ (x > x1 && x > x2) ||
+ (y > y1 && y > y2) ||
+ (x1 == x2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (y < y1 && y < y2) {
+ } else {
+ // INSIDE
+ if ((y2 - y1) * (x - x1) / (x2 - x1) <= y - y1) {
+ // INSIDE-UP
+ return 0;
+ }
+ }
+
+ // START
+ if (x == x1) {
+ return x1 < x2 ? 0 : -1;
+ }
+
+ // END
+ if (x == x2) {
+ return x1 < x2 ? 1 : 0;
+ }
+
+ // INSIDE-DOWN
+ return x1 < x2 ? 1 : -1;
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross quard curve
+ */
+ public static int crossQuad(double x1, double y1, double cx, double cy, double x2, double y2, double x, double y) {
+
+ // LEFT/RIGHT/UP/EMPTY
+ if ((x < x1 && x < cx && x < x2) ||
+ (x > x1 && x > cx && x > x2) ||
+ (y > y1 && y > cy && y > y2) ||
+ (x1 == cx && cx == x2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (y < y1 && y < cy && y < y2 && x != x1 && x != x2) {
+ if (x1 < x2) {
+ return x1 < x && x < x2 ? 1 : 0;
+ }
+ return x2 < x && x < x1 ? -1 : 0;
+ }
+
+ // INSIDE
+ QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2);
+ double px = x - x1;
+ double py = y - y1;
+ double res[] = new double[3];
+ int rc = c.solvePoint(res, px);
+
+ return c.cross(res, rc, py, py);
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross cubic curve
+ */
+ public static int crossCubic(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2, double x, double y) {
+
+ // LEFT/RIGHT/UP/EMPTY
+ if ((x < x1 && x < cx1 && x < cx2 && x < x2) ||
+ (x > x1 && x > cx1 && x > cx2 && x > x2) ||
+ (y > y1 && y > cy1 && y > cy2 && y > y2) ||
+ (x1 == cx1 && cx1 == cx2 && cx2 == x2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (y < y1 && y < cy1 && y < cy2 && y < y2 && x != x1 && x != x2) {
+ if (x1 < x2) {
+ return x1 < x && x < x2 ? 1 : 0;
+ }
+ return x2 < x && x < x1 ? -1 : 0;
+ }
+
+ // INSIDE
+ CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
+ double px = x - x1;
+ double py = y - y1;
+ double res[] = new double[3];
+ int rc = c.solvePoint(res, px);
+ return c.cross(res, rc, py, py);
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross path
+ */
+ public static int crossPath(PathIterator p, double x, double y) {
+ int cross = 0;
+ double mx, my, cx, cy;
+ mx = my = cx = cy = 0.0;
+ double coords[] = new double[6];
+
+ while (!p.isDone()) {
+ switch (p.currentSegment(coords)) {
+ case PathIterator.SEG_MOVETO:
+ if (cx != mx || cy != my) {
+ cross += crossLine(cx, cy, mx, my, x, y);
+ }
+ mx = cx = coords[0];
+ my = cy = coords[1];
+ break;
+ case PathIterator.SEG_LINETO:
+ cross += crossLine(cx, cy, cx = coords[0], cy = coords[1], x, y);
+ break;
+ case PathIterator.SEG_QUADTO:
+ cross += crossQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], x, y);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ cross += crossCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], x, y);
+ break;
+ case PathIterator.SEG_CLOSE:
+ if (cy != my || cx != mx) {
+ cross += crossLine(cx, cy, cx = mx, cy = my, x, y);
+ }
+ break;
+ }
+
+ // checks if the point (x,y) is the vertex of shape with PathIterator p
+ if (x == cx && y == cy) {
+ cross = 0;
+ cy = my;
+ break;
+ }
+ p.next();
+ }
+ if (cy != my) {
+ cross += crossLine(cx, cy, mx, my, x, y);
+ }
+ return cross;
+ }
+
+ /**
+ * Returns how many times ray from point (x,y) cross shape
+ */
+ public static int crossShape(Shape s, double x, double y) {
+ if (!s.getBounds2D().contains(x, y)) {
+ return 0;
+ }
+ return crossPath(s.getPathIterator(null), x, y);
+ }
+
+ /**
+ * Returns true if value enough small
+ */
+ public static boolean isZero(double val) {
+ return -DELTA < val && val < DELTA;
+ }
+
+ /**
+ * Sort bound array
+ */
+ static void sortBound(double bound[], int bc) {
+ for(int i = 0; i < bc - 4; i += 4) {
+ int k = i;
+ for(int j = i + 4; j < bc; j += 4) {
+ if (bound[k] > bound[j]) {
+ k = j;
+ }
+ }
+ if (k != i) {
+ double tmp = bound[i];
+ bound[i] = bound[k];
+ bound[k] = tmp;
+ tmp = bound[i + 1];
+ bound[i + 1] = bound[k + 1];
+ bound[k + 1] = tmp;
+ tmp = bound[i + 2];
+ bound[i + 2] = bound[k + 2];
+ bound[k + 2] = tmp;
+ tmp = bound[i + 3];
+ bound[i + 3] = bound[k + 3];
+ bound[k + 3] = tmp;
+ }
+ }
+ }
+
+ /**
+ * Returns are bounds intersect or not intersect rectangle
+ */
+ static int crossBound(double bound[], int bc, double py1, double py2) {
+
+ // LEFT/RIGHT
+ if (bc == 0) {
+ return 0;
+ }
+
+ // Check Y coordinate
+ int up = 0;
+ int down = 0;
+ for(int i = 2; i < bc; i += 4) {
+ if (bound[i] < py1) {
+ up++;
+ continue;
+ }
+ if (bound[i] > py2) {
+ down++;
+ continue;
+ }
+ return CROSSING;
+ }
+
+ // UP
+ if (down == 0) {
+ return 0;
+ }
+
+ if (up != 0) {
+ // bc >= 2
+ sortBound(bound, bc);
+ boolean sign = bound[2] > py2;
+ for(int i = 6; i < bc; i += 4) {
+ boolean sign2 = bound[i] > py2;
+ if (sign != sign2 && bound[i + 1] != bound[i - 3]) {
+ return CROSSING;
+ }
+ sign = sign2;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross line or the are intersect
+ */
+ public static int intersectLine(double x1, double y1, double x2, double y2, double rx1, double ry1, double rx2, double ry2) {
+
+ // LEFT/RIGHT/UP
+ if ((rx2 < x1 && rx2 < x2) ||
+ (rx1 > x1 && rx1 > x2) ||
+ (ry1 > y1 && ry1 > y2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (ry2 < y1 && ry2 < y2) {
+ } else {
+
+ // INSIDE
+ if (x1 == x2) {
+ return CROSSING;
+ }
+
+ // Build bound
+ double bx1, bx2;
+ if (x1 < x2) {
+ bx1 = x1 < rx1 ? rx1 : x1;
+ bx2 = x2 < rx2 ? x2 : rx2;
+ } else {
+ bx1 = x2 < rx1 ? rx1 : x2;
+ bx2 = x1 < rx2 ? x1 : rx2;
+ }
+ double k = (y2 - y1) / (x2 - x1);
+ double by1 = k * (bx1 - x1) + y1;
+ double by2 = k * (bx2 - x1) + y1;
+
+ // BOUND-UP
+ if (by1 < ry1 && by2 < ry1) {
+ return 0;
+ }
+
+ // BOUND-DOWN
+ if (by1 > ry2 && by2 > ry2) {
+ } else {
+ return CROSSING;
+ }
+ }
+
+ // EMPTY
+ if (x1 == x2) {
+ return 0;
+ }
+
+ // CURVE-START
+ if (rx1 == x1) {
+ return x1 < x2 ? 0 : -1;
+ }
+
+ // CURVE-END
+ if (rx1 == x2) {
+ return x1 < x2 ? 1 : 0;
+ }
+
+ if (x1 < x2) {
+ return x1 < rx1 && rx1 < x2 ? 1 : 0;
+ }
+ return x2 < rx1 && rx1 < x1 ? -1 : 0;
+
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross quad curve or the are intersect
+ */
+ public static int intersectQuad(double x1, double y1, double cx, double cy, double x2, double y2, double rx1, double ry1, double rx2, double ry2) {
+
+ // LEFT/RIGHT/UP ------------------------------------------------------
+ if ((rx2 < x1 && rx2 < cx && rx2 < x2) ||
+ (rx1 > x1 && rx1 > cx && rx1 > x2) ||
+ (ry1 > y1 && ry1 > cy && ry1 > y2))
+ {
+ return 0;
+ }
+
+ // DOWN ---------------------------------------------------------------
+ if (ry2 < y1 && ry2 < cy && ry2 < y2 && rx1 != x1 && rx1 != x2) {
+ if (x1 < x2) {
+ return x1 < rx1 && rx1 < x2 ? 1 : 0;
+ }
+ return x2 < rx1 && rx1 < x1 ? -1 : 0;
+ }
+
+ // INSIDE -------------------------------------------------------------
+ QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2);
+ double px1 = rx1 - x1;
+ double py1 = ry1 - y1;
+ double px2 = rx2 - x1;
+ double py2 = ry2 - y1;
+
+ double res1[] = new double[3];
+ double res2[] = new double[3];
+ int rc1 = c.solvePoint(res1, px1);
+ int rc2 = c.solvePoint(res2, px2);
+
+ // INSIDE-LEFT/RIGHT
+ if (rc1 == 0 && rc2 == 0) {
+ return 0;
+ }
+
+ // Build bound --------------------------------------------------------
+ double minX = px1 - DELTA;
+ double maxX = px2 + DELTA;
+ double bound[] = new double[28];
+ int bc = 0;
+ // Add roots
+ bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1);
+ // Add extremal points`
+ rc2 = c.solveExtrem(res2);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2);
+ // Add start and end
+ if (rx1 < x1 && x1 < rx2) {
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 4;
+ }
+ if (rx1 < x2 && x2 < rx2) {
+ bound[bc++] = 1.0;
+ bound[bc++] = c.ax;
+ bound[bc++] = c.ay;
+ bound[bc++] = 5;
+ }
+ // End build bound ----------------------------------------------------
+
+ int cross = crossBound(bound, bc, py1, py2);
+ if (cross != UNKNOWN) {
+ return cross;
+ }
+ return c.cross(res1, rc1, py1, py2);
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross cubic curve or the are intersect
+ */
+ public static int intersectCubic(double x1, double y1, double cx1, double cy1, double cx2, double cy2, double x2, double y2, double rx1, double ry1, double rx2, double ry2) {
+
+ // LEFT/RIGHT/UP
+ if ((rx2 < x1 && rx2 < cx1 && rx2 < cx2 && rx2 < x2) ||
+ (rx1 > x1 && rx1 > cx1 && rx1 > cx2 && rx1 > x2) ||
+ (ry1 > y1 && ry1 > cy1 && ry1 > cy2 && ry1 > y2))
+ {
+ return 0;
+ }
+
+ // DOWN
+ if (ry2 < y1 && ry2 < cy1 && ry2 < cy2 && ry2 < y2 && rx1 != x1 && rx1 != x2) {
+ if (x1 < x2) {
+ return x1 < rx1 && rx1 < x2 ? 1 : 0;
+ }
+ return x2 < rx1 && rx1 < x1 ? -1 : 0;
+ }
+
+ // INSIDE
+ CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2);
+ double px1 = rx1 - x1;
+ double py1 = ry1 - y1;
+ double px2 = rx2 - x1;
+ double py2 = ry2 - y1;
+
+ double res1[] = new double[3];
+ double res2[] = new double[3];
+ int rc1 = c.solvePoint(res1, px1);
+ int rc2 = c.solvePoint(res2, px2);
+
+ // LEFT/RIGHT
+ if (rc1 == 0 && rc2 == 0) {
+ return 0;
+ }
+
+ double minX = px1 - DELTA;
+ double maxX = px2 + DELTA;
+
+ // Build bound --------------------------------------------------------
+ double bound[] = new double[40];
+ int bc = 0;
+ // Add roots
+ bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1);
+ // Add extrimal points
+ rc2 = c.solveExtremX(res2);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2);
+ rc2 = c.solveExtremY(res2);
+ bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 4);
+ // Add start and end
+ if (rx1 < x1 && x1 < rx2) {
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 0.0;
+ bound[bc++] = 6;
+ }
+ if (rx1 < x2 && x2 < rx2) {
+ bound[bc++] = 1.0;
+ bound[bc++] = c.ax;
+ bound[bc++] = c.ay;
+ bound[bc++] = 7;
+ }
+ // End build bound ----------------------------------------------------
+
+ int cross = crossBound(bound, bc, py1, py2);
+ if (cross != UNKNOWN) {
+ return cross;
+ }
+ return c.cross(res1, rc1, py1, py2);
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross path or the are intersect
+ */
+ public static int intersectPath(PathIterator p, double x, double y, double w, double h) {
+
+ int cross = 0;
+ int count;
+ double mx, my, cx, cy;
+ mx = my = cx = cy = 0.0;
+ double coords[] = new double[6];
+
+ double rx1 = x;
+ double ry1 = y;
+ double rx2 = x + w;
+ double ry2 = y + h;
+
+ while (!p.isDone()) {
+ count = 0;
+ switch (p.currentSegment(coords)) {
+ case PathIterator.SEG_MOVETO:
+ if (cx != mx || cy != my) {
+ count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
+ }
+ mx = cx = coords[0];
+ my = cy = coords[1];
+ break;
+ case PathIterator.SEG_LINETO:
+ count = intersectLine(cx, cy, cx = coords[0], cy = coords[1], rx1, ry1, rx2, ry2);
+ break;
+ case PathIterator.SEG_QUADTO:
+ count = intersectQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], rx1, ry1, rx2, ry2);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ count = intersectCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], rx1, ry1, rx2, ry2);
+ break;
+ case PathIterator.SEG_CLOSE:
+ if (cy != my || cx != mx) {
+ count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
+ }
+ cx = mx;
+ cy = my;
+ break;
+ }
+ if (count == CROSSING) {
+ return CROSSING;
+ }
+ cross += count;
+ p.next();
+ }
+ if (cy != my) {
+ count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2);
+ if (count == CROSSING) {
+ return CROSSING;
+ }
+ cross += count;
+ }
+ return cross;
+ }
+
+ /**
+ * Returns how many times rectangle stripe cross shape or the are intersect
+ */
+ public static int intersectShape(Shape s, double x, double y, double w, double h) {
+ if (!s.getBounds2D().intersects(x, y, w, h)) {
+ return 0;
+ }
+ return intersectPath(s.getPathIterator(null), x, y, w, h);
+ }
+
+ /**
+ * Returns true if cross count correspond inside location for non zero path rule
+ */
+ public static boolean isInsideNonZero(int cross) {
+ return cross != 0;
+ }
+
+ /**
+ * Returns true if cross count correspond inside location for even-odd path rule
+ */
+ public static boolean isInsideEvenOdd(int cross) {
+ return (cross & 1) != 0;
+ }
+} \ No newline at end of file
diff --git a/src/org/apache/harmony/misc/HashCode.java b/src/org/apache/harmony/misc/HashCode.java
new file mode 100644
index 000000000..e8ce8f620
--- /dev/null
+++ b/src/org/apache/harmony/misc/HashCode.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.misc;
+
+/**
+ * This class is a convenience method to sequentially calculate hash code of the
+ * object based on the field values. The result depends on the order of elements
+ * appended. The exact formula is the same as for
+ * <code>java.util.List.hashCode</code>.
+ *
+ * If you need order independent hash code just summate, multiply or XOR all
+ * elements.
+ *
+ * <p>
+ * Suppose we have class:
+ *
+ * <pre><code>
+ * class Thing {
+ * long id;
+ * String name;
+ * float weight;
+ * }
+ * </code></pre>
+ *
+ * The hash code calculation can be expressed in 2 forms.
+ *
+ * <p>
+ * For maximum performance:
+ *
+ * <pre><code>
+ * public int hashCode() {
+ * int hashCode = HashCode.EMPTY_HASH_CODE;
+ * hashCode = HashCode.combine(hashCode, id);
+ * hashCode = HashCode.combine(hashCode, name);
+ * hashCode = HashCode.combine(hashCode, weight);
+ * return hashCode;
+ * }
+ * </code></pre>
+ *
+ * <p>
+ * For convenience: <code><pre>
+ * public int hashCode() {
+ * return new HashCode().append(id).append(name).append(weight).hashCode();
+ * }
+ * </code></pre>
+ *
+ * @see java.util.List#hashCode()
+ */
+public final class HashCode {
+ /**
+ * The hashCode value before any data is appended, equals to 1.
+ * @see java.util.List#hashCode()
+ */
+ public static final int EMPTY_HASH_CODE = 1;
+
+ private int hashCode = EMPTY_HASH_CODE;
+
+ /**
+ * Returns accumulated hashCode
+ */
+ public final int hashCode() {
+ return hashCode;
+ }
+
+ /**
+ * Combines hashCode of previous elements sequence and value's hashCode.
+ * @param hashCode previous hashCode value
+ * @param value new element
+ * @return combined hashCode
+ */
+ public static int combine(int hashCode, boolean value) {
+ int v = value ? 1231 : 1237;
+ return combine(hashCode, v);
+ }
+
+ /**
+ * Combines hashCode of previous elements sequence and value's hashCode.
+ * @param hashCode previous hashCode value
+ * @param value new element
+ * @return combined hashCode
+ */
+ public static int combine(int hashCode, long value) {
+ int v = (int) (value ^ (value >>> 32));
+ return combine(hashCode, v);
+ }
+
+ /**
+ * Combines hashCode of previous elements sequence and value's hashCode.
+ * @param hashCode previous hashCode value
+ * @param value new element
+ * @return combined hashCode
+ */
+ public static int combine(int hashCode, float value) {
+ int v = Float.floatToIntBits(value);
+ return combine(hashCode, v);
+ }
+
+ /**
+ * Combines hashCode of previous elements sequence and value's hashCode.
+ * @param hashCode previous hashCode value
+ * @param value new element
+ * @return combined hashCode
+ */
+ public static int combine(int hashCode, double value) {
+ long v = Double.doubleToLongBits(value);
+ return combine(hashCode, v);
+ }
+
+ /**
+ * Combines hashCode of previous elements sequence and value's hashCode.
+ * @param hashCode previous hashCode value
+ * @param value new element
+ * @return combined hashCode
+ */
+ public static int combine(int hashCode, Object value) {
+ return combine(hashCode, value.hashCode());
+ }
+
+ /**
+ * Combines hashCode of previous elements sequence and value's hashCode.
+ * @param hashCode previous hashCode value
+ * @param value new element
+ * @return combined hashCode
+ */
+ public static int combine(int hashCode, int value) {
+ return 31 * hashCode + value;
+ }
+
+ /**
+ * Appends value's hashCode to the current hashCode.
+ * @param value new element
+ * @return this
+ */
+ public final HashCode append(int value) {
+ hashCode = combine(hashCode, value);
+ return this;
+ }
+
+ /**
+ * Appends value's hashCode to the current hashCode.
+ * @param value new element
+ * @return this
+ */
+ public final HashCode append(long value) {
+ hashCode = combine(hashCode, value);
+ return this;
+ }
+
+ /**
+ * Appends value's hashCode to the current hashCode.
+ * @param value new element
+ * @return this
+ */
+ public final HashCode append(float value) {
+ hashCode = combine(hashCode, value);
+ return this;
+ }
+
+ /**
+ * Appends value's hashCode to the current hashCode.
+ * @param value new element
+ * @return this
+ */
+ public final HashCode append(double value) {
+ hashCode = combine(hashCode, value);
+ return this;
+ }
+
+ /**
+ * Appends value's hashCode to the current hashCode.
+ * @param value new element
+ * @return this
+ */
+ public final HashCode append(boolean value) {
+ hashCode = combine(hashCode, value);
+ return this;
+ }
+
+ /**
+ * Appends value's hashCode to the current hashCode.
+ * @param value new element
+ * @return this
+ */
+ public final HashCode append(Object value) {
+ hashCode = combine(hashCode, value);
+ return this;
+ }
+}