diff options
author | Sven Gothel <[email protected]> | 2023-09-20 19:51:55 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-09-20 19:51:55 +0200 |
commit | 5d6e8a367c03644740187e500c6de5d3ac039d5e (patch) | |
tree | a649f559413c51272ee3f4afff1f68ebfea45477 /src/jogl/classes/com/jogamp/graph/geom | |
parent | bbe845846ffc00807395a5070a7352c6bbe7e4ef (diff) |
Bug 1452 - Decouple math functionality to 'com.jogamp.math' to be toolkit agnostic (PMVMatrix, Matrix4f, Vec4f, ..)
Math functionality (PMVMatrix, Matrix4f, Vec4f, ..)
- shall be used toolkit agnostic, e.g. independent from OpenGL
- shall be reused within our upcoming Vulkan implementation
- may also move outside of JOGL, i.e. GlueGen or within its own package to be reused for other purposed.
The 'com.jogamp.opengl.util.PMVMatrix' currently also used to feed in GLUniformData
via the toolkit agnostic SyncAction and SyncBuffer
shall also be split to a toolkit agnostic variant.
An OpenGL PMVMatrix specialization implementing GLMatrixFunc can still exist,
being derived from the toolkit agnostic base implementation.
+++
Initial commit .. compile clean, passing most unit tests.
Diffstat (limited to 'src/jogl/classes/com/jogamp/graph/geom')
10 files changed, 10 insertions, 2247 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 654bd2636..272f92f37 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -29,13 +29,13 @@ package com.jogamp.graph.geom; import java.util.ArrayList; -import com.jogamp.graph.geom.plane.AffineTransform; -import com.jogamp.graph.geom.plane.Winding; +import com.jogamp.math.FloatUtil; +import com.jogamp.math.VectorUtil; +import com.jogamp.math.geom.AABBox; +import com.jogamp.math.geom.plane.AffineTransform; +import com.jogamp.math.geom.plane.Winding; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; -import com.jogamp.opengl.math.FloatUtil; -import com.jogamp.opengl.math.VectorUtil; -import com.jogamp.opengl.math.geom.AABBox; diff --git a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java index 6b07501a6..e26d2aaf1 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java @@ -27,8 +27,8 @@ */ package com.jogamp.graph.geom; -import com.jogamp.graph.geom.plane.AffineTransform; -import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.math.VectorUtil; +import com.jogamp.math.geom.plane.AffineTransform; public class Triangle { private final Vertex[] vertices = new Vertex[3]; diff --git a/src/jogl/classes/com/jogamp/graph/geom/Vertex.java b/src/jogl/classes/com/jogamp/graph/geom/Vertex.java index e5fe76c28..8744474d9 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Vertex.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Vertex.java @@ -27,9 +27,9 @@ */ package com.jogamp.graph.geom; -import com.jogamp.opengl.math.Vec2f; -import com.jogamp.opengl.math.Vec3f; -import com.jogamp.opengl.math.Vert3fImmutable; +import com.jogamp.math.Vec2f; +import com.jogamp.math.Vec3f; +import com.jogamp.math.Vert3fImmutable; /** * A Vertex exposing Vec3f vertex- and texture-coordinates. diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java b/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java deleted file mode 100644 index 66d661990..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java +++ /dev/null @@ -1,625 +0,0 @@ -/* - * 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 - * @author Sven Gothel, (c) 2010-2023 - */ -package com.jogamp.graph.geom.plane; - -// import jogamp.opengl.util.HashCode; - -import com.jogamp.graph.geom.Vertex; -import com.jogamp.opengl.math.FloatUtil; -import com.jogamp.opengl.math.Vec2f; -import com.jogamp.opengl.math.Vec3f; -import com.jogamp.opengl.math.geom.AABBox; - -public class AffineTransform implements Cloneable { - - 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; - 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 float ZERO = (float) 1E-10; - - /** - * The values of transformation matrix - */ - private float m00; - private float m10; - private float m01; - private float m11; - private float m02; - private float m12; - - /** - * The transformation <code>type</code> - */ - private transient int type; - - public AffineTransform() { - setToIdentity(); - } - - public AffineTransform(final 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(final float m00, final float m10, final float m01, final float m11, final float m02, final 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(final 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]; - } - } - - /* - * 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; - } - - final float dx = m00 * m00 + m10 * m10; - final float 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 final float getScaleX() { - return m00; - } - - public final float getScaleY() { - return m11; - } - - public final float getShearX() { - return m01; - } - - public final float getShearY() { - return m10; - } - - public final float getTranslateX() { - return m02; - } - - public final float getTranslateY() { - return m12; - } - - public final boolean isIdentity() { - return getType() == TYPE_IDENTITY; - } - - public final void getMatrix(final float[] matrix) { - matrix[0] = m00; - matrix[1] = m10; - matrix[2] = m01; - matrix[3] = m11; - if (matrix.length > 4) { - matrix[4] = m02; - matrix[5] = m12; - } - } - - public final float getDeterminant() { - return m00 * m11 - m01 * m10; - } - - public final AffineTransform setTransform(final float m00, final float m10, final float m01, final float m11, final float m02, final float m12) { - this.type = TYPE_UNKNOWN; - this.m00 = m00; - this.m10 = m10; - this.m01 = m01; - this.m11 = m11; - this.m02 = m02; - this.m12 = m12; - return this; - } - - public final AffineTransform setTransform(final AffineTransform t) { - type = t.type; - setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12); - return this; - } - - public final AffineTransform setToIdentity() { - type = TYPE_IDENTITY; - m00 = m11 = 1.0f; - m10 = m01 = m02 = m12 = 0.0f; - return this; - } - - public final AffineTransform setToTranslation(final float mx, final float my) { - m00 = m11 = 1.0f; - m01 = m10 = 0.0f; - m02 = mx; - m12 = my; - if (mx == 0.0f && my == 0.0f) { - type = TYPE_IDENTITY; - } else { - type = TYPE_TRANSLATION; - } - return this; - } - - public final AffineTransform setToScale(final float scx, final float scy) { - m00 = scx; - m11 = scy; - m10 = m01 = m02 = m12 = 0.0f; - if (scx != 1.0f || scy != 1.0f) { - type = TYPE_UNKNOWN; - } else { - type = TYPE_IDENTITY; - } - return this; - } - - public final AffineTransform setToShear(final float shx, final float shy) { - m00 = m11 = 1.0f; - m02 = m12 = 0.0f; - m01 = shx; - m10 = shy; - if (shx != 0.0f || shy != 0.0f) { - type = TYPE_UNKNOWN; - } else { - type = TYPE_IDENTITY; - } - return this; - } - - public final AffineTransform setToRotation(final float angle) { - float sin = FloatUtil.sin(angle); - float cos = FloatUtil.cos(angle); - if (Math.abs(cos) < ZERO) { - cos = 0.0f; - sin = sin > 0.0f ? 1.0f : -1.0f; - } else - if (Math.abs(sin) < ZERO) { - sin = 0.0f; - cos = cos > 0.0f ? 1.0f : -1.0f; - } - m00 = m11 = cos; - m01 = -sin; - m10 = sin; - m02 = m12 = 0.0f; - type = TYPE_UNKNOWN; - return this; - } - - public final AffineTransform setToRotation(final float angle, final float px, final float py) { - setToRotation(angle); - m02 = px * (1.0f - m00) + py * m10; - m12 = py * (1.0f - m00) - px * m10; - type = TYPE_UNKNOWN; - return this; - } - - public final AffineTransform translate(final float mx, final float my, final AffineTransform tmp) { - return concatenate(tmp.setToTranslation(mx, my)); - } - - public final AffineTransform scale(final float scx, final float scy, final AffineTransform tmp) { - return concatenate(tmp.setToScale(scx, scy)); - } - - public final AffineTransform shear(final float shx, final float shy, final AffineTransform tmp) { - return concatenate(tmp.setToShear(shx, shy)); - } - - public final AffineTransform rotate(final float angle, final AffineTransform tmp) { - return concatenate(tmp.setToRotation(angle)); - } - - public final AffineTransform rotate(final float angle, final float px, final float py, final AffineTransform tmp) { - return concatenate(tmp.setToRotation(angle, px, py)); - } - - /** - * Multiply matrix of two AffineTransform objects. - * @param tL - the AffineTransform object is a multiplicand (left argument) - * @param tR - the AffineTransform object is a multiplier (right argument) - * - * @return A new AffineTransform object containing the result of [tL] X [tR]. - */ - public final static AffineTransform multiply(final AffineTransform tL, final AffineTransform tR) { - return new AffineTransform( - tR.m00 * tL.m00 + tR.m10 * tL.m01, // m00 - tR.m00 * tL.m10 + tR.m10 * tL.m11, // m10 - tR.m01 * tL.m00 + tR.m11 * tL.m01, // m01 - tR.m01 * tL.m10 + tR.m11 * tL.m11, // m11 - tR.m02 * tL.m00 + tR.m12 * tL.m01 + tL.m02, // m02 - tR.m02 * tL.m10 + tR.m12 * tL.m11 + tL.m12);// m12 - } - - /** - * Concatenates the given matrix to this. - * <p> - * Implementations performs the matrix multiplication: - * <pre> - * [this] = [this] X [tR] - * </pre> - * </p> - * @param tR the right-argument of the matrix multiplication - * @return this transform for chaining - */ - public final AffineTransform concatenate(final AffineTransform tR) { - // setTransform(multiply(this, tR)); - type = TYPE_UNKNOWN; - setTransform( - tR.m00 * m00 + tR.m10 * m01, // m00 - tR.m00 * m10 + tR.m10 * m11, // m10 - tR.m01 * m00 + tR.m11 * m01, // m01 - tR.m01 * m10 + tR.m11 * m11, // m11 - tR.m02 * m00 + tR.m12 * m01 + m02, // m02 - tR.m02 * m10 + tR.m12 * m11 + m12);// m12 - return this; - } - - /** - * Pre-concatenates the given matrix to this. - * <p> - * Implementations performs the matrix multiplication: - * <pre> - * [this] = [tL] X [this] - * </pre> - * </p> - * @param tL the left-argument of the matrix multiplication - * @return this transform for chaining - */ - public final AffineTransform preConcatenate(final AffineTransform tL) { - // setTransform(multiply(tL, this)); - type = TYPE_UNKNOWN; - setTransform( - m00 * tL.m00 + m10 * tL.m01, // m00 - m00 * tL.m10 + m10 * tL.m11, // m10 - m01 * tL.m00 + m11 * tL.m01, // m01 - m01 * tL.m10 + m11 * tL.m11, // m11 - m02 * tL.m00 + m12 * tL.m01 + tL.m02, // m02 - m02 * tL.m10 + m12 * tL.m11 + tL.m12);// m12 - return this; - } - - public final AffineTransform createInverse() throws NoninvertibleTransformException { - final float det = getDeterminant(); - if (Math.abs(det) < ZERO) { - throw new NoninvertibleTransformException(determinantIsZero); - } - 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 - ); - } - - /** - * - * @param src - * @param dst - * @return dst for chaining - */ - public final AABBox transform(final AABBox src, final AABBox dst) { - final Vec3f lo = src.getLow(); - final Vec3f hi = src.getHigh(); - dst.setSize(lo.x() * m00 + lo.y() * m01 + m02, lo.x() * m10 + lo.y() * m11 + m12, lo.z(), - hi.x() * m00 + hi.y() * m01 + m02, hi.x() * m10 + hi.y() * m11 + m12, hi.z()); - return dst; - } - - /** - * @param src - * @param dst - * @return dst for chaining - */ - public final Vertex transform(final Vertex src, final Vertex dst) { - final float x = src.x(); - final float y = src.y(); - dst.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, src.z()); - return dst; - } - - public final void transform(final Vertex[] src, int srcOff, final Vertex[] dst, int dstOff, int length) { - while (--length >= 0) { - final Vertex srcPoint = src[srcOff++]; - final Vertex dstPoint = dst[dstOff]; - if (dstPoint == null) { - throw new IllegalArgumentException("dst["+dstOff+"] is null"); - } - final float x = srcPoint.x(); - final float y = srcPoint.y(); - dstPoint.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, srcPoint.z()); - dst[dstOff++] = dstPoint; - } - } - - /** - * @param src float[2] source of transformation - * @param dst float[2] destination of transformation, maybe be equal to <code>src</code> - * @return dst for chaining - */ - public final float[] transform(final float[] src, final float[] dst) { - final float x = src[0]; - final float y = src[1]; - dst[0] = x * m00 + y * m01 + m02; - dst[1] = x * m10 + y * m11 + m12; - return dst; - } - - public final void transform(final float[] src, final int srcOff, final float[] dst, final int dstOff) { - final float x = src[srcOff + 0]; - final float y = src[srcOff + 1]; - dst[dstOff + 0] = x * m00 + y * m01 + m02; - dst[dstOff + 1] = x * m10 + y * m11 + m12; - } - - public final void transform(final float[] src, int srcOff, final 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) { - final float x = src[srcOff + 0]; - final float y = src[srcOff + 1]; - dst[dstOff + 0] = x * m00 + y * m01 + m02; - dst[dstOff + 1] = x * m10 + y * m11 + m12; - srcOff += step; - dstOff += step; - } - } - - /** - * @param src source of transformation - * @param dst destination of transformation, maybe be equal to <code>src</code> - * @return dst for chaining - */ - public final Vec2f transform(final Vec2f src, final Vec2f dst) { - final float x = src.x(); - final float y = src.y(); - dst.setX( x * m00 + y * m01 + m02 ); - dst.setY( x * m10 + y * m11 + m12 ); - return dst; - } - - /** - * @param src source of transformation - * @param dst destination of transformation, maybe be equal to <code>src</code> - * @return dst for chaining - */ - public final Vec3f transform(final Vec3f src, final Vec3f dst) { - final float x = src.x(); - final float y = src.y(); - dst.setX( x * m00 + y * m01 + m02 ); - dst.setY( x * m10 + y * m11 + m12 ); - dst.setZ( src.z() ); // just copy z - return dst; - } - - /** - * - * @param src - * @param dst - * @return return dst for chaining - */ - public final Vertex deltaTransform(final Vertex src, final Vertex dst) { - final float x = src.x(); - final float y = src.y(); - dst.setCoord(x * m00 + y * m01, x * m10 + y * m11, src.z()); - return dst; - } - - public final void deltaTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length) { - while (--length >= 0) { - final float x = src[srcOff++]; - final float y = src[srcOff++]; - dst[dstOff++] = x * m00 + y * m01; - dst[dstOff++] = x * m10 + y * m11; - } - } - - /** - * - * @param src - * @param dst - * @return return dst for chaining - * @throws NoninvertibleTransformException - */ - public final Vertex inverseTransform(final Vertex src, final Vertex dst) throws NoninvertibleTransformException { - final float det = getDeterminant(); - if (Math.abs(det) < ZERO) { - throw new NoninvertibleTransformException(determinantIsZero); - } - final float x = src.x() - m02; - final float y = src.y() - m12; - dst.setCoord((x * m11 - y * m01) / det, (y * m00 - x * m10) / det, src.z()); - return dst; - } - - public final void inverseTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length) - throws NoninvertibleTransformException - { - final float det = getDeterminant(); - if (Math.abs(det) < ZERO) { - throw new NoninvertibleTransformException(determinantIsZero); - } - - while (--length >= 0) { - final float x = src[srcOff++] - m02; - final float y = src[srcOff++] - m12; - dst[dstOff++] = (x * m11 - y * m01) / det; - dst[dstOff++] = (y * m00 - x * m10) / det; - } - } - - public final Path2F createTransformedShape(final Path2F src) { - if (src == null) { - return null; - } - return src.createTransformedShape(this); - /** - * If !(src instanceof Path2D): (but here it always is) - final PathIterator path = src.iterator(this); - final Path2D dst = new Path2D(path.getWindingRule()); - dst.append(path, false); - return dst; - */ - } - - @Override - public final 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 final AffineTransform clone() { - try { - return (AffineTransform) super.clone(); - } catch (final 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 final boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (obj instanceof AffineTransform) { - final 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; - } - @Override - public final int hashCode() { - throw new InternalError("hashCode not designed"); - } -} - diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/Crossing2F.java b/src/jogl/classes/com/jogamp/graph/geom/plane/Crossing2F.java deleted file mode 100644 index 7eb1d0bf0..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/Crossing2F.java +++ /dev/null @@ -1,898 +0,0 @@ -/* - * 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 com.jogamp.graph.geom.plane; - -import com.jogamp.opengl.math.FloatUtil; - -/* pp */ class Crossing2F { - - /** - * 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(final float eqn[], final float res[]) { - final float a = eqn[2]; - final float b = eqn[1]; - final 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(final float eqn[], final float res[]) { - final float d = eqn[3]; - if (d == 0) { - return solveQuad(eqn, res); - } - final float a = eqn[2] / d; - final float b = eqn[1] / d; - final float c = eqn[0] / d; - int rc = 0; - - final float Q = (a * a - 3.0f * b) / 9.0f; - final float R = (2.0f * a * a * a - 9.0f * a * b + 27.0f * c) / 54.0f; - final float Q3 = Q * Q * Q; - final float R2 = R * R; - final float n = - a / 3.0f; - - if (R2 < Q3) { - final float t = FloatUtil.acos(R / FloatUtil.sqrt(Q3)) / 3.0f; - final float p = 2.0f * FloatUtil.PI / 3.0f; - final 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(Math.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 { - final float B = Q / A; - res[rc++] = A + B + n; -// if (R2 == Q3) { - final 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(final float res[], final 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(final float x1, final float y1, final float cx, final float cy, final float x2, final 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(final float res[], final int rc, final float py1, final float py2) { - int cross = 0; - - for (int i = 0; i < rc; i++) { - final 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) { - // FIXME: consider using FloatUtil.isEqual(ax, bx, epsilon), ... - if (py1 < ay && (ax != bx ? ax - bx : bx) > 0.0) { - cross++; - } - continue; - } - // CURVE-INSIDE - final float ry = t * (t * Ay + By); - // ry = t * t * Ay + t * By - if (ry > py2) { - final 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(final float res[], final float px) { - final float eqn[] = {-px, Bx, Ax}; - return solveQuad(eqn, res); - } - - int solveExtrem(final 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(final float bound[], int bc, final float res[], final int rc, final float minX, final float maxX, final boolean changeId, int id) { - for(int i = 0; i < rc; i++) { - final float t = res[i]; - if (t > -DELTA && t < 1 + DELTA) { - final 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(final float x1, final float y1, final float cx1, final float cy1, final float cx2, final float cy2, final float x2, final 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(final float res[], final int rc, final float py1, final float py2) { - int cross = 0; - for (int i = 0; i < rc; i++) { - final float t = res[i]; - - // CURVE-OUTSIDE - if (t < -DELTA || t > 1 + DELTA) { - continue; - } - // CURVE-START - if (t < DELTA) { - // FIXME: consider using FloatUtil.isZero(bx, epsilon), ... - 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 - final 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(final float res[], final float px) { - final float eqn[] = {-px, Cx, Bx, Ax}; - return solveCubic(eqn, res); - } - - int solveExtremX(final float res[]) { - final float eqn[] = {Cx, Bx2, Ax3}; - return solveQuad(eqn, res); - } - - int solveExtremY(final float res[]) { - final float eqn[] = {Cy, By + By, Ay + Ay + Ay}; - return solveQuad(eqn, res); - } - - int addBound(final float bound[], int bc, final float res[], final int rc, final float minX, final float maxX, final boolean changeId, int id) { - for(int i = 0; i < rc; i++) { - final float t = res[i]; - if (t > -DELTA && t < 1 + DELTA) { - final 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(final float x1, final float y1, final float x2, final float y2, final float x, final 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(final float x1, final float y1, final float cx, final float cy, final float x2, final float y2, final float x, final 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 - final QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2); - final float px = x - x1; - final float py = y - y1; - final float res[] = new float[3]; - final 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(final float x1, final float y1, final float cx1, final float cy1, final float cx2, final float cy2, final float x2, final float y2, final float x, final 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 - final CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2); - final float px = x - x1; - final float py = y - y1; - final float res[] = new float[3]; - final 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(final Path2F.Iterator p, final float x, final float y) { - int cross = 0; - float mx, my, cx, cy; - mx = my = cx = cy = 0.0f; - final float[] points = p.points(); - while ( p.hasNext() ) { - final int idx = p.index(); - final Path2F.SegmentType segmentType = p.next(); - switch (segmentType) { - case MOVETO: - if (cx != mx || cy != my) { - cross += crossLine(cx, cy, mx, my, x, y); - } - mx = cx = points[idx+0]; - my = cy = points[idx+1]; - break; - case LINETO: - cross += crossLine(cx, cy, cx = points[idx+0], cy = points[idx+1], x, y); - break; - case QUADTO: - cross += crossQuad(cx, cy, points[idx+0], points[idx+1], cx = points[idx+2], cy = points[idx+3], x, y); - break; - case CUBICTO: - cross += crossCubic(cx, cy, points[idx+0], points[idx+1], points[idx+2], points[idx+3], cx = points[idx+4], cy = points[idx+5], x, y); - break; - case 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; - } - } - 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(final Path2F s, final float x, final 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(final float val) { - return -DELTA < val && val < DELTA; - } - - /** - * Sort bound array - */ - static void sortBound(final float bound[], final 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(final float bound[], final int bc, final float py1, final 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) { - final 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(final float x1, final float y1, final float x2, final float y2, final float rx1, final float ry1, final float rx2, final 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; - } - final float k = (y2 - y1) / (x2 - x1); - final float by1 = k * (bx1 - x1) + y1; - final 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(final float x1, final float y1, final float cx, final float cy, final float x2, final float y2, final float rx1, final float ry1, final float rx2, final 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 ------------------------------------------------------------- - final QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2); - final float px1 = rx1 - x1; - final float py1 = ry1 - y1; - final float px2 = rx2 - x1; - final float py2 = ry2 - y1; - - final float res1[] = new float[3]; - final float res2[] = new float[3]; - final int rc1 = c.solvePoint(res1, px1); - int rc2 = c.solvePoint(res2, px2); - - // INSIDE-LEFT/RIGHT - if (rc1 == 0 && rc2 == 0) { - return 0; - } - - // Build bound -------------------------------------------------------- - final float minX = px1 - DELTA; - final float maxX = px2 + DELTA; - final 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 ---------------------------------------------------- - - final 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(final float x1, final float y1, final float cx1, final float cy1, final float cx2, final float cy2, final float x2, final float y2, final float rx1, final float ry1, final float rx2, final 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 - final CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2); - final float px1 = rx1 - x1; - final float py1 = ry1 - y1; - final float px2 = rx2 - x1; - final float py2 = ry2 - y1; - - final float res1[] = new float[3]; - final float res2[] = new float[3]; - final int rc1 = c.solvePoint(res1, px1); - int rc2 = c.solvePoint(res2, px2); - - // LEFT/RIGHT - if (rc1 == 0 && rc2 == 0) { - return 0; - } - - final float minX = px1 - DELTA; - final float maxX = px2 + DELTA; - - // Build bound -------------------------------------------------------- - final 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 ---------------------------------------------------- - - final 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(final Path2F.Iterator p, final float x, final float y, final float w, final float h) { - - int cross = 0; - int count; - float mx, my, cx, cy; - mx = my = cx = cy = 0.0f; - - final float rx1 = x; - final float ry1 = y; - final float rx2 = x + w; - final float ry2 = y + h; - - final float[] points = p.points(); - - while ( p.hasNext() ) { - final int idx = p.index(); - final Path2F.SegmentType segmentType = p.next(); - count = 0; - switch (segmentType) { - case MOVETO: - if (cx != mx || cy != my) { - count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); - } - mx = cx = points[idx+0]; - my = cy = points[idx+1]; - break; - case LINETO: - count = intersectLine(cx, cy, cx = points[idx+0], cy = points[idx+1], rx1, ry1, rx2, ry2); - break; - case QUADTO: - count = intersectQuad(cx, cy, points[idx+0], points[idx+1], cx = points[idx+2], cy = points[idx+3], rx1, ry1, rx2, ry2); - break; - case CUBICTO: - count = intersectCubic(cx, cy, points[idx+0], points[idx+1], points[idx+2], points[idx+3], cx = points[idx+4], cy = points[idx+5], rx1, ry1, rx2, ry2); - break; - case 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; - } - 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(final Path2F s, final float x, final float y, final float w, final float h) { - if (!s.getBounds2D().intersects2DRegion(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(final int cross) { - return cross != 0; - } - - /** - * Returns true if cross count correspond inside location for even-odd path rule - */ - public static boolean isInsideEvenOdd(final int cross) { - return (cross & 1) != 0; - } -} diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/IllegalPathStateException.java b/src/jogl/classes/com/jogamp/graph/geom/plane/IllegalPathStateException.java deleted file mode 100644 index be0a14a76..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/IllegalPathStateException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 com.jogamp.graph.geom.plane; - -public class IllegalPathStateException extends RuntimeException { - - private static final long serialVersionUID = -5158084205220481094L; - - public IllegalPathStateException() { - } - - public IllegalPathStateException(final String s) { - super(s); - } - -} - diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/NoninvertibleTransformException.java b/src/jogl/classes/com/jogamp/graph/geom/plane/NoninvertibleTransformException.java deleted file mode 100644 index fb90cedfc..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/NoninvertibleTransformException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 com.jogamp.graph.geom.plane; - -public class NoninvertibleTransformException extends java.lang.Exception { - - private static final long serialVersionUID = 6137225240503990466L; - - public NoninvertibleTransformException(final String s) { - super(s); - } - -} - diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/Path2F.java b/src/jogl/classes/com/jogamp/graph/geom/plane/Path2F.java deleted file mode 100644 index 588232d9a..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/Path2F.java +++ /dev/null @@ -1,603 +0,0 @@ -/* - * 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 - * @author Sven Gothel - */ -package com.jogamp.graph.geom.plane; - -import java.io.PrintStream; -import java.util.NoSuchElementException; - -import com.jogamp.opengl.math.geom.AABBox; - -/** - * Path2F represents and provides construction method for a 2D shape using float[2] points. - */ -public final class Path2F implements Cloneable { - static final String invalidWindingRuleValue = "Invalid winding rule value"; - static final String iteratorOutOfBounds = "Iterator out of bounds"; - - /** A Path2D segment type */ - public static enum SegmentType { - MOVETO(1), - LINETO(1), - QUADTO(2), - CUBICTO(3), - CLOSE(0); - - /** Number of points associated with this segment type */ - public final int point_count; - - /** Return the integer segment type value as a byte */ - public byte integer() { - return (byte) this.ordinal(); - } - - /** Return the SegmentType associated with the integer segment type */ - public static SegmentType valueOf(final int type) { - switch( type ) { - case 0: return MOVETO; - case 1: return LINETO; - case 2: return QUADTO; - case 3: return CUBICTO; - case 4: return CLOSE; - default: - throw new IllegalArgumentException("Unhandled Segment Type: "+type); - } - } - - /** Return the number of points associated with the integer segment type */ - public static int getPointCount(final int type) { - switch( type ) { - case 0: return MOVETO.point_count; - case 1: return LINETO.point_count; - case 2: return QUADTO.point_count; - case 3: return CUBICTO.point_count; - case 4: return CLOSE.point_count; - default: - throw new IllegalArgumentException("Unhandled Segment Type: "+type); - } - } - - SegmentType(final int v) { - this.point_count = v; - } - } - - /** - * 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 - */ - private byte[] m_types; - - /** - * The points buffer - */ - private float[] m_points; - - /** - * The point's type buffer size - */ - private int m_typeSize; - - /** - * The points buffer size - */ - private int m_pointSize; - - /** - * The winding path rule - */ - private WindingRule m_rule; - - /* - * GeneralPath path iterator - */ - public static final class Iterator { - - /** - * The source GeneralPath object - */ - private final Path2F p; - - /** - * The path iterator transformation - */ - private final AffineTransform t; - - /** - * The current cursor position in types buffer - */ - private int typeIndex; - - /** - * The current cursor position in points buffer - */ - private int pointIndex; - - /** - * Constructs a new GeneralPath.Iterator for given general path - * @param path - the source GeneralPath object - */ - Iterator(final Path2F 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 - */ - public Iterator(final Path2F path, final AffineTransform at) { - this.p = path; - this.t = at; - reset(); - } - - private void reset() { - typeIndex = 0; - pointIndex = 0; - } - - /** Return the {@link WindingRule} set */ - public WindingRule getWindingRule() { - return p.getWindingRule(); - } - - /** - * Compute the general winding of the vertices - * @return CCW or CW {@link Winding} - */ - public Winding getWinding() { - return area() >= 0 ? Winding.CCW : Winding.CW ; - } - - /** Returns reference of the point array covering the whole iteration of Path2D, use {@link #index()} to access the current point. */ - public float[] points() { return p.m_points; } - - /** Return the {@link #points()} index for the current segment. */ - public int index() { return pointIndex; } - - /** Return current segment type */ - public SegmentType getType() { return SegmentType.valueOf( p.m_types[typeIndex] ); } - - /** - * Return the current segment type and copies the current segment's points to given storage - * @param coords storage for current segment's points - * @return current segment type - * @see #points() - * @see #type_index() - * @see #getType() - * @deprecated try to use {@link #index()}, {@link #points()} and {@link #next()} to avoid copying - */ - @Deprecated - public SegmentType currentSegment(final float[] coords) { - if (!hasNext()) { - throw new NoSuchElementException(iteratorOutOfBounds); - } - final SegmentType type = getType(); - final int count = type.point_count; - System.arraycopy(p.m_points, pointIndex, coords, 0, count*2); - if (t != null) { - t.transform(coords, 0, coords, 0, count); - } - return type; - } - - /** Returns true if the iteration has more elements. */ - public boolean hasNext() { - return typeIndex < p.m_typeSize; - } - - /** Returns the current segment type in the iteration, then moving to the next path segment. */ - public SegmentType next() { - final SegmentType t = getType(); - pointIndex += 2 * t.point_count; - ++typeIndex; - return t; - } - - /** - * Computes the area of the path to check if ccw - * @return positive area if ccw else negative area value - */ - private float area() { - float area = 0.0f; - final float[] points = points(); - final float[] pCoord = new float[2]; - while ( hasNext() ) { - final int idx = index(); - final SegmentType type = next(); - switch ( type ) { - case MOVETO: - pCoord[0] = points[idx+0]; - pCoord[1] = points[idx+1]; - break; - case LINETO: - area += pCoord[0] * points[idx+1] - points[idx+0] * pCoord[1]; - pCoord[0] = points[idx+0]; - pCoord[1] = points[idx+1]; - break; - case QUADTO: - area += pCoord[0] * points[idx+1] - points[idx+0] * pCoord[1]; - area += points[idx+0] * points[idx+3] - points[idx+2] * points[idx+1]; - pCoord[0] = points[idx+2]; - pCoord[1] = points[idx+3]; - break; - case CUBICTO: - area += pCoord[0] * points[idx+1] - points[idx+0] * pCoord[1]; - area += points[idx+0] * points[idx+3] - points[idx+2] * points[idx+1]; - area += points[idx+2] * points[idx+5] - points[idx+4] * points[idx+3]; - pCoord[0] = points[idx+4]; - pCoord[1] = points[idx+5]; - break; - case CLOSE: - break; - } - } - reset(); - return area; - } - } - - public Path2F() { - this(WindingRule.NON_ZERO, BUFFER_SIZE, BUFFER_SIZE); - } - - public Path2F(final WindingRule rule) { - this(rule, BUFFER_SIZE, BUFFER_SIZE); - } - - public Path2F(final WindingRule rule, final int initialCapacity) { - this(rule, initialCapacity, initialCapacity); - } - - public Path2F(final WindingRule rule, final int initialTypeCapacity, final int initialPointCapacity) { - setWindingRule(rule); - m_types = new byte[initialTypeCapacity]; - m_points = new float[initialPointCapacity * 2]; - } - - public Path2F(final Path2F path) { - this(WindingRule.NON_ZERO, BUFFER_SIZE); - final Iterator p = path.iterator(null); - setWindingRule(p.getWindingRule()); - append(p, false); - } - - /** Set the {@link WindingRule} set */ - public void setWindingRule(final WindingRule rule) { - this.m_rule = rule; - } - - /** Return the {@link WindingRule} set */ - public WindingRule getWindingRule() { - return m_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 - */ - private void checkBuf(final int pointCount, final boolean checkMove) { - if (checkMove && m_typeSize == 0) { - throw new IllegalPathStateException("First segment should be SEG_MOVETO type"); - } - if (m_typeSize == m_types.length) { - final byte tmp[] = new byte[m_typeSize + BUFFER_CAPACITY]; - System.arraycopy(m_types, 0, tmp, 0, m_typeSize); - m_types = tmp; - } - if (m_pointSize + pointCount > m_points.length) { - final float tmp[] = new float[m_pointSize + Math.max(BUFFER_CAPACITY * 2, pointCount)]; - System.arraycopy(m_points, 0, tmp, 0, m_pointSize); - m_points = tmp; - } - } - - /** - * Start a new position for the next line segment at given point x/y (P1). - * @param x point (P1) - * @param y point (P1) - */ - public void moveTo(final float x, final float y) { - if ( m_typeSize > 0 && m_types[m_typeSize - 1] == SegmentType.MOVETO.integer() ) { - m_points[m_pointSize - 2] = x; - m_points[m_pointSize - 1] = y; - } else { - checkBuf(2, false); - m_types[m_typeSize++] = SegmentType.MOVETO.integer(); - m_points[m_pointSize++] = x; - m_points[m_pointSize++] = y; - } - } - - /** - * Add a line segment, intersecting the last point and the given point x/y (P1). - * @param x final point (P1) - * @param y final point (P1) - */ - public void lineTo(final float x, final float y) { - checkBuf(2, true); - m_types[m_typeSize++] = SegmentType.LINETO.integer(); - m_points[m_pointSize++] = x; - m_points[m_pointSize++] = y; - } - - /** - * Add a quadratic curve segment, intersecting the last point and the second given point x2/y2 (P2). - * @param x1 quadratic parametric control point (P1) - * @param y1 quadratic parametric control point (P1) - * @param x2 final interpolated control point (P2) - * @param y2 final interpolated control point (P2) - */ - public void quadTo(final float x1, final float y1, final float x2, final float y2) { - checkBuf(4, true); - m_types[m_typeSize++] = SegmentType.QUADTO.integer(); - m_points[m_pointSize++] = x1; - m_points[m_pointSize++] = y1; - m_points[m_pointSize++] = x2; - m_points[m_pointSize++] = y2; - } - - /** - * Add a cubic Bézier curve segment, intersecting the last point and the second given point x3/y3 (P3). - * @param x1 Bézier control point (P1) - * @param y1 Bézier control point (P1) - * @param x2 Bézier control point (P2) - * @param y2 Bézier control point (P2) - * @param x3 final interpolated control point (P3) - * @param y3 final interpolated control point (P3) - */ - public void cubicTo(final float x1, final float y1, final float x2, final float y2, final float x3, final float y3) { - checkBuf(6, true); - m_types[m_typeSize++] = SegmentType.CUBICTO.integer(); - m_points[m_pointSize++] = x1; - m_points[m_pointSize++] = y1; - m_points[m_pointSize++] = x2; - m_points[m_pointSize++] = y2; - m_points[m_pointSize++] = x3; - m_points[m_pointSize++] = y3; - } - - /** - * Closes the current sub-path segment by drawing a straight line back to the coordinates of the last moveTo. If the path is already closed then this method has no effect. - */ - public void closePath() { - if (!isClosed()) { - checkBuf(0, true); - m_types[m_typeSize++] = SegmentType.CLOSE.integer(); - } - } - - final public int size() { - return m_typeSize; - } - - /** - * Returns true if the last sub-path is closed, otherwise false. - */ - final public boolean isClosed() { - return m_typeSize > 0 && m_types[m_typeSize - 1] == SegmentType.CLOSE.integer() ; - } - - /** - * Compute the general winding of the vertices - * @param vertices array of Vertices - * @return CCW or CW {@link Winding} - */ - public Winding getWinding() { - return iterator(null).getWinding(); - } - - @Override - public String toString() { - return "[size "+size()+", closed "+isClosed()+", winding[rule "+getWindingRule()+", "+getWinding()+"]]"; - } - - /** - * Append the given path geometry to this instance - * @param path the {@link Path2F} to append to this instance - * @param connect pass true to turn an initial moveTo segment into a lineTo segment to connect the new geometry to the existing path, otherwise pass false. - */ - public void append(final Path2F path, final boolean connect) { - append(path.iterator(null), connect); - } - - /** - * Append the given path geometry to this instance - * @param path the {@link Path2F.Iterator} to append to this instance - * @param connect pass true to turn an initial moveTo segment into a lineTo segment to connect the new geometry to the existing path, otherwise pass false. - */ - public void append(final Iterator path, boolean connect) { - final float[] points = path.points(); - while ( path.hasNext() ) { - final int idx = path.index(); - final SegmentType type = path.next(); - switch ( type ) { - case MOVETO: - if ( !connect || 0 == m_typeSize ) { - moveTo(points[idx+0], points[idx+1]); - break; - } - if ( m_types[m_typeSize - 1] != SegmentType.CLOSE.integer() && - m_points[m_pointSize - 2] == points[idx+0] && - m_points[m_pointSize - 1] == points[idx+1] - ) - { - break; - } - // fallthrough: MOVETO -> LINETO - case LINETO: - lineTo(points[idx+0], points[idx+1]); - break; - case QUADTO: - quadTo(points[idx+0], points[idx+1], points[idx+2], points[idx+3]); - break; - case CUBICTO: - cubicTo(points[idx+0], points[idx+1], points[idx+2], points[idx+3], points[idx+4], points[idx+5]); - break; - case CLOSE: - closePath(); - break; - } - connect = false; - } - } - - public void printSegments(final PrintStream out) { - final Iterator path = iterator(); - final float[] points = path.points(); - int i = 0; - while ( path.hasNext() ) { - final int idx = path.index(); - final SegmentType type = path.next(); - switch ( type ) { - case MOVETO: - out.printf("%2d: moveTo(%.4f/%.4f)%n", i, points[idx+0], points[idx+1]); - break; - case LINETO: - out.printf("%2d: lineTo(%.4f/%.4f)%n", i, points[idx+0], points[idx+1]); - break; - case QUADTO: - out.printf("%2d: quadTo(%.4f/%.4f, %.4f/%.4f)%n", i, points[idx+0], points[idx+1], points[idx+2], points[idx+3]); - break; - case CUBICTO: - out.printf("%2d: cubicTo(%.4f/%.4f, %.4f/%.4f, %.4f/%.4f)%n", i, points[idx+0], points[idx+1], points[idx+2], points[idx+3], points[idx+4], points[idx+5]); - break; - case CLOSE: - out.printf("%2d: closePath()%n", i); - break; - } - ++i; - } - } - - public void reset() { - m_typeSize = 0; - m_pointSize = 0; - } - - public void transform(final AffineTransform t) { - t.transform(m_points, 0, m_points, 0, m_pointSize / 2); - } - - public Path2F createTransformedShape(final AffineTransform t) { - final Path2F p = (Path2F)clone(); - if (t != null) { - p.transform(t); - } - return p; - } - - public final synchronized AABBox getBounds2D() { - float rx1, ry1, rx2, ry2; - if (m_pointSize == 0) { - rx1 = ry1 = rx2 = ry2 = 0.0f; - } else { - int i = m_pointSize - 1; - ry1 = ry2 = m_points[i--]; - rx1 = rx2 = m_points[i--]; - while (i > 0) { - final float y = m_points[i--]; - final float x = m_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 AABBox(rx1, ry1, 0f, rx2, ry2, 0f); - } - - /** - * 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(final int cross) { - if (m_rule == WindingRule.NON_ZERO) { - return Crossing2F.isInsideNonZero(cross); - } - return Crossing2F.isInsideEvenOdd(cross); - } - - public boolean contains(final float px, final float py) { - return isInside(Crossing2F.crossShape(this, px, py)); - } - - public boolean contains(final float rx, final float ry, final float rw, final float rh) { - final int cross = Crossing2F.intersectShape(this, rx, ry, rw, rh); - return cross != Crossing2F.CROSSING && isInside(cross); - } - - public boolean intersects(final float rx, final float ry, final float rw, final float rh) { - final int cross = Crossing2F.intersectShape(this, rx, ry, rw, rh); - return cross == Crossing2F.CROSSING || isInside(cross); - } - - public boolean contains(final AABBox r) { - return contains(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight()); - } - - public boolean intersects(final AABBox r) { - return intersects(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight()); - } - - public Iterator iterator() { - return new Iterator(this); - } - - public Iterator iterator(final AffineTransform t) { - return new Iterator(this, t); - } - - /* public Path2F.Iterator getPathIterator(AffineTransform t, float flatness) { - return new FlatteningPathIterator(getPathIterator(t), flatness); - } */ - - @Override - public Object clone() { - try { - final Path2F p = (Path2F) super.clone(); - p.m_types = m_types.clone(); - p.m_points = m_points.clone(); - return p; - } catch (final CloneNotSupportedException e) { - throw new InternalError(); - } - } -} - diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/Winding.java b/src/jogl/classes/com/jogamp/graph/geom/plane/Winding.java deleted file mode 100644 index bfa214c22..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/Winding.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.jogamp.graph.geom.plane; - -/** - * Winding direction, either clockwise (CW) or counter-clockwise (CCW). - */ -public enum Winding { - /** Clockwise (Cw) negative winding direction */ - CW(-1), - /** Counter-Clockwise (CCW) positive winding direction */ - CCW(1); - - /** The winding direction sign, i.e. positive 1 for CCW and negative -1 for CW. */ - public final int dir; - - Winding(final int dir) { - this.dir = dir; - } -} diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/WindingRule.java b/src/jogl/classes/com/jogamp/graph/geom/plane/WindingRule.java deleted file mode 100644 index 46ef167a3..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/WindingRule.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.jogamp.graph.geom.plane; - -/** - * Winding rule, either EVEN_ODD or NON_ZERO (like for TrueType fonts). - */ -public enum WindingRule { - /** - * The even-odd rule specifies that a point lies inside the path - * if a ray drawn in any direction from that point to infinity is crossed by path segments - * an odd number of times. - */ - EVEN_ODD(0), - - /** - * The non-zero rule specifies that a point lies inside the path - * if a ray drawn in any direction from that point to infinity is crossed by path segments - * a different number of times in the counter-clockwise direction than the clockwise direction. - * - * Non-zero winding rule is used by TrueType fonts. - */ - NON_ZERO(1); - - public final int value; - - WindingRule(final int v) { - this.value = v; - } -} |