com.google.code.appengine.awt.geom.Area.0 Maven / Gradle / Ivy
The newest version!
/*
* 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 com.google.code.appengine.awt.geom;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.apache.harmony.awt.gl.Crossing;
import org.apache.harmony.awt.geom.CrossingHelper;
import org.apache.harmony.awt.geom.CurveCrossingHelper;
import org.apache.harmony.awt.geom.GeometryUtil;
import org.apache.harmony.awt.geom.IntersectPoint;
import org.apache.harmony.awt.internal.nls.Messages;
import com.google.code.appengine.awt.Rectangle;
import com.google.code.appengine.awt.Shape;
import com.google.code.appengine.awt.geom.AffineTransform;
import com.google.code.appengine.awt.geom.Area;
import com.google.code.appengine.awt.geom.FlatteningPathIterator;
import com.google.code.appengine.awt.geom.PathIterator;
import com.google.code.appengine.awt.geom.Point2D;
import com.google.code.appengine.awt.geom.Rectangle2D;
public class Area implements Shape, Cloneable {
/**
* the coordinates array of the shape vertices
*/
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;
public Area() {
}
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 boolean contains(double x, double y) {
return !isEmpty() &&
containsExact(x, y) > 0;
}
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 boolean contains(Point2D p) {
return contains(p.getX(), p.getY());
}
public boolean contains(Rectangle2D r) {
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
public boolean 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 boolean intersects(Rectangle2D r) {
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
}
public Rectangle getBounds() {
return getBounds2D().getBounds();
}
public Rectangle2D getBounds2D() {
double maxX = coords[0];
double maxY = coords[1];
double minX = coords[0];
double minY = coords[1];
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 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 isPolygonal() {
return isPolygonal;
}
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 isSingular() {
return (moveToCount <= 1);
}
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));
}
@Override
public Object clone() {
Area area = new Area();
copy(this, area);
return area;
}
public void add(Area area) {
if (area == null || area.isEmpty()) {
return;
} else if (isEmpty()) {
copy(area, this);
return;
}
if (isPolygonal() && area.isPolygonal()) {
addPolygon(area);
} else {
addCurvePolygon(area);
}
if (getAreaBoundsSquare() < GeometryUtil.EPSILON) {
reset();
}
}
public void intersect(Area area) {
if (area == null) {
return;
} else if (isEmpty() || area.isEmpty()) {
reset();
return;
}
if (isPolygonal() && area.isPolygonal()) {
intersectPolygon(area);
} else {
intersectCurvePolygon(area);
}
if (getAreaBoundsSquare() < GeometryUtil.EPSILON) {
reset();
}
}
public void subtract(Area area) {
if (area == null || isEmpty() || area.isEmpty()) {
return;
}
if (isPolygonal() && area.isPolygonal()) {
subtractPolygon(area);
} else {
subtractCurvePolygon(area);
}
if (getAreaBoundsSquare() < GeometryUtil.EPSILON) {
reset();
}
}
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 ((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;
}
}
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;
}
resultOffsets[resultRulesPos - 1] = resultCoordPos;
coords = resultCoords;
rules = resultRules;
offsets = resultOffsets;
coordsSize = resultCoordPos;
rulesSize = resultRulesPos;
}
private void intersectPolygon(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 (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];
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 = true;
} else {
isCurrentArea = false;
}
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;
}
}
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) {
if (contains(area.getBounds2D())) {
copy(area, this);
return;
}
return;
}
double[] resultCoords = new double[2 * (coordsSize + area.coordsSize +
intersectPoints.length)];
int[] resultRules = new int[2 * (rulesSize + area.rulesSize +
intersectPoints.length)];
int[] resultOffsets = new int[2 * (rulesSize + area.rulesSize +
intersectPoints.length)];
int resultCoordPos = 0;
int resultRulesPos = 0;
boolean isCurrentArea = true;
int countPoints = 0;
boolean curArea = false;
boolean addArea = false;
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.isVertex(coords[2 * curIndex], coords[2 * curIndex + 1]) &&
crossHelper.containsPoint(new double[] {coords[2 * curIndex],
coords[2 * curIndex + 1]}) &&
(coords[2 * curIndex] != point.getX() ||
coords[2 * curIndex + 1] != point.getY()))) {
isCurrentArea = !isCurrentArea;
} else if (area.containsExact(coords[2 * curIndex],
coords[2 * curIndex + 1]) > 0) {
isCurrentArea = false;
} else {
isCurrentArea = true;
}
if (countPoints >= intersectPoints.length) {
isCurrentArea = !isCurrentArea;
}
if (isCurrentArea) {
curArea = true;
} else {
addArea = true;
}
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;
countPoints++;
} while (point != intersectPoints[0] || !(curArea && addArea));
resultRules[resultRulesPos - 1] = PathIterator.SEG_CLOSE;
resultOffsets[resultRulesPos - 1] = resultCoordPos;
coords = resultCoords;
rules = resultRules;
offsets = resultOffsets;
coordsSize = resultCoordPos;
rulesSize = resultRulesPos;
}
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 {
if(resultCoordPos+coordsCount>resultCoords.length)
resultCoords=Arrays.copyOf(resultCoords, resultCoordPos+coordsCount);
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;
}
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;
}
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;
}
}
private double getAreaBoundsSquare() {
Rectangle2D bounds = getBounds2D();
return bounds.getHeight() * bounds.getWidth();
}
private boolean isVertex(double x, double y) {
for (int i = 0; i < coordsSize;) {
if (x == coords[i++] && y == coords[i++]) {
return true;
}
}
return false;
}
// the internal class implements PathIterator
private class AreaPathIterator implements PathIterator {
AffineTransform transform;
Area area;
int curRuleIndex = 0;
int curCoordIndex = 0;
AreaPathIterator(Area area) {
this(area, null);
}
AreaPathIterator(Area area, AffineTransform t) {
this.area = area;
this.transform = t;
}
public int getWindingRule() {
return WIND_EVEN_ODD;
}
public boolean isDone() {
return curRuleIndex >= rulesSize;
}
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 int currentSegment(double[] c) {
if (isDone()) {
throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
}
int count = 0;
switch (rules[curRuleIndex]) {
case PathIterator.SEG_CUBICTO:
c[4] = coords[curCoordIndex + 4];
c[5] = coords[curCoordIndex + 5];
count = 1;
case PathIterator.SEG_QUADTO:
c[2] = coords[curCoordIndex + 2];
c[3] = coords[curCoordIndex + 3];
count += 1;
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
c[0] = coords[curCoordIndex];
c[1] = coords[curCoordIndex + 1];
count += 1;
}
if(transform != null) {
transform.transform(c, 0, c, 0, count);
}
return rules[curRuleIndex];
}
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;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy