Index: src/main/java/common/java/awt/geom/Area.java =================================================================== --- src/main/java/common/java/awt/geom/Area.java (revision 545781) +++ src/main/java/common/java/awt/geom/Area.java (working copy) @@ -14,199 +14,1287 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** - * @author Denis M. Kishenko - * @version $Revision$ - */ package java.awt.geom; import java.awt.Rectangle; import java.awt.Shape; -import java.awt.geom.PathIterator; -import java.awt.geom.Rectangle2D; import java.util.NoSuchElementException; +import org.apache.harmony.awt.gl.Crossing; +import org.apache.harmony.awt.internal.CrossingHelper; +import org.apache.harmony.awt.internal.CurveCrossingHelper; +import org.apache.harmony.awt.internal.GeometryUtil; +import org.apache.harmony.awt.internal.IntersectPoint; import org.apache.harmony.awt.internal.nls.Messages; + public class Area implements Shape, Cloneable { /** - * The source Shape object + * the coordinates array of the shape vertices */ - Shape s; + private double coords[] = new double[20]; + + /** + * the coordinates quantity + */ + private int coordsSize = 0; + + /** + * the rules array for the drawing of the shape edges + */ + private int rules[] = new int[10]; + + /** + * the rules quantity + */ + private int rulesSize = 0; + + /** + * offsets[i] - index in array of coords and i - index in array of rules + */ + private int offsets[] = new int[10]; + + /** + * the quantity of MOVETO rule occurences + */ + private int moveToCount = 0; + + /** + * true if the shape is polygon + */ + private boolean isPolygonal = true; - private static class NullIterator implements PathIterator { + public Area() { + } - NullIterator() { - } + public Area(Shape s) { + double segmentCoords[] = new double[6]; + double lastMoveX = 0.0; + double lastMoveY = 0.0; + int rulesIndex = 0; + int coordsIndex = 0; + + for (PathIterator pi = s.getPathIterator(null); + !pi.isDone(); pi.next()) { + coords = adjustSize(coords, coordsIndex + 6); + rules = adjustSize(rules, rulesIndex + 1); + offsets = adjustSize(offsets, rulesIndex + 1); + rules[rulesIndex] = pi.currentSegment(segmentCoords); + offsets[rulesIndex] = coordsIndex; + + switch (rules[rulesIndex]) { + case PathIterator.SEG_MOVETO: + coords[coordsIndex++] = segmentCoords[0]; + coords[coordsIndex++] = segmentCoords[1]; + lastMoveX = segmentCoords[0]; + lastMoveY = segmentCoords[1]; + ++moveToCount; + break; + case PathIterator.SEG_LINETO: + if ((segmentCoords[0] != lastMoveX) || + (segmentCoords[1] != lastMoveY)) { + coords[coordsIndex++] = segmentCoords[0]; + coords[coordsIndex++] = segmentCoords[1]; + } else { + --rulesIndex; + } + break; + case PathIterator.SEG_QUADTO: + System.arraycopy(segmentCoords, 0, coords, coordsIndex, 4); + coordsIndex += 4; + isPolygonal = false; + break; + case PathIterator.SEG_CUBICTO: + System.arraycopy(segmentCoords, 0, coords, coordsIndex, 6); + coordsIndex += 6; + isPolygonal = false; + break; + case PathIterator.SEG_CLOSE: + break; + } + ++rulesIndex; + } + + if ((rulesIndex != 0) && + (rules[rulesIndex - 1] != PathIterator.SEG_CLOSE)) { + rules[rulesIndex] = PathIterator.SEG_CLOSE; + offsets[rulesIndex] = coordsSize; + } + + rulesSize = rulesIndex; + coordsSize = coordsIndex; + } - public int getWindingRule() { - return WIND_NON_ZERO; - } + public boolean contains(double x, double y) { + return !isEmpty() && + containsExact(x, y) > 0; + } - public boolean isDone() { - return true; - } + public boolean contains(double x, double y, double width, double height) { + int crossCount = Crossing.intersectPath(getPathIterator(null), x, y, + width, height); + return crossCount != Crossing.CROSSING && + Crossing.isInsideEvenOdd(crossCount); + } - public void next() { - // nothing - } + public boolean contains(Point2D p) { + return contains(p.getX(), p.getY()); + } - public int currentSegment(double[] coords) { - // awt.4B=Iterator out of bounds - throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ - } + public boolean contains(Rectangle2D r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } - public int currentSegment(float[] coords) { - // awt.4B=Iterator out of bounds - throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ - } + public boolean equals(Area obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + Area area = (Area)clone(); + area.subtract(obj); + return area.isEmpty(); + } - } + public boolean intersects(double x, double y, double width, double height) { + if ((width <= 0.0) || (height <= 0.0)) { + return false; + } else if (!getBounds2D().intersects(x, y, width, height)) { + return false; + } + + int crossCount = Crossing.intersectShape(this, x, y, width, height); + return Crossing.isInsideEvenOdd(crossCount); + } - public Area() { - } + public boolean intersects(Rectangle2D r) { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } - public Area(Shape s) { - if (s == null) { - throw new NullPointerException(); - } - this.s = s; - } + public Rectangle getBounds() { + return getBounds2D().getBounds(); + } - public boolean contains(double x, double y) { - return s == null ? false : s.contains(x, y); - } + public Rectangle2D getBounds2D() { + double maxX = coords[0]; + double maxY = coords[1]; + double minX = coords[0]; + double minY = coords[1]; - public boolean contains(double x, double y, double width, double height) { - return s == null ? false : s.contains(x, y, width, height); - } + for (int i = 0; i < coordsSize;) { + minX = Math.min(minX, coords[i]); + maxX = Math.max(maxX, coords[i++]); + minY = Math.min(minY, coords[i]); + maxY = Math.max(maxY, coords[i++]); + } + + return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + } - public boolean contains(Point2D p) { - if (p == null) { - throw new NullPointerException(); - } - return s == null ? false : s.contains(p); - } + public PathIterator getPathIterator(AffineTransform t) { + return new AreaPathIterator(this, t); + } + + public PathIterator getPathIterator(AffineTransform t, double flatness) { + return new FlatteningPathIterator(getPathIterator(t), flatness); + } + + public boolean isEmpty() { + return (rulesSize == 0) && (coordsSize == 0); + } - public boolean contains(Rectangle2D r) { - if (r == null) { - throw new NullPointerException(); - } - return s == null ? false : s.contains(r); - } + public boolean isPolygonal() { + return isPolygonal; + } - public boolean equals(Area obj) throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + public boolean isRectangular() { + return (isPolygonal) && (rulesSize <= 5) && (coordsSize <= 8) && + (coords[1] == coords[3]) && (coords[7] == coords[5]) && + (coords[0] == coords[6]) && (coords[2] == coords[4]); } - public boolean intersects(double x, double y, double width, double height) { - return s == null ? false : s.intersects(x, y, width, height); - } + public boolean isSingular() { + return (moveToCount <= 1); + } - public boolean intersects(Rectangle2D r) { - if (r == null) { - throw new NullPointerException(); + public void reset() { + coordsSize = 0; + rulesSize = 0; + } + + public void transform(AffineTransform t) { + copy(new Area(t.createTransformedShape(this)), this); + } + + public Area createTransformedArea(AffineTransform t) { + return new Area(t.createTransformedShape(this)); + } + + public Object clone() { + Area area = new Area(); + copy(this, area); + return area; + } + + public void add(Area area) { + if (isPolygonal() && area.isPolygonal()) { + addPolygon(area); + } else { + addCurvePolygon(area); + } + } + + public void intersect(Area area) { + if (isPolygonal() && area.isPolygonal()) { + intersectPolygon(area); + } else { + intersectCurvePolygon(area); + } + } + + public void subtract(Area area) { + if (isPolygonal() && area.isPolygonal()) { + subtractPolygon(area); + } else { + subtractCurvePolygon(area); + } + } + + public void exclusiveOr(Area area) { + Area a = (Area) clone(); + a.intersect(area); + add(area); + subtract(a); + } + + private void addCurvePolygon(Area area) { + CurveCrossingHelper crossHelper = new CurveCrossingHelper( + new double[][] { coords, area.coords }, + new int[] { coordsSize, area.coordsSize }, + new int[][] { rules, area.rules }, + new int[] { rulesSize, area.rulesSize }, + new int[][] { offsets, area.offsets }); + IntersectPoint[] intersectPoints = crossHelper.findCrossing(); + + if (intersectPoints.length == 0) { + if (area.contains(getBounds2D())) { + copy(area, this); + } else if (!contains(area.getBounds2D())) { + coords = adjustSize(coords, coordsSize + area.coordsSize); + System.arraycopy(area.coords, 0, coords, coordsSize, + area.coordsSize); + coordsSize += area.coordsSize; + rules = adjustSize(rules, rulesSize + area.rulesSize); + System.arraycopy(area.rules, 0, rules, rulesSize, + area.rulesSize); + rulesSize += area.rulesSize; + offsets = adjustSize(offsets, rulesSize + area.rulesSize); + System.arraycopy(area.offsets, 0, offsets, rulesSize, + area.rulesSize); + } + + return; + } + + double[] resultCoords = new double[coordsSize + area.coordsSize + + intersectPoints.length]; + int[] resultRules = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int[] resultOffsets = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int resultCoordPos = 0; + int resultRulesPos = 0; + boolean isCurrentArea = true; + + IntersectPoint point = intersectPoints[0]; + resultRules[resultRulesPos] = PathIterator.SEG_MOVETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + + do { + resultCoords[resultCoordPos++] = point.getX(); + resultCoords[resultCoordPos++] = point.getY(); + int curIndex = point.getEndIndex(true); + + if (curIndex < 0) { + isCurrentArea = !isCurrentArea; + } else if (area.containsExact(coords[2 * curIndex], + coords[2 * curIndex + 1]) > 0) { + isCurrentArea = false; + } else { + isCurrentArea = true; + } + + IntersectPoint nextPoint = getNextIntersectPoint(intersectPoints, + point, + isCurrentArea); + double[] coords = (isCurrentArea) ? this.coords : area.coords; + int[] offsets = (isCurrentArea) ? this.offsets : area.offsets; + int[] rules = (isCurrentArea) ? this.rules : area.rules; + int offset = point.getRuleIndex(isCurrentArea); + boolean isCopyUntilZero = false; + + if ((point.getRuleIndex(isCurrentArea) > + nextPoint.getRuleIndex(isCurrentArea))) { + int rulesSize = (isCurrentArea) ? this.rulesSize : + area.rulesSize; + resultCoordPos = includeCoordsAndRules(offset + 1, rulesSize, + rules, offsets, + resultRules, + resultOffsets, + resultCoords, coords, + resultRulesPos, + resultCoordPos, + point, isCurrentArea, + false, 0); + resultRulesPos += rulesSize - offset - 1; + offset = 1; + isCopyUntilZero = true; + } + + int length = nextPoint.getRuleIndex(isCurrentArea) - offset + 1; + + if (isCopyUntilZero) { + offset = 0; + } + + resultCoordPos = includeCoordsAndRules(offset, length, rules, + offsets, resultRules, + resultOffsets, resultCoords, + coords, resultRulesPos, + resultCoordPos, point, + isCurrentArea, true, 0); + resultRulesPos += length - offset; + point = nextPoint; + } while (point != intersectPoints[0]); + + resultRules[resultRulesPos++] = PathIterator.SEG_CLOSE; + resultOffsets[resultRulesPos - 1] = resultCoordPos; + this.coords = resultCoords; + this.rules = resultRules; + this.offsets = resultOffsets; + this.coordsSize = resultCoordPos; + this.rulesSize = resultRulesPos; + } + + private void addPolygon(Area area) { + CrossingHelper crossHelper = new CrossingHelper(new double[][] {coords, + area.coords }, + new int[] {coordsSize, + area.coordsSize }); + IntersectPoint[] intersectPoints = crossHelper.findCrossing(); + + if (intersectPoints.length == 0) { + if (area.contains(getBounds2D())) { + copy(area, this); + } else if (!contains(area.getBounds2D())) { + coords = adjustSize(coords, coordsSize + area.coordsSize); + System.arraycopy(area.coords, 0, coords, coordsSize, + area.coordsSize); + coordsSize += area.coordsSize; + rules = adjustSize(rules, rulesSize + area.rulesSize); + System.arraycopy(area.rules, 0, rules, rulesSize, + area.rulesSize); + rulesSize += area.rulesSize; + offsets = adjustSize(offsets, rulesSize + area.rulesSize); + System.arraycopy(area.offsets, 0, offsets, rulesSize, + area.rulesSize); + } + return; + } + + double[] resultCoords = new double[coordsSize + area.coordsSize + + intersectPoints.length]; + int[] resultRules = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int[] resultOffsets = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int resultCoordPos = 0; + int resultRulesPos = 0; + boolean isCurrentArea = true; + + IntersectPoint point = intersectPoints[0]; + resultRules[resultRulesPos] = PathIterator.SEG_MOVETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + + do { + resultCoords[resultCoordPos++] = point.getX(); + resultCoords[resultCoordPos++] = point.getY(); + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos - 2; + int curIndex = point.getEndIndex(true); + if (curIndex < 0) { + isCurrentArea = !isCurrentArea; + } else if (area.containsExact(coords[2 * curIndex], + coords[2 * curIndex + 1]) > 0) { + isCurrentArea = false; + } else { + isCurrentArea = true; + } + + IntersectPoint nextPoint = getNextIntersectPoint(intersectPoints, + point, + isCurrentArea); + double[] coords = (isCurrentArea) ? this.coords : area.coords; + int offset = 2 * point.getEndIndex(isCurrentArea); + + if (nextPoint.getBegIndex(isCurrentArea) < + point.getEndIndex(isCurrentArea)) { + int coordSize = (isCurrentArea) ? this.coordsSize : + area.coordsSize; + int length = coordSize - offset; + System.arraycopy(coords, offset, + resultCoords, resultCoordPos, length); + + for (int i = 0; i < length / 2; i++) { + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + resultCoordPos += 2; + } + + offset = 0; + } + + int length = 2 * nextPoint.getBegIndex(isCurrentArea) - offset + 2; + System.arraycopy(coords, offset, + resultCoords, resultCoordPos, length); + + for (int i = 0; i < length / 2; i++) { + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + resultCoordPos += 2; + } + + point = nextPoint; + } while (point != intersectPoints[0]); + + resultRules[resultRulesPos - 1] = PathIterator.SEG_CLOSE; + resultOffsets[resultRulesPos - 1] = resultCoordPos; + coords = resultCoords; + rules = resultRules; + offsets = resultOffsets; + coordsSize = resultCoordPos; + rulesSize = resultRulesPos; + } + + private void intersectCurvePolygon(Area area) { + CurveCrossingHelper crossHelper = new CurveCrossingHelper( + new double[][] {coords, area.coords }, + new int[] { coordsSize, area.coordsSize }, + new int[][] { rules, area.rules }, + new int[] { rulesSize, area.rulesSize }, + new int[][] { offsets, area.offsets }); + IntersectPoint[] intersectPoints = crossHelper.findCrossing(); + + if (intersectPoints.length == 0) { + if (contains(area.getBounds2D())) { + copy(area, this); + } else if (!area.contains(getBounds2D())) { + reset(); + } + return; + } + + double[] resultCoords = new double[coordsSize + area.coordsSize + + intersectPoints.length]; + int[] resultRules = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int[] resultOffsets = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int resultCoordPos = 0; + int resultRulesPos = 0; + boolean isCurrentArea = true; + + IntersectPoint point = intersectPoints[0]; + IntersectPoint nextPoint = intersectPoints[0]; + resultRules[resultRulesPos] = PathIterator.SEG_MOVETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + + do { + resultCoords[resultCoordPos++] = point.getX(); + resultCoords[resultCoordPos++] = point.getY(); + + int curIndex = point.getEndIndex(true); + if ((curIndex < 0) || (area.containsExact( + coords[2 * curIndex], coords[2 * curIndex + 1]) == 0)) { + isCurrentArea = !isCurrentArea; + } else if (area.containsExact(coords[2 * curIndex], + coords[2 * curIndex + 1]) > 0) { + isCurrentArea = true; + } else { + isCurrentArea = false; + } + + nextPoint = getNextIntersectPoint(intersectPoints, point, isCurrentArea); + double[] coords = (isCurrentArea) ? this.coords : area.coords; + int[] offsets = (isCurrentArea) ? this.offsets : area.offsets; + int[] rules = (isCurrentArea) ? this.rules : area.rules; + int offset = point.getRuleIndex(isCurrentArea); + boolean isCopyUntilZero = false; + + if (point.getRuleIndex(isCurrentArea) > + nextPoint.getRuleIndex(isCurrentArea)) { + int rulesSize = (isCurrentArea) ? this.rulesSize : + area.rulesSize; + resultCoordPos = includeCoordsAndRules(offset + 1, rulesSize, + rules, offsets, + resultRules, + resultOffsets, + resultCoords, coords, + resultRulesPos, + resultCoordPos, point, + isCurrentArea, false, + 1); + resultRulesPos += rulesSize - offset - 1; + offset = 1; + isCopyUntilZero = true; + } + + int length = nextPoint.getRuleIndex(isCurrentArea) - offset + 1; + + if (isCopyUntilZero) { + offset = 0; + isCopyUntilZero = false; + } + if ((length == offset) && + (nextPoint.getRule(isCurrentArea) != PathIterator.SEG_LINETO) && + (nextPoint.getRule(isCurrentArea) != PathIterator.SEG_CLOSE) && + (point.getRule(isCurrentArea) != PathIterator.SEG_LINETO) && + (point.getRule(isCurrentArea) != PathIterator.SEG_CLOSE)) { + + isCopyUntilZero = true; + length++; + } + + resultCoordPos = includeCoordsAndRules(offset, length, rules, + offsets, resultRules, + resultOffsets, resultCoords, + coords, resultRulesPos, + resultCoordPos, nextPoint, + isCurrentArea, true, 1); + resultRulesPos = ((length <= offset) || (isCopyUntilZero)) ? + resultRulesPos + 1 : resultRulesPos + length; + + point = nextPoint; + } while (point != intersectPoints[0]); + + if (resultRules[resultRulesPos - 1] == PathIterator.SEG_LINETO) { + resultRules[resultRulesPos - 1] = PathIterator.SEG_CLOSE; + } else { + resultCoords[resultCoordPos++] = nextPoint.getX(); + resultCoords[resultCoordPos++] = nextPoint.getY(); + resultRules[resultRulesPos++] = PathIterator.SEG_CLOSE; } - return s == null ? false : s.intersects(r); - } + + resultOffsets[resultRulesPos - 1] = resultCoordPos; + coords = resultCoords; + rules = resultRules; + offsets = resultOffsets; + coordsSize = resultCoordPos; + rulesSize = resultRulesPos; + } - public Rectangle getBounds() { - return s == null ? new Rectangle() : s.getBounds(); - } + private void intersectPolygon(Area area) { + CrossingHelper crossHelper = new CrossingHelper(new double[][] {coords, + area.coords }, + new int[] { coordsSize, + area.coordsSize }); + IntersectPoint[] intersectPoints = crossHelper.findCrossing(); - public Rectangle2D getBounds2D() { - return s == null ? new Rectangle2D.Double(): s.getBounds2D(); - } + if (intersectPoints.length == 0) { + if (contains(area.getBounds2D())) { + copy(area, this); + } else if (!area.contains(getBounds2D())) { + reset(); + } + return; + } - public PathIterator getPathIterator(AffineTransform t) { - return s == null ? new NullIterator() : s.getPathIterator(t); - } + double[] resultCoords = new double[coordsSize + area.coordsSize + + intersectPoints.length]; + int[] resultRules = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int[] resultOffsets = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int resultCoordPos = 0; + int resultRulesPos = 0; + boolean isCurrentArea = true; - public PathIterator getPathIterator(AffineTransform t, double flatness) { - return s == null ? new NullIterator() : s.getPathIterator(t, flatness); - } + IntersectPoint point = intersectPoints[0]; + resultRules[resultRulesPos] = PathIterator.SEG_MOVETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + + do { + resultCoords[resultCoordPos++] = point.getX(); + resultCoords[resultCoordPos++] = point.getY(); + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos - 2; + int curIndex = point.getEndIndex(true); - public void add(Area area) throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ - } + if ((curIndex < 0) || + (area.containsExact(coords[2 * curIndex], + coords[2 * curIndex + 1]) == 0)) { + isCurrentArea = !isCurrentArea; + } else if (area.containsExact(coords[2 * curIndex], + coords[2 * curIndex + 1]) > 0) { + isCurrentArea = true; + } else { + isCurrentArea = false; + } - public void exclusiveOr(Area area) throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ - } + IntersectPoint nextPoint = getNextIntersectPoint(intersectPoints, + point, + isCurrentArea); + double[] coords = (isCurrentArea) ? this.coords : area.coords; + int offset = 2 * point.getEndIndex(isCurrentArea); + if ((offset >= 0) && + (nextPoint.getBegIndex(isCurrentArea) < + point.getEndIndex(isCurrentArea))) { + int coordSize = (isCurrentArea) ? this.coordsSize : + area.coordsSize; + int length = coordSize - offset; + System.arraycopy(coords, offset, + resultCoords, resultCoordPos, length); + + for (int i = 0; i < length / 2; i++) { + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + resultCoordPos += 2; + } + + offset = 0; + } + + if (offset >= 0) { + int length = 2 * nextPoint.getBegIndex(isCurrentArea) - + offset + 2; + System.arraycopy(coords, offset, + resultCoords, resultCoordPos, length); + + for (int i = 0; i < length / 2; i++) { + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + resultCoordPos += 2; + } + } - /** - * Extract Rectangle2D from the source shape - * @return a Rectangle2D object if the source shape is rectangle, or null if shape is empty or not rectangle. - */ - Rectangle2D extractRectangle() { - if (s == null) { - return null; - } - float[] points = new float[12]; + point = nextPoint; + } while (point != intersectPoints[0]); + + resultRules[resultRulesPos - 1] = PathIterator.SEG_CLOSE; + resultOffsets[resultRulesPos - 1] = resultCoordPos; + coords = resultCoords; + rules = resultRules; + offsets = resultOffsets; + coordsSize = resultCoordPos; + rulesSize = resultRulesPos; + } + + private void subtractCurvePolygon(Area area) { + CurveCrossingHelper crossHelper = new CurveCrossingHelper( + new double[][] { coords, area.coords }, + new int[] { coordsSize, area.coordsSize }, + new int[][] { rules, area.rules }, + new int[] { rulesSize, area.rulesSize }, + new int[][] { offsets, area.offsets }); + IntersectPoint[] intersectPoints = crossHelper.findCrossing(); + + if (intersectPoints.length == 0 && contains(area.getBounds2D())) { + copy(area, this); + return; + } + + double[] resultCoords = new double[coordsSize + area.coordsSize + + intersectPoints.length]; + int[] resultRules = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int[] resultOffsets = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int resultCoordPos = 0; + int resultRulesPos = 0; + boolean isCurrentArea = true; + + IntersectPoint point = intersectPoints[0]; + resultRules[resultRulesPos] = PathIterator.SEG_MOVETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + + do { + resultCoords[resultCoordPos++] = point.getX(); + resultCoords[resultCoordPos++] = point.getY(); + int curIndex = offsets[point.getRuleIndex(true)] % coordsSize; + + if (area.containsExact(coords[curIndex], + coords[curIndex + 1]) == 0) { + isCurrentArea = !isCurrentArea; + } else if (area.containsExact(coords[curIndex], + coords[curIndex + 1]) > 0) { + isCurrentArea = false; + } else { + isCurrentArea = true; + } + + IntersectPoint nextPoint = (isCurrentArea) ? + getNextIntersectPoint(intersectPoints, point, + isCurrentArea): + getPrevIntersectPoint(intersectPoints, point, + isCurrentArea); + double[] coords = (isCurrentArea) ? this.coords : area.coords; + int[] offsets = (isCurrentArea) ? this.offsets : area.offsets; + int[] rules = (isCurrentArea) ? this.rules : area.rules; + int offset = (isCurrentArea) ? point.getRuleIndex(isCurrentArea) : + nextPoint.getRuleIndex(isCurrentArea); + boolean isCopyUntilZero = false; + + if (((isCurrentArea) && + (point.getRuleIndex(isCurrentArea) > + nextPoint.getRuleIndex(isCurrentArea))) || + ((!isCurrentArea) && + (nextPoint.getRuleIndex(isCurrentArea) > + nextPoint.getRuleIndex(isCurrentArea)))) { + + int rulesSize = (isCurrentArea) ? this.rulesSize : + area.rulesSize; + resultCoordPos = includeCoordsAndRules(offset + 1, rulesSize, + rules, offsets, + resultRules, + resultOffsets, + resultCoords, coords, + resultRulesPos, + resultCoordPos, point, + isCurrentArea, false, + 2); + resultRulesPos += rulesSize - offset - 1; + offset = 1; + isCopyUntilZero = true; + } + + int length = nextPoint.getRuleIndex(isCurrentArea) - offset + 1; + + if (isCopyUntilZero) { + offset = 0; + isCopyUntilZero = false; + } + + resultCoordPos = includeCoordsAndRules(offset, length, rules, + offsets, resultRules, + resultOffsets, resultCoords, + coords, resultRulesPos, + resultCoordPos, point, + isCurrentArea, true, 2); + + if ((length == offset) && + ((rules[offset] == PathIterator.SEG_QUADTO) || + (rules[offset] == PathIterator.SEG_CUBICTO))) { + + resultRulesPos++; + } else { + resultRulesPos = (length < offset || isCopyUntilZero) ? + resultRulesPos + 1 : resultRulesPos + length - offset; + } + + point = nextPoint; + } while (point != intersectPoints[0]); + + resultRules[resultRulesPos++] = PathIterator.SEG_CLOSE; + resultOffsets[resultRulesPos - 1] = resultCoordPos; + coords = resultCoords; + rules = resultRules; + offsets = resultOffsets; + coordsSize = resultCoordPos; + rulesSize = resultRulesPos; + } + + private void subtractPolygon(Area area) { + CrossingHelper crossHelper = new CrossingHelper(new double[][] {coords, + area.coords }, + new int[] { coordsSize, + area.coordsSize }); + IntersectPoint[] intersectPoints = crossHelper.findCrossing(); + + if ((intersectPoints.length == 0) && (contains(area.getBounds2D()))) { + copy(area, this); + return; + } + + double[] resultCoords = new double[coordsSize + area.coordsSize + + intersectPoints.length]; + int[] resultRules = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int[] resultOffsets = new int[rulesSize + area.rulesSize + + intersectPoints.length]; + int resultCoordPos = 0; + int resultRulesPos = 0; + boolean isCurrentArea = true; int count = 0; - PathIterator p = s.getPathIterator(null); - float[] coords = new float[6]; - while(!p.isDone()) { - int type = p.currentSegment(coords); - if (count > 12 || type == PathIterator.SEG_QUADTO || type == PathIterator.SEG_CUBICTO) { - return null; + + IntersectPoint point = intersectPoints[0]; + resultRules[resultRulesPos] = PathIterator.SEG_MOVETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + + do { + resultCoords[resultCoordPos++] = point.getX(); + resultCoords[resultCoordPos++] = point.getY(); + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos - 2; + int curIndex = point.getEndIndex(true); + + if ((curIndex < 0) || + (area.containsExact(coords[2 * curIndex], + coords[2 * curIndex + 1]) > 0)) { + isCurrentArea = !isCurrentArea; + } else if (area.containsExact(coords[2 * curIndex], + coords[2 * curIndex + 1]) > 0) { + isCurrentArea = false; + } else { + isCurrentArea = true; } - points[count++] = coords[0]; - points[count++] = coords[1]; - p.next(); + + IntersectPoint nextPoint = (isCurrentArea) ? + getNextIntersectPoint(intersectPoints, point, isCurrentArea): + getPrevIntersectPoint(intersectPoints, point, isCurrentArea); + double[] coords = (isCurrentArea) ? this.coords : area.coords; + + int offset = (isCurrentArea) ? 2 * point.getEndIndex(isCurrentArea): + 2 * nextPoint.getEndIndex(isCurrentArea); + + if ((offset > 0) && + (((isCurrentArea) && + (nextPoint.getBegIndex(isCurrentArea) < + point.getEndIndex(isCurrentArea))) || + ((!isCurrentArea) && + (nextPoint.getEndIndex(isCurrentArea) < + nextPoint.getBegIndex(isCurrentArea))))) { + + int coordSize = (isCurrentArea) ? this.coordsSize : + area.coordsSize; + int length = coordSize - offset; + + if (isCurrentArea) { + System.arraycopy(coords, offset, + resultCoords, resultCoordPos, length); + } else { + double[] temp = new double[length]; + System.arraycopy(coords, offset, temp, 0, length); + reverseCopy(temp); + System.arraycopy(temp, 0, + resultCoords, resultCoordPos, length); + } + + for (int i = 0; i < length / 2; i++) { + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + resultCoordPos += 2; + } + + offset = 0; + } + + if (offset >= 0) { + int length = (isCurrentArea) ? + 2 * nextPoint.getBegIndex(isCurrentArea) - offset + 2: + 2 * point.getBegIndex(isCurrentArea) - offset + 2; + + if (isCurrentArea) { + System.arraycopy(coords, offset, + resultCoords, resultCoordPos, length); + } else { + double[] temp = new double[length]; + System.arraycopy(coords, offset, temp, 0, length); + reverseCopy(temp); + System.arraycopy(temp, 0, + resultCoords, resultCoordPos, length); + } + + for (int i = 0; i < length / 2; i++) { + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos; + resultCoordPos += 2; + } + } + + point = nextPoint; + count++; + } while (point != intersectPoints[0] && count <= intersectPoints.length); + + if (count > intersectPoints.length) { + reset(); + } else { + resultRules[resultRulesPos - 1] = PathIterator.SEG_CLOSE; + resultOffsets[resultRulesPos - 1] = resultCoordPos; + coords = resultCoords; + rules = resultRules; + offsets = resultOffsets; + coordsSize = resultCoordPos; + rulesSize = resultRulesPos; } - if (points[0] == points[6] && points[6] == points[8] && points[2] == points[4] && - points[1] == points[3] && points[3] == points[9] && points[5] == points[7]) - { - return new Rectangle2D.Float(points[0], points[1], points[2] - points[0], points[7] - points[1]); + } + + private IntersectPoint getNextIntersectPoint(IntersectPoint[] iPoints, + IntersectPoint isectPoint, + boolean isCurrentArea) { + + int endIndex = isectPoint.getEndIndex(isCurrentArea); + if (endIndex < 0) { + return iPoints[Math.abs(endIndex) - 1]; + } + + IntersectPoint firstIsectPoint = null; + IntersectPoint nextIsectPoint = null; + for (IntersectPoint point : iPoints) { + int begIndex = point.getBegIndex(isCurrentArea); + + if (begIndex >= 0) { + if (firstIsectPoint == null) { + firstIsectPoint = point; + } else if (begIndex < firstIsectPoint + .getBegIndex(isCurrentArea)) { + firstIsectPoint = point; + } + } + + if (endIndex <= begIndex) { + if (nextIsectPoint == null) { + nextIsectPoint = point; + } else if (begIndex < + nextIsectPoint.getBegIndex(isCurrentArea)) { + nextIsectPoint = point; + } + } + } + + return (nextIsectPoint != null) ? nextIsectPoint : firstIsectPoint; + } + + private IntersectPoint getPrevIntersectPoint(IntersectPoint[] iPoints, + IntersectPoint isectPoint, + boolean isCurrentArea) { + + int begIndex = isectPoint.getBegIndex(isCurrentArea); + + if (begIndex < 0) { + return iPoints[Math.abs(begIndex) - 1]; + } + + IntersectPoint firstIsectPoint = null; + IntersectPoint predIsectPoint = null; + for (IntersectPoint point : iPoints) { + int endIndex = point.getEndIndex(isCurrentArea); + + if (endIndex >= 0) { + if (firstIsectPoint == null) { + firstIsectPoint = point; + } else if (endIndex < firstIsectPoint + .getEndIndex(isCurrentArea)) { + firstIsectPoint = point; + } + } + + if (endIndex <= begIndex) { + if (predIsectPoint == null) { + predIsectPoint = point; + } else if (endIndex > + predIsectPoint.getEndIndex(isCurrentArea)) { + predIsectPoint = point; + } + } + } + + return (predIsectPoint != null) ? predIsectPoint : firstIsectPoint; + } + + + private int includeCoordsAndRules(int offset, int length, int[] rules, + int[] offsets, int[] resultRules, + int[] resultOffsets, double[] resultCoords, + double[] coords, int resultRulesPos, + int resultCoordPos, IntersectPoint point, + boolean isCurrentArea, boolean way, + int operation) { + + double[] temp = new double[8 * length]; + int coordsCount = 0; + boolean isMoveIndex = true; + boolean isMoveLength = true; + boolean additional = false; + + if (length <= offset) { + for (int i = resultRulesPos; i < resultRulesPos + 1; i++) { + resultRules[i] = PathIterator.SEG_LINETO; + } + } else { + int j = resultRulesPos; + for (int i = offset; i < length; i++) { + resultRules[j++] = PathIterator.SEG_LINETO; + } + } + + if ((length == offset) && + ((rules[offset] == PathIterator.SEG_QUADTO) || + (rules[offset] == PathIterator.SEG_CUBICTO))) { + length++; + additional = true; + } + for (int i = offset; i < length; i++) { + int index = offsets[i]; + + if (!isMoveIndex) { + index -= 2; + } + + if (!isMoveLength) { + length++; + isMoveLength = true; + } + + switch (rules[i]) { + case PathIterator.SEG_MOVETO: + isMoveIndex = false; + isMoveLength = false; + break; + case PathIterator.SEG_LINETO: + case PathIterator.SEG_CLOSE: + resultRules[resultRulesPos] = PathIterator.SEG_LINETO; + resultOffsets[resultRulesPos++] = resultCoordPos + 2; + boolean isLeft = CrossingHelper.compare(coords[index], + coords[index + 1], point.getX(), point.getY()) > 0; + + if (way || !isLeft) { + temp[coordsCount++] = coords[index]; + temp[coordsCount++] = coords[index + 1]; + } + break; + case PathIterator.SEG_QUADTO: + resultRules[resultRulesPos] = PathIterator.SEG_QUADTO; + resultOffsets[resultRulesPos++] = resultCoordPos + 4; + double[] coefs = new double[] { coords[index - 2], + coords[index - 1], coords[index], coords[index + 1], + coords[index + 2], coords[index + 3] }; + isLeft = CrossingHelper.compare(coords[index - 2], + coords[index - 1], point.getX(), point.getY()) > 0; + + if ((!additional) && (operation == 0 || operation == 2)) { + isLeft = !isLeft; + way = false; + } + GeometryUtil + .subQuad(coefs, point.getParam(isCurrentArea), isLeft); + + if (way || isLeft) { + temp[coordsCount++] = coefs[2]; + temp[coordsCount++] = coefs[3]; + } else { + System.arraycopy(coefs, 2, temp, coordsCount, 4); + coordsCount += 4; + } + break; + case PathIterator.SEG_CUBICTO: + resultRules[resultRulesPos] = PathIterator.SEG_CUBICTO; + resultOffsets[resultRulesPos++] = resultCoordPos + 6; + coefs = new double[] {coords[index - 2], coords[index - 1], + coords[index], coords[index + 1], + coords[index + 2], coords[index + 3], + coords[index + 4], coords[index + 5] }; + isLeft = CrossingHelper.compare(coords[index - 2], + coords[index - 1], point.getX(), point.getY()) > 0; + GeometryUtil.subCubic(coefs, point.getParam(isCurrentArea), + !isLeft); + + if (isLeft) { + System.arraycopy(coefs, 2, temp, coordsCount, 6); + coordsCount += 6; + } else { + System.arraycopy(coefs, 2, temp, coordsCount, 4); + coordsCount += 4; + } + break; + } + } + + if (operation == 2 && !isCurrentArea && coordsCount > 2) { + reverseCopy(temp); + System.arraycopy(temp, 0, resultCoords, resultCoordPos, coordsCount); + } else { + System.arraycopy(temp, 0, resultCoords, resultCoordPos, coordsCount); + } + + return (resultCoordPos + coordsCount); + } + + // the method check up the array size and necessarily increases it. + private static double[] adjustSize(double[] array, int newSize) { + if (newSize <= array.length) { + return array; + } + double[] newArray = new double[2 * newSize]; + System.arraycopy(array, 0, newArray, 0, array.length); + return newArray; + } + + private static int[] adjustSize(int[] array, int newSize) { + if (newSize <= array.length) { + return array; + } + int[] newArray = new int[2 * newSize]; + System.arraycopy(array, 0, newArray, 0, array.length); + return newArray; + } + + private void copy(Area src, Area dst) { + dst.coordsSize = src.coordsSize; + dst.coords = src.coords.clone(); + dst.rulesSize = src.rulesSize; + dst.rules = src.rules.clone(); + dst.moveToCount = src.moveToCount; + dst.offsets = src.offsets.clone(); + } + + private int containsExact(double x, double y) { + PathIterator pi = getPathIterator(null); + int crossCount = Crossing.crossPath(pi, x, y); + + if (Crossing.isInsideEvenOdd(crossCount)) { + return 1; } - return null; - } - - public void intersect(Area area) { - Rectangle2D src1 = extractRectangle(); - Rectangle2D src2 = area.extractRectangle(); - if (src1 != null && src2 != null) { - Rectangle2D.intersect(src1, src2, (Rectangle2D)s); + + double[] segmentCoords = new double[6]; + double[] resultPoints = new double[6]; + int rule; + double curX = -1; + double curY = -1; + double moveX = -1; + double moveY = -1; + + for (pi = getPathIterator(null); !pi.isDone(); pi.next()) { + rule = pi.currentSegment(segmentCoords); + switch (rule) { + case PathIterator.SEG_MOVETO: + moveX = curX = segmentCoords[0]; + moveY = curY = segmentCoords[1]; + break; + case PathIterator.SEG_LINETO: + if (GeometryUtil.intersectLines(curX, curY, + segmentCoords[0], segmentCoords[1], x, y, x, y, + resultPoints) != 0) { + return 0; + } + curX = segmentCoords[0]; + curY = segmentCoords[1]; + break; + case PathIterator.SEG_QUADTO: + if (GeometryUtil.intersectLineAndQuad(x, y, x, y, + curX, curY, segmentCoords[0], segmentCoords[1], + segmentCoords[2], segmentCoords[3], + resultPoints) > 0) { + return 0; + } + curX = segmentCoords[2]; + curY = segmentCoords[3]; + break; + case PathIterator.SEG_CUBICTO: + if (GeometryUtil.intersectLineAndCubic(x, y, x, y, + curX, curY, segmentCoords[0], segmentCoords[1], + segmentCoords[2], segmentCoords[3], segmentCoords[4], + segmentCoords[5], resultPoints) > 0) { + return 0; + } + curX = segmentCoords[4]; + curY = segmentCoords[5]; + break; + case PathIterator.SEG_CLOSE: + if (GeometryUtil.intersectLines(curX, curY, moveX, moveY, + x, y, x, y, resultPoints) != 0) { + return 0; + } + curX = moveX; + curY = moveY; + break; + } } + return -1; } - public void subtract(Area area) throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ + private void reverseCopy(double[] coords) { + double[] temp = new double[coords.length]; + System.arraycopy(coords, 0, temp, 0, coords.length); + + for (int i = 0; i < coords.length;) { + coords[i] = temp[coords.length - i - 2]; + coords[i + 1] = temp[coords.length - i - 1]; + i = i + 2; + } } + + // the internal class implements PathIterator + private class AreaPathIterator implements PathIterator { - public boolean isEmpty() throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ - } + AffineTransform t; + Area area; + int curRuleIndex = 0; + int curCoordIndex = 0; - public boolean isPolygonal() throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ - } + AreaPathIterator(Area area) { + this(area, null); + } - public boolean isRectangular() throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ - } + AreaPathIterator(Area area, AffineTransform t) { + this.area = area; + this.t = t; + } - public boolean isSingular() throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ - } + public int getWindingRule() { + return WIND_EVEN_ODD; + } - public void reset() throws org.apache.harmony.luni.util.NotImplementedException { - throw new RuntimeException("Not implemented"); //$NON-NLS-1$ - } + public boolean isDone() { + return curRuleIndex >= rulesSize; + } - public void transform(AffineTransform t) { - s = t.createTransformedShape(s); - } + public void next() { + switch (rules[curRuleIndex]) { + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + curCoordIndex += 2; + break; + case PathIterator.SEG_QUADTO: + curCoordIndex += 4; + break; + case PathIterator.SEG_CUBICTO: + curCoordIndex += 6; + break; + } + curRuleIndex++; + } - public Area createTransformedArea(AffineTransform t) { - return s == null ? new Area() : new Area(t.createTransformedShape(s)); - } + public int currentSegment(double[] c) { + if (isDone()) { + throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ + } + switch (rules[curRuleIndex]) { + case PathIterator.SEG_CUBICTO: + c[4] = coords[curCoordIndex + 4]; + c[5] = coords[curCoordIndex + 5]; + case PathIterator.SEG_QUADTO: + c[2] = coords[curCoordIndex + 2]; + c[3] = coords[curCoordIndex + 3]; + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + c[0] = coords[curCoordIndex]; + c[1] = coords[curCoordIndex + 1]; + } + return rules[curRuleIndex]; + } - @Override - public Object clone() { - return new Area(this); - } - -} + public int currentSegment(float[] c) { + double[] doubleCoords = new double[6]; + int rule = currentSegment(doubleCoords); + + for (int i = 0; i < 6; i++) { + c[i] = (float) doubleCoords[i]; + } + return rule; + } + } +} \ No newline at end of file Index: src/main/java/common/org/apache/harmony/awt/internal/CrossingHelper.java =================================================================== --- src/main/java/common/org/apache/harmony/awt/internal/CrossingHelper.java (revision 0) +++ src/main/java/common/org/apache/harmony/awt/internal/CrossingHelper.java (revision 0) @@ -0,0 +1,327 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.awt.internal; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + +public class CrossingHelper { + + private double[][] coords; + private int[] sizes; + private List isectPoints = new ArrayList(); + + public CrossingHelper(double[][] coords, int[] sizes) { + this.coords = coords; + this.sizes = sizes; + } + + public IntersectPoint[] findCrossing() { + int pointCount1 = sizes[0] / 2; + int pointCount2 = sizes[1] / 2; + int[] indices = new int[pointCount1 + pointCount2]; + + for(int i = 0; i < pointCount1 + pointCount2; i++) { + indices[i] = i; + } + + sort(coords[0], pointCount1, coords[1], pointCount2, indices); + // the set for the shapes edges storing + List edges = new ArrayList(); + Edge edge; + int begIndex, endIndex; + int areaNumber; + + for (int i = 0; i < indices.length; i++) { + if (indices[i] < pointCount1) { + begIndex = indices[i]; + endIndex = indices[i] - 1; + + if (endIndex < 0) { + endIndex = pointCount1 - 1; + } + + areaNumber = 0; + } else if (indices[i] < pointCount1 + pointCount2) { + begIndex = indices[i] - pointCount1; + endIndex = indices[i] - 1 - pointCount1; + + if (endIndex < 0) { + endIndex = pointCount2 - 1; + } + + areaNumber = 1; + } else { + throw new IndexOutOfBoundsException(); + } + + if (!removeEdge(edges, begIndex, endIndex)) { + edge = new Edge(begIndex, endIndex, areaNumber); + intersectShape(edges, coords[0], pointCount1, + coords[1], pointCount2, edge); + edges.add(edge); + } + + begIndex = indices[i]; + endIndex = indices[i] + 1; + + if ((begIndex < pointCount1) && (endIndex == pointCount1)) { + endIndex = 0; + } else if ((begIndex >= pointCount1) && + (endIndex == (pointCount2 + pointCount1))) { + endIndex = pointCount1; + } + + if (endIndex < pointCount1) { + areaNumber = 0; + } else { + areaNumber = 1; + endIndex -= pointCount1; + begIndex -= pointCount1; + } + + if (!removeEdge(edges, begIndex, endIndex)) { + edge = new Edge(begIndex, endIndex, areaNumber); + intersectShape(edges, coords[0], pointCount1, + coords[1], pointCount2, edge); + edges.add(edge); + } + } + + return isectPoints.toArray(new IntersectPoint[isectPoints.size()]); + } + + private boolean removeEdge(List edges, int begIndex, int endIndex) { + + for (Edge edge : edges) { + if (edge.reverseCompare(begIndex, endIndex)) { + edges.remove(edge); + return true; + } + } + + return false; + } + + // return the quantity of intersect points + private void intersectShape(List edges, + double[] coords1, int length1, + double[] coords2, int length2, + Edge initEdge) { + int areaOfEdge1, areaOfEdge2; + int initBegin, initEnd; + int addBegin, addEnd; + double x1, y1, x2, y2, x3, y3, x4, y4; + double[] point = new double[2]; + Edge edge; + + if (initEdge.areaNumber == 0) { + x1 = coords1[2* initEdge.begIndex]; + y1 = coords1[2* initEdge.begIndex + 1]; + x2 = coords1[2* initEdge.endIndex]; + y2 = coords1[2* initEdge.endIndex + 1]; + areaOfEdge1 = 0; + } else { + x1 = coords2[2* initEdge.begIndex]; + y1 = coords2[2* initEdge.begIndex + 1]; + x2 = coords2[2* initEdge.endIndex]; + y2 = coords2[2* initEdge.endIndex + 1]; + areaOfEdge1 = 1; + } + + for (Iterator iter = edges.iterator(); iter.hasNext(); ) { + edge = (Edge) iter.next(); + + if (edge.areaNumber == 0) { + x3 = coords1[2* edge.begIndex]; + y3 = coords1[2* edge.begIndex + 1]; + x4 = coords1[2* edge.endIndex]; + y4 = coords1[2* edge.endIndex + 1]; + areaOfEdge2 = 0; + } else { + x3 = coords2[2* edge.begIndex]; + y3 = coords2[2* edge.begIndex + 1]; + x4 = coords2[2* edge.endIndex]; + y4 = coords2[2* edge.endIndex + 1]; + areaOfEdge2 = 1; + } + + if ((areaOfEdge1 != areaOfEdge2) && + (GeometryUtil.intersectLines( + x1, y1, x2, y2, x3, y3, x4, y4, point) == 1) && + (!containsPoint(point))) { + + if (initEdge.areaNumber == 0) { + initBegin = initEdge.begIndex; + initEnd = initEdge.endIndex; + addBegin = edge.begIndex; + addEnd = edge.endIndex; + } else { + initBegin = edge.begIndex; + initEnd = edge.endIndex; + addBegin = initEdge.begIndex; + addEnd = initEdge.endIndex; + } + + if (((initEnd == length1 - 1) && + (initBegin == 0 && initEnd > initBegin)) || + (((initEnd != length1 - 1) || (initBegin != 0)) && + ((initBegin != length1 - 1) || (initEnd != 0)) && + (initBegin > initEnd))) { + + int temp = initBegin; + initBegin = initEnd; + initEnd = temp; + } + + if (((addEnd == length2 - 1) && (addBegin == 0) && (addEnd > addBegin)) || + (((addEnd != length2 - 1) || (addBegin != 0)) && + ((addBegin != length2 - 1) || (addEnd != 0)) && (addBegin > addEnd))) { + + int temp = addBegin; + addBegin = addEnd; + addEnd = temp; + } + + IntersectPoint ip; + for (Iterator i = isectPoints.iterator(); i.hasNext(); ) { + ip = (IntersectPoint)i.next(); + + if ((initBegin == ip.getBegIndex(true)) && + (initEnd == ip.getEndIndex(true))) { + + if (compare(ip.getX(), ip.getY(), point[0], point[1]) > 0) { + initEnd = - (isectPoints.indexOf(ip) + 1); + ip.setBegIndex1(-(isectPoints.size() + 1)); + } else { + initBegin = - (isectPoints.indexOf(ip) + 1); + ip.setEndIndex1(-(isectPoints.size() + 1)); + } + } + + if ((addBegin == ip.getBegIndex(false)) && + (addEnd == ip.getEndIndex(false))) { + + if (compare(ip.getX(), ip.getY(), point[0], point[1]) > 0) { + addEnd = - (isectPoints.indexOf(ip) + 1); + ip.setBegIndex2(-(isectPoints.size() + 1)); + } else { + addBegin = - (isectPoints.indexOf(ip) + 1); + ip.setEndIndex2(-(isectPoints.size() + 1)); + } + } + } + + isectPoints.add(new IntersectPoint(initBegin, initEnd, + addBegin, addEnd, + point[0], point[1])); + } + } + } + + // the array sorting + private static void sort(double[] coords1, int length1, + double[] coords2, int length2, + int[] array) { + int temp; + int length = length1 + length2; + double x1, y1, x2, y2; + + for (int i = 1; i < length; i++) { + if (array[i-1] < length1) { + x1 = coords1[2*array[i-1]]; + y1 = coords1[2*array[i-1] + 1]; + } else { + x1 = coords2[2*(array[i-1] - length1)]; + y1 = coords2[2*(array[i-1] - length1) + 1]; + } + if (array[i] < length1) { + x2 = coords1[2*array[i]]; + y2 = coords1[2*array[i] + 1]; + } else { + x2 = coords2[2*(array[i] - length1)]; + y2 = coords2[2*(array[i] - length1) + 1]; + } + int j = i; + while (j > 0 && compare(x1, y1, x2, y2) <= 0) { + temp = array[j]; + array[j] = array[j-1]; + array[j-1] = temp; + j--; + if (j > 0) { + if (array[j-1] < length1) { + x1 = coords1[2*array[j-1]]; + y1 = coords1[2*array[j-1] + 1]; + } else { + x1 = coords2[2*(array[j-1] - length1)]; + y1 = coords2[2*(array[j-1] - length1) + 1]; + } + if (array[j] < length1) { + x2 = coords1[2*array[j]]; + y2 = coords1[2*array[j] + 1]; + } else { + x2 = coords2[2*(array[j] - length1)]; + y2 = coords2[2*(array[j] - length1) + 1]; + } + } + } + } + } + + private boolean containsPoint(double[] point) { + IntersectPoint ipoint; + + for (Iterator i = isectPoints.iterator(); i.hasNext(); ) { + ipoint = (IntersectPoint)i.next(); + + if (ipoint.getX() == point[0] && ipoint.getY() == point[1]) { + return true; + } + } + + return false; + } + + public static int compare(double x1, double y1, double x2, double y2) { + + if ((x1 < x2) || (x1 == x2 && y1 < y2)) { + return 1; + } else if (x1 == x2 && y1 == y2) { + return 0; + } + + return -1; + } + + private static class Edge { + int begIndex; + int endIndex; + int areaNumber; + + public Edge(int begIndex, int endIndex, int areaNumber) { + this.begIndex = begIndex; + this.endIndex = endIndex; + this.areaNumber = areaNumber; + } + + public boolean reverseCompare (int begIndex, int endIndex) { + return this.begIndex == endIndex && this.endIndex == begIndex; + } + } +} \ No newline at end of file Index: src/main/java/common/org/apache/harmony/awt/internal/GeometryUtil.java =================================================================== --- src/main/java/common/org/apache/harmony/awt/internal/GeometryUtil.java (revision 0) +++ src/main/java/common/org/apache/harmony/awt/internal/GeometryUtil.java (revision 0) @@ -0,0 +1,510 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.awt.internal; + +import org.apache.harmony.awt.gl.Crossing; + +public class GeometryUtil { + static final double EPSILON = Math.pow(10, -15); + + public static int intersectLinesWithParams(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, + double[] params) { + double dx = x4 - x3; + double dy = y4 - y3; + double d = dx * (y2 - y1) - dy * (x2 - x1); + // double comparison + if (Math.abs(d) < EPSILON) { + return 0; + } + + params[0] = (- dx * (y1 - y3) + dy * (x1 - x3)) / d; + + if (dx != 0) { + params[1] = (line(params[0], x1, x2) - x3) / dx; + } else if (dy != 0) { + params[1] = (line(params[0], y1, y2) - y3) / dy; + } else { + params[1] = 0.0; + } + + if (params[0] >= 0 && params[0] <= 1 && params[1] >= 0 && params[1] <= 1) { + return 1; + } + + return 0; + } + + /** + * The method checks up if line (x1, y1) - (x2, y2) and line (x3, y3) - (x4, y4) + * intersect. If lines intersect then the result parameters are saved to point + * array. The size of array point must be at least 2. + * @returns the method returns 1 if two lines intersect in the defined interval, + * otherwise 0 + */ + public static int intersectLines(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, + double[] point) { + double A1 = -(y2 - y1); + double B1 = (x2 - x1); + double C1 = x1 * y2 - x2 * y1; + double A2 = - (y4 - y3); + double B2 = (x4 - x3); + double C2 = x3 * y4 - x4 * y3; + double coefParallel = A1 * B2 - A2 * B1; + // double comparison + if (x3 == x4 && y3 == y4 && (A1 * x3 + B1 * y3 + C1 == 0) && + (x3 >= Math.min(x1, x2)) && (x3 <= Math.max(x1, x2)) && + (y3 >= Math.min(y1, y2)) && (y3 <= Math.max(y1, y2))) { + return 1; + } + if (Math.abs(coefParallel) < EPSILON) { + return 0; + } + point[0] = (B1 * C2 - B2 * C1) / coefParallel; + point[1] = (A2 * C1 - A1 * C2) / coefParallel; + if (point[0] >= Math.min(x1, x2) && point[0] >= Math.min(x3, x4) && + point[0] <= Math.max(x1, x2) && point[0] <= Math.max(x3, x4) && + point[1] >= Math.min(y1, y2) && point[1] >= Math.min(y3, y4) && + point[1] <= Math.max(y1, y2) && point[1] <= Math.max(y3, y4)) { + return 1; + } + return 0; + } + + /** + * It checks up if there is intersection of the line (x1, y1) - (x2, y2) and + * the quad curve (qx1, qy1) - (qx2, qy2) - (qx3, qy3). The parameters of the intersection + * area saved to params array. Therefore the params size must be at learst 4. + * @return The method returns the quantity of roots lied in the defined interval + */ + public static int intersectLineAndQuad(double x1, double y1, double x2, double y2, + double qx1, double qy1, double qx2, double qy2, + double qx3, double qy3, double[] params) { + double[] eqn = new double[3]; + double[] t = new double[2]; + double[] s = new double[2]; + double dy = y2 - y1; + double dx = x2 - x1; + int quantity = 0; + int count = 0; + + eqn[0] = dy * (qx1 - x1) - dx * (qy1 - y1); + eqn[1] = 2 * dy * (qx2 - qx1) - 2 * dx * (qy2 - qy1); + eqn[2] = dy * (qx1 - 2 * qx2 + qx3) - dx *(qy1 -2 * qy2 + qy3); + + if ((count = Crossing.solveQuad(eqn, t)) == 0) { + return 0; + } + + for (int i = 0; i < count; i++) { + if (dx != 0) { + s[i] = (quad(t[i], qx1, qx2, qx3) - x1) / dx; + } else if (dy != 0) { + s[i] = (quad(t[i], qy1, qy2, qy3) - y1) / dy; + } else { + s[i] = 0.0; + } + if (t[i] >= 0 && t[i] <= 1 && s[i] >= 0 && s[i] <= 1) { + params[2 * quantity] = t[i]; + params[2 * quantity + 1] = s[i]; + ++quantity; + } + } + + return quantity; + } + + /** + * It checks up if the line (x1, y1) - (x2, y2) and + * the cubic curve (cx1, cy1) - (cx2, cy2) - (cx3, cy3) - (cx4, cy4). + * The points of the intersection is saved to points array. + * Therefore the points size must be at learst 6. + * @return The method returns the quantity of roots lied in the defined interval + */ + public static int intersectLineAndCubic(double x1, double y1, double x2, double y2, + double cx1, double cy1, double cx2, double cy2, + double cx3, double cy3, double cx4, double cy4, + double[] params) { + double[] eqn = new double[4]; + double[] t = new double[3]; + double[] s = new double[3]; + double dy = y2 - y1; + double dx = x2 - x1; + int quantity = 0; + int count = 0; + + eqn[0] = (cy1 - y1) * dx + (x1 - cx1) * dy; + eqn[1] = - 3 * (cy1 - cy2) * dx + 3 * (cx1 - cx2) * dy ; + eqn[2] = (3 * cy1 - 6 * cy2 + 3 * cy3) * dx - (3 * cx1 - 6 * cx2 + 3 * cx3) * dy; + eqn[3] = (- 3 * cy1 + 3 * cy2 - 3 * cy3 + cy4) * dx + + (3 * cx1 - 3 * cx2 + 3 * cx3 - cx4) * dy; + + if ((count = Crossing.solveCubic(eqn, t)) == 0) { + return 0; + } + + for (int i = 0; i < count; i++) { + if (dx != 0) { + s[i] = (cubic(t[i], cx1, cx2, cx3, cx4) - x1) / dx; + } else if (dy != 0) { + s[i] = (cubic(t[i], cy1, cy2, cy3, cy4) - y1) / dy; + } else { + s[i] = 0.0; + } + if (t[i] >= 0 && t[i] <= 1 && s[i] >= 0 && s[i] <= 1) { + params[2 * quantity] = t[i]; + params[2 * quantity + 1] = s[i]; + ++quantity; + } + } + + return quantity; + } + + /** + * The method checks up if two quads (x1, y1) - (x2, y2) - (x3, y3) and + * (qx1, qy1) - (qx2, qy2) - (qx3, qy3) intersect. The result is saved to + * point array. Size of points should be at learst 4. + * @return the method returns the quantity of roots lied in the interval + */ + public static int intersectQuads(double x1, double y1, double x2, double y2, + double x3, double y3, double qx1, double qy1, + double qx2, double qy2, double qx3, double qy3, + double[] params) { + + double[] initParams = new double[2]; + double[] xCoefs1 = new double[3]; + double[] yCoefs1 = new double[3]; + double[] xCoefs2 = new double[3]; + double[] yCoefs2 = new double[3]; + int quantity = 0; + + xCoefs1[0] = x1 - 2 * x2 + x3; + xCoefs1[1] = - 2 * x1 + 2 * x2; + xCoefs1[2] = x1; + + yCoefs1[0] = y1 - 2 * y2 + y3; + yCoefs1[1] = - 2 * y1 + 2 * y2; + yCoefs1[2] = y1; + + xCoefs2[0] = qx1 - 2 * qx2 + qx3; + xCoefs2[1] = - 2 * qx1 + 2 * qx2; + xCoefs2[2] = qx1; + + yCoefs2[0] = qy1 - 2 * qy2 + qy3; + yCoefs2[1] = - 2 * qy1 + 2 * qy2; + yCoefs2[2] = qy1; + + // initialize params[0] and params[1] + params[0] = params[1] = 0.25; + quadNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, initParams); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + // initialize params + params[0] = params[1] = 0.75; + quadNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + + return quantity; + } + + /** + * It checks up if the quad (x1, y1) - (x2, y2) - (x3, y3) and + * the cubic (cx1, cy1) - (cx2, cy2) - (cx3, cy3) - (cx4, cy4) curves intersect. + * The points of the intersection is saved to points array. + * The points size should be at learst 6. + * @return The method returns the quantity of the intersection points + * lied in the interval. + */ + public static int intersectQuadAndCubic(double qx1, double qy1, double qx2, double qy2, + double qx3, double qy3, double cx1, double cy1, + double cx2, double cy2, double cx3, double cy3, + double cx4, double cy4, + double[] params) { + int quantity = 0; + double[] initParams = new double[3]; + double[] xCoefs1 = new double[3]; + double[] yCoefs1 = new double[3]; + double[] xCoefs2 = new double[4]; + double[] yCoefs2 = new double[4]; + xCoefs1[0] = qx1 - 2 * qx2 + qx3; + xCoefs1[1] = 2* qx2 - 2 * qx1; + xCoefs1[2] = qx1; + + yCoefs1[0] = qy1 - 2 * qy2 + qy3; + yCoefs1[1] = 2* qy2 - 2 * qy1; + yCoefs1[2] = qy1; + + xCoefs2[0] = - cx1 + 3 * cx2 - 3 * cx3 + cx4; + xCoefs2[1] = 3 * cx1 - 6 * cx2 + 3 * cx3; + xCoefs2[2] = - 3 * cx1 + 3 * cx2; + xCoefs2[3] = cx1; + + yCoefs2[0] = - cy1 + 3 * cy2 - 3 * cy3 + cy4; + yCoefs2[1] = 3 * cy1 - 6 * cy2 + 3 * cy3; + yCoefs2[2] = - 3 * cy1 + 3 * cy2; + yCoefs2[3] = cy1; + + // initialize params[0] and params[1] + params[0] = params[1] = 0.25; + quadAndCubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, initParams); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + // initialize params + params[0] = params[1] = 0.5; + quadAndCubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + + params[0] = params[1] = 0.75; + quadAndCubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + return quantity; + } + + /** + * The method checks up if two cubic curves (x1, y1) - (x2, y2) - (x3, y3) - (x4, y4) + * and (cx1, cy1) - (cx2, cy2) - (cx3, cy3) - (cx4, cy4) intersect. The result is saved to + * point array. Size of points should be at learst 6. + * @return the method returns the quantity of the intersection points lied in the interval + */ + public static int intersectCubics(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, + double cx1, double cy1, double cx2, double cy2, + double cx3, double cy3, double cx4, double cy4, + double[] params) { + + int quantity = 0; + double[] initParams = new double[3]; + double[] xCoefs1 = new double[4]; + double[] yCoefs1 = new double[4]; + double[] xCoefs2 = new double[4]; + double[] yCoefs2 = new double[4]; + xCoefs1[0] = - x1 + 3 * x2 - 3 * x3 + x4; + xCoefs1[1] = 3 * x1 - 6 * x2 + 3 * x3; + xCoefs1[2] = - 3 * x1 + 3 * x2; + xCoefs1[3] = x1; + + yCoefs1[0] = - y1 + 3 * y2 - 3 * y3 + y4; + yCoefs1[1] = 3 * y1 - 6 * y2 + 3 * y3; + yCoefs1[2] = - 3 * y1 + 3 * y2; + yCoefs1[3] = y1; + + xCoefs2[0] = - cx1 + 3 * cx2 - 3 * cx3 + cx4; + xCoefs2[1] = 3 * cx1 - 6 * cx2 + 3 * cx3; + xCoefs2[2] = - 3 * cx1 + 3 * cx2; + xCoefs2[3] = cx1; + + yCoefs2[0] = - cy1 + 3 * cy2 - 3 * cy3 + cy4; + yCoefs2[1] = 3 * cy1 - 6 * cy2 + 3 * cy3; + yCoefs2[2] = - 3 * cy1 + 3 * cy2; + yCoefs2[3] = cy1; + + // TODO + params[0] = params[1] = 0.25; + cubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, initParams); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + + params[0] = params[1] = 0.5; + cubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + + params[0] = params[1] = 0.75; + cubicNewton(xCoefs1, yCoefs1, xCoefs2, yCoefs2, params); + if (initParams[0] <= 1 && initParams[0] >= 0 && + initParams[1] >=0 && initParams[1] <=1) { + params[2 * quantity] = initParams[0]; + params[2 * quantity + 1] = initParams[1]; + ++quantity; + } + return quantity; + } + + public static double line(double t, double x1, double x2) { + return x1 * (1.0 - t) + x2 * t; + } + + public static double quad(double t, double x1, double x2, double x3) { + return x1 * (1.0 - t) * (1.0 - t) + 2.0 * x2 * t * (1.0 - t) + x3 * t * t; + } + + public static double cubic(double t, double x1, double x2, double x3, double x4) { + return x1 * (1.0 - t) * (1.0 - t) * (1.0 - t) + + 3.0 * x2 * (1.0 - t) * (1.0 - t) * t + + 3.0 * x3 * (1.0 - t) * t * t + + x4 * t * t * t; + } + + // x, y - the coordinates of new vertex + // t0 - ? + public static void subQuad(double coef[], double t0, boolean left) { + if (left) { + coef[2] = (1 - t0) * coef[0] + t0 * coef[2]; + coef[3] = (1 - t0) * coef[1] + t0 * coef[3]; + } else { + coef[2] = (1 - t0) * coef[2] + t0 * coef[4]; + coef[3] = (1 - t0) * coef[3] + t0 * coef[5]; + } + } + + public static void subCubic(double coef[], double t0, boolean left) { + if (left) { + coef[2] = (1 - t0) * coef[0] + t0 * coef[2]; + coef[3] = (1 - t0) * coef[1] + t0 * coef[3]; + } else { + coef[4] = (1 - t0) * coef[4] + t0 * coef[6]; + coef[5] = (1 - t0) * coef[5] + t0 * coef[7]; + } + } + + private static void cubicNewton(double xCoefs1[], double yCoefs1[], double xCoefs2[], + double yCoefs2[], double[] params) { + double t = 0.0, s = 0.0; + double t1 = params[0]; + double s1 = params[1]; + double d, dt, ds; + + while (Math.sqrt((t - t1) * (t - t1) + (s - s1) * (s - s1)) > EPSILON) { + d = -(3 * t * t * xCoefs1[0] + 2 * t * xCoefs1[1] + xCoefs1[2]) * + (3 * s * s * yCoefs2[0] + 2 * s * yCoefs2[1] + yCoefs2[2]) + + (3 * t * t * yCoefs1[0] + 2 * t * yCoefs1[1] + yCoefs1[2]) * + (3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]); + + dt = (t * t * t * xCoefs1[0] + t * t * xCoefs1[1] + t * xCoefs1[2] + + xCoefs1[3] - s * s * s * xCoefs2[0] - s * s * xCoefs2[1] - + s * xCoefs2[2] - xCoefs2[3]) * (- 3 * s * s * yCoefs2[0] - + 2 * s * yCoefs2[1] - yCoefs2[2]) + (t * t * t * yCoefs1[0] + + t * t * yCoefs1[1] + t * yCoefs1[2] + yCoefs1[3] - s * s *s * yCoefs2[0] - + s * s * yCoefs2[1] - s * yCoefs2[2] - yCoefs2[3]) * + (3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]); + + ds = (3 * t * t * xCoefs1[0] + 2 * t * xCoefs1[1] + xCoefs1[2]) * + (t * t * t * yCoefs1[0] + t * t * yCoefs1[1] + t * yCoefs1[2] + + yCoefs1[3] - s * s * s * yCoefs2[0] - s * s * yCoefs2[1] - + s * yCoefs2[2] - yCoefs2[3]) - (3 * t * t * yCoefs1[0] + + 2 * t * yCoefs1[1] + yCoefs1[2]) * (t * t * t * xCoefs1[0] + + t * t * xCoefs1[1] + t * xCoefs1[2] + xCoefs1[3] - + s * s * s * xCoefs2[0] - s * s * xCoefs2[1] - s * xCoefs2[2] - xCoefs2[3]); + + t1 = t - dt / d; + s1 = s - ds / d; + } + params[0] = t1; + params[1] = s1; + } + + private static void quadAndCubicNewton(double xCoefs1[], double yCoefs1[], + double xCoefs2[], double yCoefs2[], + double[] params) { + double t = 0.0, s = 0.0; + double t1 = params[0]; + double s1 = params[1]; + double d, dt, ds; + + while (Math.sqrt((t - t1) * (t - t1) + (s - s1) * (s - s1)) > EPSILON) { + d = -(2 *t * xCoefs1[0] + xCoefs1[1]) * + (3 * s * s * yCoefs2[0] + 2 * s * yCoefs2[1] + yCoefs2[2]) + + (2 *t * yCoefs1[0] + yCoefs1[1]) * + (3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]) ; + + dt = (t * t * xCoefs1[0] + t * xCoefs1[1] + xCoefs1[2] + + - s * s * s * xCoefs2[0] - s * s * xCoefs2[1] - + s * xCoefs2[2] - xCoefs2[3]) * (- 3 * s * s * yCoefs2[0] - + 2 * s * yCoefs2[1] - yCoefs2[2]) + (t * t * yCoefs1[0] + + t * yCoefs1[1] + yCoefs1[2] - s * s *s * yCoefs2[0] - + s * s * yCoefs2[1] - s * yCoefs2[2] - yCoefs2[3]) * + (3 * s * s * xCoefs2[0] + 2 * s * xCoefs2[1] + xCoefs2[2]); + + ds = (2 * t * xCoefs1[0] + xCoefs1[1]) * + (t * t * yCoefs1[0] + t * yCoefs1[1] + yCoefs1[2] - + s * s * s * yCoefs2[0] - s * s * yCoefs2[1] - + s * yCoefs2[2] - yCoefs2[3]) - (2 * t * yCoefs1[0] + + yCoefs1[1]) * (t * t * xCoefs1[0] + + t * xCoefs1[1] + xCoefs1[2] - s * s * s * xCoefs2[0] - + s * s * xCoefs2[1] - s * xCoefs2[2] - xCoefs2[3]); + + t1 = t - dt / d; + s1 = s - ds / d; + } + params[0] = t1; + params[1] = s1; + } + + private static void quadNewton(double xCoefs1[], double yCoefs1[], double xCoefs2[], + double yCoefs2[], double params[]) { + double t = 0.0, s = 0.0; + double t1 = params[0]; + double s1 = params[1]; + double d, dt, ds; + + while (Math.sqrt((t - t1) * (t - t1) + (s - s1) * (s - s1)) > EPSILON) { + t = t1; + s = s1; + d = - (2 * t * xCoefs1[0] + xCoefs1[1]) * (2 * s * yCoefs2[0] + yCoefs2[1]) + + (2 * s * xCoefs2[0] + xCoefs2[1]) * (2 * t * yCoefs1[0] + yCoefs1[1]); + + dt = - (t * t * xCoefs1[0] + t * xCoefs1[1] + xCoefs1[1] - s * s * xCoefs2[0] - + s * xCoefs2[1] -xCoefs2[2]) * (2 * s * yCoefs2[0] + yCoefs2[1]) + + (2 * s * xCoefs2[0] + xCoefs2[1]) * (t * t * yCoefs1[0] + t * yCoefs1[1] + + yCoefs1[2] - s * s * yCoefs2[0] - s * yCoefs2[1] - yCoefs2[2]); + + ds = (2 * t * xCoefs1[0] + xCoefs1[1]) * (t * t * yCoefs1[0] + t * yCoefs1[1] + + yCoefs1[2] - s * s * yCoefs2[0] - s * yCoefs2[1] - yCoefs2[2]) - + (2 * t * yCoefs1[0] + yCoefs1[1]) * (t * t * xCoefs1[0] + t * xCoefs1[1] + + xCoefs1[2] - s * s * xCoefs2[0] - s * xCoefs2[1] - xCoefs2[2]); + + t1 = t - dt / d; + s1 = s - ds / d; + } + params[0] = t1; + params[1] = s1; + } + +} \ No newline at end of file Index: src/main/java/common/org/apache/harmony/awt/internal/IntersectPoint.java =================================================================== --- src/main/java/common/org/apache/harmony/awt/internal/IntersectPoint.java (revision 0) +++ src/main/java/common/org/apache/harmony/awt/internal/IntersectPoint.java (revision 0) @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.awt.internal; + + +// the class represents the intersect point of two edges +public class IntersectPoint { + // the edge begin number of first line + private int begIndex1; + // the edge end number of first line + private int endIndex1; + // the edge rule of first figure + private int rule1; + // the index of the first figure rules array + private int ruleIndex1; + // the parameter value of edge1 + private double param1; + // the edge begin number of second line + private int begIndex2; + // the edge end number of second line + private int endIndex2; + // the edge rule of second figure + private int rule2; + // the index of the second figure rules array + private int ruleIndex2; + // the absciss coordinate of the point + private double x; + // the ordinate coordinate of the point + private double y; +// the parameter value of edge2 + private double param2; + + public IntersectPoint(int begIndex1, int endIndex1, + int begIndex2, int endIndex2, + double x, double y) { + this.begIndex1 = begIndex1; + this.endIndex1 = endIndex1; + this.begIndex2 = begIndex2; + this.endIndex2 = endIndex2; + this.x = x; + this.y = y; + } + + public IntersectPoint (int begIndex1, int endIndex1, int rule1, int ruleIndex1, + int begIndex2, int endIndex2, int rule2, int ruleIndex2, + double x, double y, double param1, double param2) { + this.begIndex1 = begIndex1; + this.endIndex1 = endIndex1; + this.rule1 = rule1; + this.ruleIndex1 = ruleIndex1; + this.param1 = param1; + this.begIndex2 = begIndex2; + this.endIndex2 = endIndex2; + this.rule2 = rule2; + this.ruleIndex2 = ruleIndex2; + this.param2 = param2; + this.x = x; + this.y = y; + } + + public int getBegIndex(boolean isCurrentArea) { + if (isCurrentArea) { + return begIndex1; + } else { + return begIndex2; + } + } + + public int getEndIndex(boolean isCurrentArea) { + if (isCurrentArea) { + return endIndex1; + } else { + return endIndex2; + } + } + + public int getRuleIndex(boolean isCurrentArea) { + if (isCurrentArea) { + return ruleIndex1; + } else { + return ruleIndex2; + } + } + + public double getParam(boolean isCurrentArea) { + if (isCurrentArea) { + return param1; + } else { + return param2; + } + } + + public int getRule(boolean isCurrentArea) { + if (isCurrentArea) { + return rule1; + } else { + return rule2; + } + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public void setBegIndex1(int begIndex) { + this.begIndex1 = begIndex; + } + + public void setEndIndex1(int endIndex) { + this.endIndex1 = endIndex; + } + + public void setBegIndex2(int begIndex) { + this.begIndex2 = begIndex; + } + + public void setEndIndex2(int endIndex) { + this.endIndex2 = endIndex; + } +} \ No newline at end of file Index: src/main/java/common/org/apache/harmony/awt/internal/CurveCrossingHelper.java =================================================================== --- src/main/java/common/org/apache/harmony/awt/internal/CurveCrossingHelper.java (revision 0) +++ src/main/java/common/org/apache/harmony/awt/internal/CurveCrossingHelper.java (revision 0) @@ -0,0 +1,317 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.awt.internal; + +import java.awt.geom.PathIterator; +import java.util.Iterator; +import java.util.List; +import java.util.ArrayList; + + +public class CurveCrossingHelper { + private double[][] coords; + private int[][] rules; + private int[] sizes; + private int[] rulesSizes; + private int[][] offsets; + private List isectPoints = new ArrayList(); + + public CurveCrossingHelper(double[][] coords, int[] sizes, + int[][] rules, int[] rulesSizes, + int[][] offsets) { + this.coords = coords; + this.rules = rules; + this.sizes = sizes; + this.rulesSizes = rulesSizes; + this.offsets = offsets; + } + + public IntersectPoint[] findCrossing() { + double[] edge1 = new double[8]; + double[] edge2 = new double[8]; + double[] points = new double[6]; + double[] params = new double[6]; + double[] mp1 = new double[2]; + double[] cp1 = new double[2]; + double[] mp2 = new double[2]; + double[] cp2 = new double[2]; + int rule1, rule2, endIndex1, endIndex2; + int ipCount = 0; + + for (int i = 0; i < rulesSizes[0]; i++) { + rule1 = rules[0][i]; + endIndex1 = getCurrentEdge(0, i, edge1, mp1, cp1); + for (int j = 0; j < rulesSizes[1]; j++) { + ipCount = 0; + rule2 = rules[1][j]; + endIndex2 = getCurrentEdge(1, j, edge2, mp2, cp2); + if (((rule1 == PathIterator.SEG_LINETO) || + (rule1 == PathIterator.SEG_CLOSE)) && + ((rule2 == PathIterator.SEG_LINETO) || + (rule2 == PathIterator.SEG_CLOSE))) { + + ipCount = GeometryUtil.intersectLinesWithParams( + edge1[0], edge1[1], edge1[2], edge1[3], + edge2[0], edge2[1], edge2[2], edge2[3], + params); + + if (ipCount != 0) { + points[0] = GeometryUtil.line( + params[0], edge1[0], edge1[2]); + points[1] = GeometryUtil.line( + params[0], edge1[1], edge1[3]); + } + } else if (((rule1 == PathIterator.SEG_LINETO) || + (rule1 == PathIterator.SEG_CLOSE)) && + (rule2 == PathIterator.SEG_QUADTO)) { + ipCount = GeometryUtil.intersectLineAndQuad( + edge1[0], edge1[1], edge1[2], + edge1[3], edge2[0], edge2[1], + edge2[2], edge2[3], edge2[4], + edge2[5], params); + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.line(params[2*k], edge1[0], edge1[2]); + points[2*k + 1] = GeometryUtil.line(params[2*k], edge1[1], edge1[3]); + } + } else if (rule1 == PathIterator.SEG_QUADTO && + (rule2 == PathIterator.SEG_LINETO || rule2 == PathIterator.SEG_CLOSE)) { + ipCount = GeometryUtil.intersectLineAndQuad( + edge2[0], edge2[1], edge2[2], + edge2[3], edge1[0], edge1[1], + edge1[2], edge1[3], edge1[4], + edge1[5], params); + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.line( + params[2*k + 1], edge2[0], edge2[2]); + points[2*k + 1] = GeometryUtil.line( + params[2*k + 1], edge2[1], edge2[3]); + } + } else if ((rule1 == PathIterator.SEG_CUBICTO) && + ((rule2 == PathIterator.SEG_LINETO) || + (rule2 == PathIterator.SEG_CLOSE))) { + ipCount = GeometryUtil.intersectLineAndCubic( + edge1[0], edge1[1], edge1[2], + edge1[3], edge1[4], edge1[5], + edge1[6], edge1[7], edge2[0], + edge2[1], edge2[2], edge2[3], + params); + + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.line( + params[2*k + 1], edge2[0], edge2[2]); + points[2*k + 1] = GeometryUtil.line( + params[2*k + 1], edge2[1], edge2[3]); + } + } else if (((rule1 == PathIterator.SEG_LINETO) || + (rule1 == PathIterator.SEG_CLOSE)) && + (rule2 == PathIterator.SEG_CUBICTO)) { + ipCount = GeometryUtil.intersectLineAndCubic( + edge1[0], edge1[1], edge1[2], + edge1[3], edge2[0], edge2[1], + edge2[2], edge2[3], edge2[4], + edge2[5], edge2[6], edge2[7], + params); + + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.line( + params[2*k], edge1[0], edge1[2]); + points[2*k + 1] = GeometryUtil.line( + params[2*k], edge1[1], edge1[3]); + } + } else if ((rule1 == PathIterator.SEG_QUADTO) && + (rule2 == PathIterator.SEG_QUADTO)) { + ipCount = GeometryUtil.intersectQuads( + edge1[0], edge1[1], edge1[2], edge1[3], + edge1[4], edge1[5], edge2[0], edge2[1], + edge2[2], edge2[3], edge2[4], edge2[5], + params); + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.quad( + params[2*k], edge1[0], edge1[2], edge1[4]); + points[2*k + 1] = GeometryUtil.quad( + params[2*k], edge1[1], edge1[3], edge1[5]); + } + } else if ((rule1 == PathIterator.SEG_QUADTO) && + (rule2 == PathIterator.SEG_CUBICTO)) { + ipCount = GeometryUtil.intersectQuadAndCubic( + edge1[0], edge1[1], edge1[2], + edge1[3], edge1[4], edge1[5], + edge2[0], edge2[1], edge2[2], + edge2[3], edge2[4], edge2[5], + edge2[6], edge2[7], params); + + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.quad( + params[2*k], edge1[0], edge1[2], edge1[4]); + points[2*k + 1] = GeometryUtil.quad( + params[2*k], edge1[1], edge1[3], edge1[5]); + } + } else if ((rule1 == PathIterator.SEG_CUBICTO) && + (rule2 == PathIterator.SEG_QUADTO)) { + ipCount = GeometryUtil.intersectQuadAndCubic( + edge2[0], edge2[1], edge2[2], + edge2[3], edge2[4], edge2[5], + edge1[0], edge1[1], edge1[2], + edge1[3], edge1[4], edge1[5], + edge2[6], edge2[7], params); + + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.quad( + params[2*k + 1], edge2[0], edge2[2], edge2[4]); + points[2*k + 1] = GeometryUtil.quad( + params[2*k + 1], edge2[1], edge2[3], edge2[5]); + } + } else if ((rule1 == PathIterator.SEG_CUBICTO) && + (rule2 == PathIterator.SEG_CUBICTO)) { + ipCount = GeometryUtil.intersectCubics( + edge1[0], edge1[1], edge1[2], edge1[3], + edge1[4], edge1[5], edge1[6], edge1[7], + edge2[0], edge2[1], edge2[2], edge2[3], + edge2[4], edge2[5], edge2[6], edge2[7], + params); + + for (int k = 0; k < ipCount; k++) { + points[2*k] = GeometryUtil.cubic( + params[2*k], edge1[0], edge1[2], edge1[4], edge1[6]); + points[2*k + 1] = GeometryUtil.cubic( + params[2*k], edge1[1], edge1[3], edge1[5], edge1[7]); + } + } + + endIndex1 = i; + endIndex2 = j; + int begIndex1 = i - 1; + int begIndex2 = j - 1; + + for (int k = 0; k < ipCount; k++) { + IntersectPoint ip = null; + if (!containsPoint(points[2*k], points[2*k + 1])) { + for (Iterator iter = isectPoints.iterator(); + iter.hasNext(); ) { + ip = (IntersectPoint)iter.next(); + if ((begIndex1 == ip.getBegIndex(true)) && + (endIndex1 == ip.getEndIndex(true))) { + + if (ip.getParam(true) > params[2*k]) { + endIndex1 = - (isectPoints.indexOf(ip) + 1); + ip.setBegIndex1(-(isectPoints.size() + 1)); + } else { + begIndex1 = - (isectPoints.indexOf(ip) + 1); + ip.setEndIndex1(-(isectPoints.size() + 1)); + } + } + + if ((begIndex2 == ip.getBegIndex(false)) && + (endIndex2 == ip.getEndIndex(false))) { + + if (ip.getParam(false) > params[2*k + 1]) { + endIndex2 = - (isectPoints.indexOf(ip) + 1); + ip.setBegIndex2(-(isectPoints.size() + 1)); + } else { + begIndex2 = - (isectPoints.indexOf(ip) + 1); + ip.setEndIndex2(-(isectPoints.size() + 1)); + } + } + } + + if (rule1 == PathIterator.SEG_CLOSE) { + rule1 = PathIterator.SEG_LINETO; + } + + if (rule2 == PathIterator.SEG_CLOSE) { + rule2 = PathIterator.SEG_LINETO; + } + + isectPoints.add(new IntersectPoint(begIndex1, endIndex1, + rule1, i, + begIndex2, endIndex2, + rule2, j, + points[2*k], points[2*k + 1], + params[2*k], params[2*k + 1])); + } + } + } + } + return isectPoints.toArray(new IntersectPoint[isectPoints.size()]); + } + + private int getCurrentEdge(int areaIndex, int index, + double[] c, double[] mp, double[] cp) { + int endIndex = 0; + + switch (rules[areaIndex][index]) { + case PathIterator.SEG_MOVETO: + cp[0] = mp[0] = coords[areaIndex][offsets[areaIndex][index]]; + cp[1] = mp[1] = coords[areaIndex][offsets[areaIndex][index] + 1]; + break; + case PathIterator.SEG_LINETO: + c[0] = cp[0]; + c[1] = cp[1]; + cp[0] = c[2] = coords[areaIndex][offsets[areaIndex][index]]; + cp[1] = c[3] = coords[areaIndex][offsets[areaIndex][index] + 1]; + endIndex = 0; + break; + case PathIterator.SEG_QUADTO: + c[0] = cp[0]; + c[1] = cp[1]; + c[2] = coords[areaIndex][offsets[areaIndex][index]]; + c[3] = coords[areaIndex][offsets[areaIndex][index] + 1]; + cp[0] = c[4] = coords[areaIndex][offsets[areaIndex][index] + 2]; + cp[1] = c[5] = coords[areaIndex][offsets[areaIndex][index] + 3]; + endIndex = 2; + break; + case PathIterator.SEG_CUBICTO: + c[0] = cp[0]; + c[1] = cp[1]; + c[2] = coords[areaIndex][offsets[areaIndex][index]]; + c[3] = coords[areaIndex][offsets[areaIndex][index] + 1]; + c[4] = coords[areaIndex][offsets[areaIndex][index] + 2]; + c[5] = coords[areaIndex][offsets[areaIndex][index] + 3]; + cp[0] = c[6] = coords[areaIndex][offsets[areaIndex][index] + 4]; + cp[1] = c[7] = coords[areaIndex][offsets[areaIndex][index] + 5]; + endIndex = 4; + break; + case PathIterator.SEG_CLOSE: + c[0] = cp[0]; + c[1] = cp[1]; + cp[0] = c[2] = mp[0]; + cp[1] = c[3] = mp[1]; + if (offsets[areaIndex][index] >= sizes[areaIndex]) { + endIndex = -sizes[areaIndex]; + } else { + endIndex = 0; + } + break; + } + return offsets[areaIndex][index] + endIndex; + } + + private boolean containsPoint(double x, double y) { + IntersectPoint ipoint; + + for (Iterator i = isectPoints.iterator(); i.hasNext(); ) { + ipoint = (IntersectPoint)i.next(); + + if ((Math.abs(ipoint.getX() - x) < Math.pow(10, -6)) && + (Math.abs(ipoint.getY() - y) < Math.pow(10, -6))) { + return true; + } + } + + return false; + } +}