aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/graph/geom/plane
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-02-23 14:51:06 +0100
committerSven Gothel <[email protected]>2014-02-23 14:51:06 +0100
commit3352601e0860584509adf2b76f993d03893ded4b (patch)
tree974fccc8c0eb2f5ad9d4ffd741dfc35869ed67b5 /src/jogl/classes/jogamp/graph/geom/plane
parentf51933f0ebe9ae030c26c066e59a728ce08b8559 (diff)
parentc67de337a8aaf52e36104c3f13e273aa19d21f1f (diff)
Merge branch 'master' into stash_glyphcache
Conflicts: make/scripts/tests.sh src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java src/jogl/classes/com/jogamp/graph/curve/Region.java src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java src/jogl/classes/com/jogamp/graph/curve/opengl/TextRenderer.java src/jogl/classes/com/jogamp/graph/font/Font.java src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java src/jogl/classes/jogamp/graph/curve/text/GlyphShape.java src/jogl/classes/jogamp/graph/curve/text/GlyphString.java src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
Diffstat (limited to 'src/jogl/classes/jogamp/graph/geom/plane')
-rw-r--r--src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java66
-rw-r--r--src/jogl/classes/jogamp/graph/geom/plane/Crossing.java902
-rw-r--r--src/jogl/classes/jogamp/graph/geom/plane/Path2D.java50
3 files changed, 962 insertions, 56 deletions
diff --git a/src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java b/src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java
index fc086ebe4..32e2b6a39 100644
--- a/src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java
+++ b/src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java
@@ -22,18 +22,18 @@ package jogamp.graph.geom.plane;
import java.io.IOException;
import java.io.Serializable;
-import jogamp.graph.math.MathFloat;
// import jogamp.opengl.util.HashCode;
import com.jogamp.graph.geom.Vertex;
import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.opengl.math.FloatUtil;
public class AffineTransform implements Cloneable, Serializable {
private static final long serialVersionUID = 1330973210523860834L;
static final String determinantIsZero = "Determinant is zero";
-
+
public static final int TYPE_IDENTITY = 0;
public static final int TYPE_TRANSLATION = 1;
public static final int TYPE_UNIFORM_SCALE = 2;
@@ -49,14 +49,14 @@ public class AffineTransform implements Cloneable, Serializable {
* 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.
+ * The min value equivalent to zero. If absolute value less then ZERO it considered as zero.
*/
static final float ZERO = (float) 1E-10;
-
+
private final Vertex.Factory<? extends Vertex> pointFactory;
-
+
/**
* The values of transformation matrix
*/
@@ -68,7 +68,7 @@ public class AffineTransform implements Cloneable, Serializable {
float m12;
/**
- * The transformation <code>type</code>
+ * The transformation <code>type</code>
*/
transient int type;
@@ -123,20 +123,20 @@ public class AffineTransform implements Cloneable, Serializable {
/*
* 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:
+ *
+ * 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_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_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_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
*/
@@ -285,13 +285,13 @@ public class AffineTransform implements Cloneable, Serializable {
}
public void setToRotation(float angle) {
- float sin = MathFloat.sin(angle);
- float cos = MathFloat.cos(angle);
- if (MathFloat.abs(cos) < ZERO) {
+ float sin = FloatUtil.sin(angle);
+ float cos = FloatUtil.cos(angle);
+ if (FloatUtil.abs(cos) < ZERO) {
cos = 0.0f;
sin = sin > 0.0f ? 1.0f : -1.0f;
} else
- if (MathFloat.abs(sin) < ZERO) {
+ if (FloatUtil.abs(sin) < ZERO) {
sin = 0.0f;
cos = cos > 0.0f ? 1.0f : -1.0f;
}
@@ -322,7 +322,7 @@ public class AffineTransform implements Cloneable, Serializable {
}
public static <T extends Vertex> AffineTransform getShearInstance(Vertex.Factory<? extends Vertex> factory, float shx, float shy) {
- AffineTransform t = new AffineTransform(factory);
+ AffineTransform t = new AffineTransform(factory);
t.setToShear(shx, shy);
return t;
}
@@ -359,13 +359,13 @@ public class AffineTransform implements Cloneable, Serializable {
concatenate(AffineTransform.getRotateInstance(pointFactory, angle, px, py));
}
- /**
+ /**
* Multiply matrix of two AffineTransform objects.
* The first argument's {@link Vertex.Factory} is being used.
- *
+ *
* @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.
+ * @return an AffineTransform object that is a result of t1 multiplied by matrix t2.
*/
AffineTransform multiply(AffineTransform t1, AffineTransform t2) {
return new AffineTransform(t1.pointFactory,
@@ -387,7 +387,7 @@ public class AffineTransform implements Cloneable, Serializable {
public AffineTransform createInverse() throws NoninvertibleTransformException {
float det = getDeterminant();
- if (MathFloat.abs(det) < ZERO) {
+ if (FloatUtil.abs(det) < ZERO) {
throw new NoninvertibleTransformException(determinantIsZero);
}
return new AffineTransform(
@@ -415,7 +415,7 @@ public class AffineTransform implements Cloneable, Serializable {
public void transform(Vertex[] src, int srcOff, Vertex[] dst, int dstOff, int length) {
while (--length >= 0) {
- Vertex srcPoint = src[srcOff++];
+ Vertex srcPoint = src[srcOff++];
float x = srcPoint.getX();
float y = srcPoint.getY();
Vertex dstPoint = dst[dstOff];
@@ -426,7 +426,7 @@ public class AffineTransform implements Cloneable, Serializable {
dst[dstOff++] = dstPoint;
}
}
-
+
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) {
@@ -443,7 +443,7 @@ public class AffineTransform implements Cloneable, Serializable {
dstOff += step;
}
}
-
+
public Vertex deltaTransform(Vertex src, Vertex dst) {
if (dst == null) {
dst = pointFactory.create();
@@ -467,7 +467,7 @@ public class AffineTransform implements Cloneable, Serializable {
public Vertex inverseTransform(Vertex src, Vertex dst) throws NoninvertibleTransformException {
float det = getDeterminant();
- if (MathFloat.abs(det) < ZERO) {
+ if (FloatUtil.abs(det) < ZERO) {
throw new NoninvertibleTransformException(determinantIsZero);
}
if (dst == null) {
@@ -485,8 +485,8 @@ public class AffineTransform implements Cloneable, Serializable {
throws NoninvertibleTransformException
{
float det = getDeterminant();
- if (MathFloat.abs(det) < ZERO) {
- throw new NoninvertibleTransformException(determinantIsZero);
+ if (FloatUtil.abs(det) < ZERO) {
+ throw new NoninvertibleTransformException(determinantIsZero);
}
while (--length >= 0) {
@@ -554,7 +554,7 @@ public class AffineTransform implements Cloneable, Serializable {
return false;
}
-
+
/**
* Write AffineTrasform object to the output steam.
* @param stream - the output stream
@@ -564,12 +564,12 @@ public class AffineTransform implements Cloneable, Serializable {
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
+ * @throws ClassNotFoundException - if class could not be found
*/
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
diff --git a/src/jogl/classes/jogamp/graph/geom/plane/Crossing.java b/src/jogl/classes/jogamp/graph/geom/plane/Crossing.java
new file mode 100644
index 000000000..4ee0c250d
--- /dev/null
+++ b/src/jogl/classes/jogamp/graph/geom/plane/Crossing.java
@@ -0,0 +1,902 @@
+/*
+ * 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 jogamp.graph.geom.plane;
+
+import com.jogamp.opengl.math.FloatUtil;
+
+
+
+public class Crossing {
+
+ /**
+ * Allowable tolerance for bounds comparison
+ */
+ static final float DELTA = (float) 1E-5;
+
+ /**
+ * If roots have distance less then <code>ROOT_DELTA</code> they are double
+ */
+ static final float ROOT_DELTA = (float) 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(float eqn[], float res[]) {
+ float a = eqn[2];
+ float b = eqn[1];
+ float c = eqn[0];
+ int rc = 0;
+ if (a == 0.0) {
+ if (b == 0.0) {
+ return -1;
+ }
+ res[rc++] = -c / b;
+ } else {
+ float d = b * b - 4.0f * a * c;
+ // d < 0.0
+ if (d < 0.0) {
+ return 0;
+ }
+ d = FloatUtil.sqrt(d);
+ res[rc++] = (- b + d) / (a * 2.0f);
+ // d != 0.0
+ if (d != 0.0) {
+ res[rc++] = (- b - d) / (a * 2.0f);
+ }
+ }
+ 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(float eqn[], float res[]) {
+ float d = eqn[3];
+ if (d == 0) {
+ return solveQuad(eqn, res);
+ }
+ float a = eqn[2] / d;
+ float b = eqn[1] / d;
+ float c = eqn[0] / d;
+ int rc = 0;
+
+ float Q = (a * a - 3.0f * b) / 9.0f;
+ float R = (2.0f * a * a * a - 9.0f * a * b + 27.0f * c) / 54.0f;
+ float Q3 = Q * Q * Q;
+ float R2 = R * R;
+ float n = - a / 3.0f;
+
+ if (R2 < Q3) {
+ float t = FloatUtil.acos(R / FloatUtil.sqrt(Q3)) / 3.0f;
+ float p = 2.0f * FloatUtil.PI / 3.0f;
+ float m = -2.0f * FloatUtil.sqrt(Q);
+ res[rc++] = m * FloatUtil.cos(t) + n;
+ res[rc++] = m * FloatUtil.cos(t + p) + n;
+ res[rc++] = m * FloatUtil.cos(t - p) + n;
+ } else {
+// Debug.println("R2 >= Q3 (" + R2 + "/" + Q3 + ")");
+ float A = FloatUtil.pow(FloatUtil.abs(R) + FloatUtil.sqrt(R2 - Q3), 1.0f / 3.0f);
+ if (R > 0.0) {
+ A = -A;
+ }
+// if (A == 0.0) {
+ if (-ROOT_DELTA < A && A < ROOT_DELTA) {
+ res[rc++] = n;
+ } else {
+ float B = Q / A;
+ res[rc++] = A + B + n;
+// if (R2 == Q3) {
+ float delta = R2 - Q3;
+ if (-ROOT_DELTA < delta && delta < ROOT_DELTA) {
+ res[rc++] = - (A + B) / 2.0f + n;
+ }
+ }
+
+ }
+ return fixRoots(res, rc);
+ }
+
+ /**
+ * Excludes float roots. Roots are float if they lies enough close with each other.
+ * @param res - the roots
+ * @param rc - the roots count
+ * @return new roots count
+ */
+ static int fixRoots(float 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 {
+
+ float ax, ay, bx, by;
+ float Ax, Ay, Bx, By;
+
+ public QuadCurve(float x1, float y1, float cx, float cy, float x2, float 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(float res[], int rc, float py1, float py2) {
+ int cross = 0;
+
+ for (int i = 0; i < rc; i++) {
+ float 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
+ float ry = t * (t * Ay + By);
+ // ry = t * t * Ay + t * By
+ if (ry > py2) {
+ float 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(float res[], float px) {
+ float eqn[] = {-px, Bx, Ax};
+ return solveQuad(eqn, res);
+ }
+
+ int solveExtrem(float 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(float bound[], int bc, float res[], int rc, float minX, float maxX, boolean changeId, int id) {
+ for(int i = 0; i < rc; i++) {
+ float t = res[i];
+ if (t > -DELTA && t < 1 + DELTA) {
+ float 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 {
+
+ float ax, ay, bx, by, cx, cy;
+ float Ax, Ay, Bx, By, Cx, Cy;
+ float Ax3, Bx2;
+
+ public CubicCurve(float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float 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(float res[], int rc, float py1, float py2) {
+ int cross = 0;
+ for (int i = 0; i < rc; i++) {
+ float 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
+ float ry = t * (t * (t * Ay + By) + Cy);
+ // ry = t * t * t * Ay + t * t * By + t * Cy
+ if (ry > py2) {
+ float 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(float res[], float px) {
+ float eqn[] = {-px, Cx, Bx, Ax};
+ return solveCubic(eqn, res);
+ }
+
+ int solveExtremX(float res[]) {
+ float eqn[] = {Cx, Bx2, Ax3};
+ return solveQuad(eqn, res);
+ }
+
+ int solveExtremY(float res[]) {
+ float eqn[] = {Cy, By + By, Ay + Ay + Ay};
+ return solveQuad(eqn, res);
+ }
+
+ int addBound(float bound[], int bc, float res[], int rc, float minX, float maxX, boolean changeId, int id) {
+ for(int i = 0; i < rc; i++) {
+ float t = res[i];
+ if (t > -DELTA && t < 1 + DELTA) {
+ float 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(float x1, float y1, float x2, float y2, float x, float 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(float x1, float y1, float cx, float cy, float x2, float y2, float x, float 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);
+ float px = x - x1;
+ float py = y - y1;
+ float res[] = new float[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(float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float x, float 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);
+ float px = x - x1;
+ float py = y - y1;
+ float res[] = new float[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, float x, float y) {
+ int cross = 0;
+ float mx, my, cx, cy;
+ mx = my = cx = cy = 0.0f;
+ final float coords[] = new float[6];
+
+ while (!p.isDone()) {
+ final int segmentType = p.currentSegment(coords);
+ switch (segmentType) {
+ 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;
+ default:
+ throw new IllegalArgumentException("Unhandled Segment Type: "+segmentType);
+ }
+
+ // 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(Path2D s, float x, float y) {
+ if (!s.getBounds2D().contains(x, y)) {
+ return 0;
+ }
+ return crossPath(s.iterator(null), x, y);
+ }
+
+ /**
+ * Returns true if value enough small
+ */
+ public static boolean isZero(float val) {
+ return -DELTA < val && val < DELTA;
+ }
+
+ /**
+ * Sort bound array
+ */
+ static void sortBound(float 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) {
+ float 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(float bound[], int bc, float py1, float 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(float x1, float y1, float x2, float y2, float rx1, float ry1, float rx2, float 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
+ float 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;
+ }
+ float k = (y2 - y1) / (x2 - x1);
+ float by1 = k * (bx1 - x1) + y1;
+ float 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(float x1, float y1, float cx, float cy, float x2, float y2, float rx1, float ry1, float rx2, float 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);
+ float px1 = rx1 - x1;
+ float py1 = ry1 - y1;
+ float px2 = rx2 - x1;
+ float py2 = ry2 - y1;
+
+ float res1[] = new float[3];
+ float res2[] = new float[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 --------------------------------------------------------
+ float minX = px1 - DELTA;
+ float maxX = px2 + DELTA;
+ float bound[] = new float[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.0f;
+ bound[bc++] = 0.0f;
+ bound[bc++] = 0.0f;
+ bound[bc++] = 4;
+ }
+ if (rx1 < x2 && x2 < rx2) {
+ bound[bc++] = 1.0f;
+ 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(float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float rx1, float ry1, float rx2, float 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);
+ float px1 = rx1 - x1;
+ float py1 = ry1 - y1;
+ float px2 = rx2 - x1;
+ float py2 = ry2 - y1;
+
+ float res1[] = new float[3];
+ float res2[] = new float[3];
+ int rc1 = c.solvePoint(res1, px1);
+ int rc2 = c.solvePoint(res2, px2);
+
+ // LEFT/RIGHT
+ if (rc1 == 0 && rc2 == 0) {
+ return 0;
+ }
+
+ float minX = px1 - DELTA;
+ float maxX = px2 + DELTA;
+
+ // Build bound --------------------------------------------------------
+ float bound[] = new float[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.0f;
+ bound[bc++] = 0.0f;
+ bound[bc++] = 0.0f;
+ bound[bc++] = 6;
+ }
+ if (rx1 < x2 && x2 < rx2) {
+ bound[bc++] = 1.0f;
+ 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, float x, float y, float w, float h) {
+
+ int cross = 0;
+ int count;
+ float mx, my, cx, cy;
+ mx = my = cx = cy = 0.0f;
+ final float coords[] = new float[6];
+
+ float rx1 = x;
+ float ry1 = y;
+ float rx2 = x + w;
+ float ry2 = y + h;
+
+ while (!p.isDone()) {
+ count = 0;
+ final int segmentType = p.currentSegment(coords);
+ switch (segmentType) {
+ 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;
+ default:
+ throw new IllegalArgumentException("Unhandled Segment Type: "+segmentType);
+ }
+ 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(Path2D s, float x, float y, float w, float h) {
+ if (!s.getBounds2D().intersects(x, y, w, h)) {
+ return 0;
+ }
+ return intersectPath(s.iterator(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;
+ }
+}
diff --git a/src/jogl/classes/jogamp/graph/geom/plane/Path2D.java b/src/jogl/classes/jogamp/graph/geom/plane/Path2D.java
index 8082fe4e1..33b80d6b8 100644
--- a/src/jogl/classes/jogamp/graph/geom/plane/Path2D.java
+++ b/src/jogl/classes/jogamp/graph/geom/plane/Path2D.java
@@ -21,11 +21,10 @@ package jogamp.graph.geom.plane;
import java.util.NoSuchElementException;
-import com.jogamp.graph.geom.AABBox;
import com.jogamp.graph.geom.Vertex;
import com.jogamp.graph.geom.opengl.SVertex;
+import com.jogamp.opengl.math.geom.AABBox;
-import jogamp.graph.math.plane.Crossing;
public final class Path2D implements Cloneable {
@@ -34,12 +33,12 @@ public final class Path2D implements Cloneable {
static final String invalidWindingRuleValue = "Invalid winding rule value";
static final String iteratorOutOfBounds = "Iterator out of bounds";
-
+
/**
* The buffers size
*/
private static final int BUFFER_SIZE = 10;
-
+
/**
* The buffers capacity
*/
@@ -49,24 +48,24 @@ public final class Path2D implements Cloneable {
* 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
+ * The path rule
*/
int rule;
@@ -81,7 +80,7 @@ public final class Path2D implements Cloneable {
0}; // CLOSE
/*
- * GeneralPath path iterator
+ * GeneralPath path iterator
*/
class Iterator implements PathIterator {
@@ -89,17 +88,17 @@ public final class Path2D implements Cloneable {
* The current cursor position in types buffer
*/
int typeIndex;
-
+
/**
* The current cursor position in points buffer
*/
int pointIndex;
-
+
/**
* The source GeneralPath object
*/
Path2D p;
-
+
/**
* The path iterator transformation
*/
@@ -123,18 +122,22 @@ public final class Path2D implements Cloneable {
this.t = at;
}
+ @Override
public int getWindingRule() {
return p.getWindingRule();
}
+ @Override
public boolean isDone() {
return typeIndex >= p.typeSize;
}
+ @Override
public void next() {
typeIndex++;
}
+ @Override
public int currentSegment(float[] coords) {
if (isDone()) {
throw new NoSuchElementException(iteratorOutOfBounds);
@@ -184,7 +187,7 @@ public final class Path2D implements Cloneable {
}
/**
- * Checks points and types buffer size to add pointCount points. If necessary realloc buffers to enlarge size.
+ * 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) {
@@ -245,18 +248,19 @@ public final class Path2D implements Cloneable {
final public int size() {
return typeSize;
}
-
+
final public boolean isClosed() {
return typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_CLOSE ;
}
-
+
public void closePath() {
if (!isClosed()) {
checkBuf(0, true);
types[typeSize++] = PathIterator.SEG_CLOSE;
}
}
-
+
+ @Override
public String toString() {
return "[size "+size()+", closed "+isClosed()+"]";
}
@@ -296,7 +300,7 @@ public final class Path2D implements Cloneable {
closePath();
break;
default:
- throw new IllegalArgumentException("Unhandled Segment Type: "+segmentType);
+ throw new IllegalArgumentException("Unhandled Segment Type: "+segmentType);
}
path.next();
connect = false;
@@ -367,9 +371,9 @@ public final class Path2D implements Cloneable {
}
/**
- * Checks cross count according to path rule to define is it point inside shape or not.
+ * 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
+ * @return true if point is inside path, or false otherwise
*/
boolean isInside(int cross) {
if (rule == WIND_NON_ZERO) {
@@ -397,7 +401,7 @@ public final class Path2D implements Cloneable {
}
public boolean contains(AABBox r) {
- return contains(r);
+ return contains(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
}
public boolean intersects(AABBox r) {
@@ -405,9 +409,9 @@ public final class Path2D implements Cloneable {
}
public PathIterator iterator() {
- return new Iterator(this);
+ return new Iterator(this);
}
-
+
public PathIterator iterator(AffineTransform t) {
return new Iterator(this, t);
}