diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/graph/geom')
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/Outline.java | 1 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java | 2 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/plane/Crossing2F.java (renamed from src/jogl/classes/com/jogamp/graph/geom/plane/Crossing.java) | 74 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/plane/Path2D.java | 454 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/plane/Path2F.java | 603 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/plane/PathIterator.java | 60 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/plane/Winding.java | 18 | ||||
-rw-r--r-- | src/jogl/classes/com/jogamp/graph/geom/plane/WindingRule.java | 28 |
8 files changed, 684 insertions, 556 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 486e787a2..b18d51849 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -29,7 +29,6 @@ package com.jogamp.graph.geom; import java.util.ArrayList; -import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.plane.AffineTransform; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java b/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java index a5b3cac93..62cda0322 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java +++ b/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java @@ -530,7 +530,7 @@ public class AffineTransform implements Cloneable { } } - public final Path2D createTransformedShape(final Path2D src) { + public final Path2F createTransformedShape(final Path2F src) { if (src == null) { return null; } diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/Crossing.java b/src/jogl/classes/com/jogamp/graph/geom/plane/Crossing2F.java index 173f1d4b4..0cd4b66ff 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/Crossing.java +++ b/src/jogl/classes/com/jogamp/graph/geom/plane/Crossing2F.java @@ -21,9 +21,7 @@ package com.jogamp.graph.geom.plane; import com.jogamp.opengl.math.FloatUtil; - - -public class Crossing { +/* pp */ class Crossing2F { /** * Allowable tolerance for bounds comparison @@ -464,38 +462,36 @@ public class Crossing { /** * Returns how many times ray from point (x,y) cross path */ - public static int crossPath(final PathIterator p, final float x, final float y) { + 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 coords[] = new float[6]; - - while (!p.isDone()) { - final int segmentType = p.currentSegment(coords); + final float[] points = p.points(); + while ( p.hasNext() ) { + final int idx = p.index(); + final Path2F.SegmentType segmentType = p.next(); switch (segmentType) { - case PathIterator.SEG_MOVETO: + case MOVETO: if (cx != mx || cy != my) { cross += crossLine(cx, cy, mx, my, x, y); } - mx = cx = coords[0]; - my = cy = coords[1]; + mx = cx = points[idx+0]; + my = cy = points[idx+1]; break; - case PathIterator.SEG_LINETO: - cross += crossLine(cx, cy, cx = coords[0], cy = coords[1], x, y); + case LINETO: + cross += crossLine(cx, cy, cx = points[idx+0], cy = points[idx+1], x, y); break; - case PathIterator.SEG_QUADTO: - cross += crossQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], x, y); + case QUADTO: + cross += crossQuad(cx, cy, points[idx+0], points[idx+1], cx = points[idx+2], cy = points[idx+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); + 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 PathIterator.SEG_CLOSE: + case 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 @@ -504,7 +500,6 @@ public class Crossing { cy = my; break; } - p.next(); } if (cy != my) { cross += crossLine(cx, cy, mx, my, x, y); @@ -515,7 +510,7 @@ public class Crossing { /** * Returns how many times ray from point (x,y) cross shape */ - public static int crossShape(final Path2D s, final float x, final float y) { + public static int crossShape(final Path2F s, final float x, final float y) { if (!s.getBounds2D().contains(x, y)) { return 0; } @@ -819,54 +814,53 @@ public class Crossing { /** * Returns how many times rectangle stripe cross path or the are intersect */ - public static int intersectPath(final PathIterator p, final float x, final float y, final float w, final float h) { + 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 coords[] = new float[6]; final float rx1 = x; final float ry1 = y; final float rx2 = x + w; final float ry2 = y + h; - while (!p.isDone()) { + final float[] points = p.points(); + + while ( p.hasNext() ) { + final int idx = p.index(); + final Path2F.SegmentType segmentType = p.next(); count = 0; - final int segmentType = p.currentSegment(coords); switch (segmentType) { - case PathIterator.SEG_MOVETO: + case MOVETO: if (cx != mx || cy != my) { count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); } - mx = cx = coords[0]; - my = cy = coords[1]; + mx = cx = points[idx+0]; + my = cy = points[idx+1]; break; - case PathIterator.SEG_LINETO: - count = intersectLine(cx, cy, cx = coords[0], cy = coords[1], rx1, ry1, rx2, ry2); + case LINETO: + count = intersectLine(cx, cy, cx = points[idx+0], cy = points[idx+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); + 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 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); + 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 PathIterator.SEG_CLOSE: + case 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); @@ -881,7 +875,7 @@ public class Crossing { /** * Returns how many times rectangle stripe cross shape or the are intersect */ - public static int intersectShape(final Path2D s, final float x, final float y, final float w, final float h) { + 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; } diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/Path2D.java b/src/jogl/classes/com/jogamp/graph/geom/plane/Path2D.java deleted file mode 100644 index 8dbc5fd21..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/Path2D.java +++ /dev/null @@ -1,454 +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.util.NoSuchElementException; - -import com.jogamp.graph.geom.SVertex; -import com.jogamp.graph.geom.Vertex; -import com.jogamp.opengl.math.geom.AABBox; - - -public final class Path2D implements Cloneable { - - public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; - public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; - - 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 - */ - private static final int BUFFER_CAPACITY = 10; - - /** - * The point's types buffer - */ - byte[] m_types; - - /** - * The points buffer - */ - float[] m_points; - - /** - * The point's type buffer size - */ - int m_typeSize; - - /** - * The points buffer size - */ - int m_pointSize; - - /** - * The path rule - */ - int m_rule; - - /** - * The space amount in points buffer for different segmenet's types - */ - static int pointShift[] = { - 2, // MOVETO - 2, // LINETO - 4, // QUADTO - 6, // CUBICTO - 0}; // CLOSE - - /* - * GeneralPath path iterator - */ - static class Iterator implements PathIterator { - - /** - * The current cursor position in types buffer - */ - int typeIndex; - - /** - * The current cursor position in points buffer - */ - int pointIndex; - - /** - * The source GeneralPath object - */ - Path2D p; - - /** - * The path iterator transformation - */ - AffineTransform t; - - /** - * Constructs a new GeneralPath.Iterator for given general path - * @param path - the source GeneralPath object - */ - Iterator(final Path2D path) { - this(path, null); - } - - /** - * Constructs a new GeneralPath.Iterator for given general path and transformation - * @param path - the source GeneralPath object - * @param at - the AffineTransform object to apply rectangle path - */ - Iterator(final Path2D path, final AffineTransform at) { - this.p = path; - this.t = at; - } - - @Override - public int getWindingRule() { - return p.getWindingRule(); - } - - @Override - public int index() { return typeIndex; } - - @Override - public float[] points() { return p.m_points; } - - @Override - public int getType(final int idx) { return p.m_types[idx]; } - - @Override - public boolean isDone() { - return typeIndex >= p.m_typeSize; - } - - @Override - public void next() { - typeIndex++; - } - - @Override - public int currentSegment(final float[] coords) { - if (isDone()) { - throw new NoSuchElementException(iteratorOutOfBounds); - } - final int type = p.m_types[typeIndex]; - final int count = Path2D.pointShift[type]; - System.arraycopy(p.m_points, pointIndex, coords, 0, count); - if (t != null) { - t.transform(coords, 0, coords, 0, count / 2); - } - pointIndex += count; - return type; - } - - } - - public static int getPointCount(final int type) { return pointShift[type]; } - - public Path2D() { - this(WIND_NON_ZERO, BUFFER_SIZE); - } - - public Path2D(final int rule) { - this(rule, BUFFER_SIZE); - } - - public Path2D(final int rule, final int initialCapacity) { - setWindingRule(rule); - m_types = new byte[initialCapacity]; - m_points = new float[initialCapacity * 2]; - } - - public Path2D(final Path2D path) { - this(WIND_NON_ZERO, BUFFER_SIZE); - final PathIterator p = path.iterator(null); - setWindingRule(p.getWindingRule()); - append(p, false); - } - - public void setWindingRule(final int rule) { - if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) { - throw new NoSuchElementException(invalidWindingRuleValue); - } - this.m_rule = rule; - } - - public int 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 - */ - 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; - } - } - - public void moveTo(final float x, final float y) { - if (m_typeSize > 0 && m_types[m_typeSize - 1] == PathIterator.SEG_MOVETO) { - m_points[m_pointSize - 2] = x; - m_points[m_pointSize - 1] = y; - } else { - checkBuf(2, false); - m_types[m_typeSize++] = PathIterator.SEG_MOVETO; - m_points[m_pointSize++] = x; - m_points[m_pointSize++] = y; - } - } - - public void lineTo(final float x, final float y) { - checkBuf(2, true); - m_types[m_typeSize++] = PathIterator.SEG_LINETO; - m_points[m_pointSize++] = x; - m_points[m_pointSize++] = y; - } - - public void quadTo(final float x1, final float y1, final float x2, final float y2) { - checkBuf(4, true); - m_types[m_typeSize++] = PathIterator.SEG_QUADTO; - m_points[m_pointSize++] = x1; - m_points[m_pointSize++] = y1; - m_points[m_pointSize++] = x2; - m_points[m_pointSize++] = y2; - } - - public void curveTo(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++] = PathIterator.SEG_CUBICTO; - 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; - } - - final public int size() { - return m_typeSize; - } - - final public boolean isClosed() { - return m_typeSize > 0 && m_types[m_typeSize - 1] == PathIterator.SEG_CLOSE ; - } - - public void closePath() { - if (!isClosed()) { - checkBuf(0, true); - m_types[m_typeSize++] = PathIterator.SEG_CLOSE; - } - } - - @Override - public String toString() { - return "[size "+size()+", closed "+isClosed()+"]"; - } - - public void append(final Path2D path, final boolean connect) { - final PathIterator p = path.iterator(null); - append(p, connect); - } - - /** - * Append the given path geometry to this instance - * @param path the {@link PathIterator} to append to this {@link Path2D} - * @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 PathIterator path, boolean connect) { - final float[] points = path.points(); - while ( !path.isDone() ) { - final int index = path.index(); - final int type = path.getType(index); - switch ( type ) { - case PathIterator.SEG_MOVETO: - if ( !connect || 0 == m_typeSize ) { - moveTo(points[index+0], points[index+1]); - break; - } - if ( m_types[m_typeSize - 1] != PathIterator.SEG_CLOSE && - m_points[m_pointSize - 2] == points[index+0] && - m_points[m_pointSize - 1] == points[index+1] - ) - { - break; - } - // fallthrough: MOVETO -> LINETO - case PathIterator.SEG_LINETO: - lineTo(points[index+0], points[index+1]); - break; - case PathIterator.SEG_QUADTO: - quadTo(points[index+0], points[index+1], points[index+2], points[index+3]); - break; - case PathIterator.SEG_CUBICTO: - curveTo(points[index+0], points[index+1], points[index+2], points[index+3], points[index+4], points[index+5]); - break; - case PathIterator.SEG_CLOSE: - closePath(); - break; - default: - throw new IllegalArgumentException("Unhandled Segment Type: "+type); - } - path.next(); - connect = false; - } - } - - public SVertex getCurrentPoint() { - if (m_typeSize == 0) { - return null; - } - int j = m_pointSize - 2; - if (m_types[m_typeSize - 1] == PathIterator.SEG_CLOSE) { - - for (int i = m_typeSize - 2; i > 0; i--) { - final int type = m_types[i]; - if (type == PathIterator.SEG_MOVETO) { - break; - } - j -= pointShift[type]; - } - } - return new SVertex(m_points[j], m_points[j + 1], 0f, true); - } - - 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 Path2D createTransformedShape(final AffineTransform t) { - final Path2D p = (Path2D)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 == WIND_NON_ZERO) { - return Crossing.isInsideNonZero(cross); - } - return Crossing.isInsideEvenOdd(cross); - } - - public boolean contains(final float px, final float py) { - return isInside(Crossing.crossShape(this, px, py)); - } - - public boolean contains(final float rx, final float ry, final float rw, final float rh) { - final int cross = Crossing.intersectShape(this, rx, ry, rw, rh); - return cross != Crossing.CROSSING && isInside(cross); - } - - public boolean intersects(final float rx, final float ry, final float rw, final float rh) { - final int cross = Crossing.intersectShape(this, rx, ry, rw, rh); - return cross == Crossing.CROSSING || isInside(cross); - } - - public boolean contains(final Vertex p) { - return contains(p.getX(), p.getY()); - } - - 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 PathIterator iterator() { - return new Iterator(this); - } - - public PathIterator iterator(final AffineTransform t) { - return new Iterator(this, t); - } - - /* public PathIterator getPathIterator(AffineTransform t, float flatness) { - return new FlatteningPathIterator(getPathIterator(t), flatness); - } */ - - @Override - public Object clone() { - try { - final Path2D p = (Path2D) 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/Path2F.java b/src/jogl/classes/com/jogamp/graph/geom/plane/Path2F.java new file mode 100644 index 000000000..588232d9a --- /dev/null +++ b/src/jogl/classes/com/jogamp/graph/geom/plane/Path2F.java @@ -0,0 +1,603 @@ +/* + * 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/PathIterator.java b/src/jogl/classes/com/jogamp/graph/geom/plane/PathIterator.java deleted file mode 100644 index 3aae2a172..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/PathIterator.java +++ /dev/null @@ -1,60 +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; - -public interface PathIterator { - - public static final int WIND_EVEN_ODD = 0; - public static final int WIND_NON_ZERO = 1; - - public static final int SEG_MOVETO = 0; - public static final int SEG_LINETO = 1; - public static final int SEG_QUADTO = 2; - public static final int SEG_CUBICTO = 3; - public static final int SEG_CLOSE = 4; - - int getWindingRule(); - - /** Return the current {@link #points()} index for the current segment. */ - int index(); - - /** Returns reference of the point array for the whole Path2D */ - float[] points(); - - /** Return current segment type */ - int getType(final int idx); - - /** Returns true if completed */ - boolean isDone(); - - void next(); - - /** - * Return the path segment type and copies the current segment's points to given storage - * @param coords storage for current segment's points - * @return segment type - * @see #points() - * @see #index() - * @see #getType(int) - */ - int currentSegment(float[] coords); -} - diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/Winding.java b/src/jogl/classes/com/jogamp/graph/geom/plane/Winding.java new file mode 100644 index 000000000..bfa214c22 --- /dev/null +++ b/src/jogl/classes/com/jogamp/graph/geom/plane/Winding.java @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..46ef167a3 --- /dev/null +++ b/src/jogl/classes/com/jogamp/graph/geom/plane/WindingRule.java @@ -0,0 +1,28 @@ +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; + } +} |