All Downloads are FREE. Search and download functionalities are using the official Maven repository.

harmony.java.awt.geom.Area Maven / Gradle / Ivy

Go to download

Version modificada de iText 2.1.7 con el paquete cambiado, adaptaciones menores para firma y dependencias actualizadas.

There is a newer version: 1.5
Show 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 harmony.java.awt.geom;

import harmony.java.awt.Rectangle;
import harmony.java.awt.Shape;

import java.util.NoSuchElementException;

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.gl.Crossing;
import org.apache.harmony.awt.internal.nls.Messages;

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(final Shape s) {
		final double segmentCoords[] = new double[6];
		double lastMoveX = 0.0;
		double lastMoveY = 0.0;
		int rulesIndex = 0;
		int coordsIndex = 0;

		for (final PathIterator pi = s.getPathIterator(null); !pi.isDone(); pi.next()) {
			this.coords = adjustSize(this.coords, coordsIndex + 6);
			this.rules = adjustSize(this.rules, rulesIndex + 1);
			this.offsets = adjustSize(this.offsets, rulesIndex + 1);
			this.rules[rulesIndex] = pi.currentSegment(segmentCoords);
			this.offsets[rulesIndex] = coordsIndex;

			switch (this.rules[rulesIndex]) {
			case PathIterator.SEG_MOVETO:
				this.coords[coordsIndex++] = segmentCoords[0];
				this.coords[coordsIndex++] = segmentCoords[1];
				lastMoveX = segmentCoords[0];
				lastMoveY = segmentCoords[1];
				++this.moveToCount;
				break;
			case PathIterator.SEG_LINETO:
				if (segmentCoords[0] != lastMoveX || segmentCoords[1] != lastMoveY) {
					this.coords[coordsIndex++] = segmentCoords[0];
					this.coords[coordsIndex++] = segmentCoords[1];
				} else {
					--rulesIndex;
				}
				break;
			case PathIterator.SEG_QUADTO:
				System.arraycopy(segmentCoords, 0, this.coords, coordsIndex, 4);
				coordsIndex += 4;
				this.isPolygonal = false;
				break;
			case PathIterator.SEG_CUBICTO:
				System.arraycopy(segmentCoords, 0, this.coords, coordsIndex, 6);
				coordsIndex += 6;
				this.isPolygonal = false;
				break;
			case PathIterator.SEG_CLOSE:
				break;
			}
			++rulesIndex;
		}

		if (rulesIndex != 0 && this.rules[rulesIndex - 1] != PathIterator.SEG_CLOSE) {
			this.rules[rulesIndex] = PathIterator.SEG_CLOSE;
			this.offsets[rulesIndex] = this.coordsSize;
		}

		this.rulesSize = rulesIndex;
		this.coordsSize = coordsIndex;
	}

	@Override
	public boolean contains(final double x, final double y) {
		return !isEmpty() && containsExact(x, y) > 0;
	}

	@Override
	public boolean contains(final double x, final double y, final double width, final double height) {
		final int crossCount = Crossing.intersectPath(getPathIterator(null), x, y, width, height);
		return crossCount != Crossing.CROSSING && Crossing.isInsideEvenOdd(crossCount);
	}

	@Override
	public boolean contains(final Point2D p) {
		return contains(p.getX(), p.getY());
	}

	@Override
	public boolean contains(final Rectangle2D r) {
		return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
	}

	public boolean equals(final Area obj) {
		if (this == obj) {
			return true;
		}

		if (obj == null) {
			return false;
		}

		final Area area = (Area) clone();
		area.subtract(obj);
		return area.isEmpty();
	}

	@Override
	public boolean intersects(final double x, final double y, final double width, final double height) {
		if (width <= 0.0 || height <= 0.0) {
			return false;
		} else if (!getBounds2D().intersects(x, y, width, height)) {
			return false;
		}

		final int crossCount = Crossing.intersectShape(this, x, y, width, height);
		return Crossing.isInsideEvenOdd(crossCount);
	}

	@Override
	public boolean intersects(final Rectangle2D r) {
		return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
	}

	@Override
	public Rectangle getBounds() {
		return getBounds2D().getBounds();
	}

	@Override
	public Rectangle2D getBounds2D() {
		double maxX = this.coords[0];
		double maxY = this.coords[1];
		double minX = this.coords[0];
		double minY = this.coords[1];

		for (int i = 0; i < this.coordsSize;) {
			minX = Math.min(minX, this.coords[i]);
			maxX = Math.max(maxX, this.coords[i++]);
			minY = Math.min(minY, this.coords[i]);
			maxY = Math.max(maxY, this.coords[i++]);
		}

		return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
	}

	@Override
	public PathIterator getPathIterator(final AffineTransform t) {
		return new AreaPathIterator(this, t);
	}

	@Override
	public PathIterator getPathIterator(final AffineTransform t, final double flatness) {
		return new FlatteningPathIterator(getPathIterator(t), flatness);
	}

	public boolean isEmpty() {
		return this.rulesSize == 0 && this.coordsSize == 0;
	}

	public boolean isPolygonal() {
		return this.isPolygonal;
	}

	public boolean isRectangular() {
		return this.isPolygonal && this.rulesSize <= 5 && this.coordsSize <= 8 && this.coords[1] == this.coords[3]
				&& this.coords[7] == this.coords[5] && this.coords[0] == this.coords[6] && this.coords[2] == this.coords[4];
	}

	public boolean isSingular() {
		return this.moveToCount <= 1;
	}

	public void reset() {
		this.coordsSize = 0;
		this.rulesSize = 0;
	}

	public void transform(final AffineTransform t) {
		copy(new Area(t.createTransformedShape(this)), this);
	}

	public Area createTransformedArea(final AffineTransform t) {
		return new Area(t.createTransformedShape(this));
	}

	@Override
	public Object clone() {
		final Area area = new Area();
		copy(this, area);
		return area;
	}

	public void add(final 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(final 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(final 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(final Area area) {
		final Area a = (Area) clone();
		a.intersect(area);
		add(area);
		subtract(a);
	}

	private void addCurvePolygon(final Area area) {
		final CurveCrossingHelper crossHelper = new CurveCrossingHelper(new double[][] { this.coords, area.coords }, new int[] {
				this.coordsSize, area.coordsSize }, new int[][] { this.rules, area.rules },
				new int[] { this.rulesSize, area.rulesSize }, new int[][] { this.offsets, area.offsets });
		final IntersectPoint[] intersectPoints = crossHelper.findCrossing();

		if (intersectPoints.length == 0) {
			if (area.contains(getBounds2D())) {
				copy(area, this);
			} else if (!contains(area.getBounds2D())) {
				this.coords = adjustSize(this.coords, this.coordsSize + area.coordsSize);
				System.arraycopy(area.coords, 0, this.coords, this.coordsSize, area.coordsSize);
				this.coordsSize += area.coordsSize;
				this.rules = adjustSize(this.rules, this.rulesSize + area.rulesSize);
				System.arraycopy(area.rules, 0, this.rules, this.rulesSize, area.rulesSize);
				this.rulesSize += area.rulesSize;
				this.offsets = adjustSize(this.offsets, this.rulesSize + area.rulesSize);
				System.arraycopy(area.offsets, 0, this.offsets, this.rulesSize, area.rulesSize);
			}

			return;
		}

		final double[] resultCoords = new double[this.coordsSize + area.coordsSize + intersectPoints.length];
		final int[] resultRules = new int[this.rulesSize + area.rulesSize + intersectPoints.length];
		final int[] resultOffsets = new int[this.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();
			final int curIndex = point.getEndIndex(true);

			if (curIndex < 0) {
				isCurrentArea = !isCurrentArea;
			} else if (area.containsExact(this.coords[2 * curIndex], this.coords[2 * curIndex + 1]) > 0) {
				isCurrentArea = false;
			} else {
				isCurrentArea = true;
			}

			final IntersectPoint nextPoint = getNextIntersectPoint(intersectPoints, point, isCurrentArea);
			final double[] coords = isCurrentArea ? this.coords : area.coords;
			final int[] offsets = isCurrentArea ? this.offsets : area.offsets;
			final int[] rules = isCurrentArea ? this.rules : area.rules;
			int offset = point.getRuleIndex(isCurrentArea);
			boolean isCopyUntilZero = false;

			if (point.getRuleIndex(isCurrentArea) > nextPoint.getRuleIndex(isCurrentArea)) {
				final 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;
			}

			final 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(final Area area) {
		final CrossingHelper crossHelper = new CrossingHelper(new double[][] { this.coords, area.coords }, new int[] { this.coordsSize,
				area.coordsSize });
		final IntersectPoint[] intersectPoints = crossHelper.findCrossing();

		if (intersectPoints.length == 0) {
			if (area.contains(getBounds2D())) {
				copy(area, this);
			} else if (!contains(area.getBounds2D())) {
				this.coords = adjustSize(this.coords, this.coordsSize + area.coordsSize);
				System.arraycopy(area.coords, 0, this.coords, this.coordsSize, area.coordsSize);
				this.coordsSize += area.coordsSize;
				this.rules = adjustSize(this.rules, this.rulesSize + area.rulesSize);
				System.arraycopy(area.rules, 0, this.rules, this.rulesSize, area.rulesSize);
				this.rulesSize += area.rulesSize;
				this.offsets = adjustSize(this.offsets, this.rulesSize + area.rulesSize);
				System.arraycopy(area.offsets, 0, this.offsets, this.rulesSize, area.rulesSize);
			}
			return;
		}

		final double[] resultCoords = new double[this.coordsSize + area.coordsSize + intersectPoints.length];
		final int[] resultRules = new int[this.rulesSize + area.rulesSize + intersectPoints.length];
		final int[] resultOffsets = new int[this.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;
			final int curIndex = point.getEndIndex(true);
			if (curIndex < 0) {
				isCurrentArea = !isCurrentArea;
			} else if (area.containsExact(this.coords[2 * curIndex], this.coords[2 * curIndex + 1]) > 0) {
				isCurrentArea = false;
			} else {
				isCurrentArea = true;
			}

			final IntersectPoint nextPoint = getNextIntersectPoint(intersectPoints, point, isCurrentArea);
			final double[] coords = isCurrentArea ? this.coords : area.coords;
			int offset = 2 * point.getEndIndex(isCurrentArea);

			if (offset >= 0 && nextPoint.getBegIndex(isCurrentArea) < point.getEndIndex(isCurrentArea)) {
				final int coordSize = isCurrentArea ? this.coordsSize : area.coordsSize;
				final 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) {
				final 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;
		this.coords = resultCoords;
		this.rules = resultRules;
		this.offsets = resultOffsets;
		this.coordsSize = resultCoordPos;
		this.rulesSize = resultRulesPos;
	}

	private void intersectCurvePolygon(final Area area) {
		final CurveCrossingHelper crossHelper = new CurveCrossingHelper(new double[][] { this.coords, area.coords }, new int[] {
				this.coordsSize, area.coordsSize }, new int[][] { this.rules, area.rules },
				new int[] { this.rulesSize, area.rulesSize }, new int[][] { this.offsets, area.offsets });
		final IntersectPoint[] intersectPoints = crossHelper.findCrossing();

		if (intersectPoints.length == 0) {
			if (contains(area.getBounds2D())) {
				copy(area, this);
			} else if (!area.contains(getBounds2D())) {
				reset();
			}
			return;
		}

		final double[] resultCoords = new double[this.coordsSize + area.coordsSize + intersectPoints.length];
		final int[] resultRules = new int[this.rulesSize + area.rulesSize + intersectPoints.length];
		final int[] resultOffsets = new int[this.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();

			final int curIndex = point.getEndIndex(true);
			if (curIndex < 0 || area.containsExact(this.coords[2 * curIndex], this.coords[2 * curIndex + 1]) == 0) {
				isCurrentArea = !isCurrentArea;
			} else if (area.containsExact(this.coords[2 * curIndex], this.coords[2 * curIndex + 1]) > 0) {
				isCurrentArea = true;
			} else {
				isCurrentArea = false;
			}

			nextPoint = getNextIntersectPoint(intersectPoints, point, isCurrentArea);
			final double[] coords = isCurrentArea ? this.coords : area.coords;
			final int[] offsets = isCurrentArea ? this.offsets : area.offsets;
			final int[] rules = isCurrentArea ? this.rules : area.rules;
			int offset = point.getRuleIndex(isCurrentArea);
			boolean isCopyUntilZero = false;

			if (point.getRuleIndex(isCurrentArea) > nextPoint.getRuleIndex(isCurrentArea)) {
				final 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;
		this.coords = resultCoords;
		this.rules = resultRules;
		this.offsets = resultOffsets;
		this.coordsSize = resultCoordPos;
		this.rulesSize = resultRulesPos;
	}

	private void intersectPolygon(final Area area) {
		final CrossingHelper crossHelper = new CrossingHelper(new double[][] { this.coords, area.coords }, new int[] { this.coordsSize,
				area.coordsSize });
		final IntersectPoint[] intersectPoints = crossHelper.findCrossing();

		if (intersectPoints.length == 0) {
			if (contains(area.getBounds2D())) {
				copy(area, this);
			} else if (!area.contains(getBounds2D())) {
				reset();
			}
			return;
		}

		final double[] resultCoords = new double[this.coordsSize + area.coordsSize + intersectPoints.length];
		final int[] resultRules = new int[this.rulesSize + area.rulesSize + intersectPoints.length];
		final int[] resultOffsets = new int[this.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;
			final int curIndex = point.getEndIndex(true);

			if (curIndex < 0 || area.containsExact(this.coords[2 * curIndex], this.coords[2 * curIndex + 1]) == 0) {
				isCurrentArea = !isCurrentArea;
			} else if (area.containsExact(this.coords[2 * curIndex], this.coords[2 * curIndex + 1]) > 0) {
				isCurrentArea = true;
			} else {
				isCurrentArea = false;
			}

			final IntersectPoint nextPoint = getNextIntersectPoint(intersectPoints, point, isCurrentArea);
			final double[] coords = isCurrentArea ? this.coords : area.coords;
			int offset = 2 * point.getEndIndex(isCurrentArea);
			if (offset >= 0 && nextPoint.getBegIndex(isCurrentArea) < point.getEndIndex(isCurrentArea)) {
				final int coordSize = isCurrentArea ? this.coordsSize : area.coordsSize;
				final 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) {
				final 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;
		this.coords = resultCoords;
		this.rules = resultRules;
		this.offsets = resultOffsets;
		this.coordsSize = resultCoordPos;
		this.rulesSize = resultRulesPos;
	}

	private void subtractCurvePolygon(final Area area) {
		final CurveCrossingHelper crossHelper = new CurveCrossingHelper(new double[][] { this.coords, area.coords }, new int[] {
				this.coordsSize, area.coordsSize }, new int[][] { this.rules, area.rules },
				new int[] { this.rulesSize, area.rulesSize }, new int[][] { this.offsets, area.offsets });
		final IntersectPoint[] intersectPoints = crossHelper.findCrossing();

		if (intersectPoints.length == 0 && contains(area.getBounds2D())) {
			copy(area, this);
			return;
		}

		final double[] resultCoords = new double[this.coordsSize + area.coordsSize + intersectPoints.length];
		final int[] resultRules = new int[this.rulesSize + area.rulesSize + intersectPoints.length];
		final int[] resultOffsets = new int[this.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();
			final int curIndex = this.offsets[point.getRuleIndex(true)] % this.coordsSize;

			if (area.containsExact(this.coords[curIndex], this.coords[curIndex + 1]) == 0) {
				isCurrentArea = !isCurrentArea;
			} else if (area.containsExact(this.coords[curIndex], this.coords[curIndex + 1]) > 0) {
				isCurrentArea = false;
			} else {
				isCurrentArea = true;
			}

			final IntersectPoint nextPoint = isCurrentArea ? getNextIntersectPoint(intersectPoints, point, isCurrentArea)
					: getPrevIntersectPoint(intersectPoints, point, isCurrentArea);
			final double[] coords = isCurrentArea ? this.coords : area.coords;
			final int[] offsets = isCurrentArea ? this.offsets : area.offsets;
			final 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)) {

				final 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;
			}

			final 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;
		this.coords = resultCoords;
		this.rules = resultRules;
		this.offsets = resultOffsets;
		this.coordsSize = resultCoordPos;
		this.rulesSize = resultRulesPos;
	}

	private void subtractPolygon(final Area area) {
		final CrossingHelper crossHelper = new CrossingHelper(new double[][] { this.coords, area.coords }, new int[] { this.coordsSize,
				area.coordsSize });
		final IntersectPoint[] intersectPoints = crossHelper.findCrossing();

		if (intersectPoints.length == 0) {
			if (contains(area.getBounds2D())) {
				copy(area, this);
				return;
			}
			return;
		}

		final double[] resultCoords = new double[2 * (this.coordsSize + area.coordsSize + intersectPoints.length)];
		final int[] resultRules = new int[2 * (this.rulesSize + area.rulesSize + intersectPoints.length)];
		final int[] resultOffsets = new int[2 * (this.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;
			final int curIndex = point.getEndIndex(true);

			if (curIndex < 0
					|| area.isVertex(this.coords[2 * curIndex], this.coords[2 * curIndex + 1])
							&& crossHelper
									.containsPoint(new double[] { this.coords[2 * curIndex], this.coords[2 * curIndex + 1] }) && (this.coords[2 * curIndex] != point
							.getX() || this.coords[2 * curIndex + 1] != point.getY())) {
				isCurrentArea = !isCurrentArea;
			} else if (area.containsExact(this.coords[2 * curIndex], this.coords[2 * curIndex + 1]) > 0) {
				isCurrentArea = false;
			} else {
				isCurrentArea = true;
			}

			if (countPoints >= intersectPoints.length) {
				isCurrentArea = !isCurrentArea;
			}

			if (isCurrentArea) {
				curArea = true;
			} else {
				addArea = true;
			}

			final IntersectPoint nextPoint = isCurrentArea ? getNextIntersectPoint(intersectPoints, point, isCurrentArea)
					: getPrevIntersectPoint(intersectPoints, point, isCurrentArea);
			final 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))) {

				final int coordSize = isCurrentArea ? this.coordsSize : area.coordsSize;
				final int length = coordSize - offset;

				if (isCurrentArea) {
					System.arraycopy(coords, offset, resultCoords, resultCoordPos, length);
				} else {
					final 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) {
				final 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 {
					final 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;
		this.coords = resultCoords;
		this.rules = resultRules;
		this.offsets = resultOffsets;
		this.coordsSize = resultCoordPos;
		this.rulesSize = resultRulesPos;
	}

	private IntersectPoint getNextIntersectPoint(final IntersectPoint[] iPoints, final IntersectPoint isectPoint,
			final boolean isCurrentArea) {
		final int endIndex = isectPoint.getEndIndex(isCurrentArea);
		if (endIndex < 0) {
			return iPoints[Math.abs(endIndex) - 1];
		}

		IntersectPoint firstIsectPoint = null;
		IntersectPoint nextIsectPoint = null;
		for (final IntersectPoint point : iPoints) {
			final 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(final IntersectPoint[] iPoints, final IntersectPoint isectPoint,
			final boolean isCurrentArea) {

		final int begIndex = isectPoint.getBegIndex(isCurrentArea);

		if (begIndex < 0) {
			return iPoints[Math.abs(begIndex) - 1];
		}

		IntersectPoint firstIsectPoint = null;
		IntersectPoint predIsectPoint = null;
		for (final IntersectPoint point : iPoints) {
			final 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(final int offset, int length, final int[] rules, final int[] offsets, final int[] resultRules,
			final int[] resultOffsets, final double[] resultCoords, final double[] coords, int resultRulesPos, final int resultCoordPos,
			final IntersectPoint point, final boolean isCurrentArea, boolean way, final int operation) {

		final 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(final double[] array, final int newSize) {
		if (newSize <= array.length) {
			return array;
		}
		final double[] newArray = new double[2 * newSize];
		System.arraycopy(array, 0, newArray, 0, array.length);
		return newArray;
	}

	private static int[] adjustSize(final int[] array, final int newSize) {
		if (newSize <= array.length) {
			return array;
		}
		final int[] newArray = new int[2 * newSize];
		System.arraycopy(array, 0, newArray, 0, array.length);
		return newArray;
	}

	private void copy(final Area src, final 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(final double x, final double y) {
		PathIterator pi = getPathIterator(null);
		final int crossCount = Crossing.crossPath(pi, x, y);

		if (Crossing.isInsideEvenOdd(crossCount)) {
			return 1;
		}

		final double[] segmentCoords = new double[6];
		final 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(final double[] coords) {
		final 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() {
		final Rectangle2D bounds = getBounds2D();
		return bounds.getHeight() * bounds.getWidth();
	}

	private boolean isVertex(final double x, final double y) {
		for (int i = 0; i < this.coordsSize;) {
			if (x == this.coords[i++] && y == this.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(final Area area) {
			this(area, null);
		}

		AreaPathIterator(final Area area, final AffineTransform t) {
			this.area = area;
			this.transform = t;
		}

		@Override
		public int getWindingRule() {
			return WIND_EVEN_ODD;
		}

		@Override
		public boolean isDone() {
			return this.curRuleIndex >= Area.this.rulesSize;
		}

		@Override
		public void next() {
			switch (Area.this.rules[this.curRuleIndex]) {
			case PathIterator.SEG_MOVETO:
			case PathIterator.SEG_LINETO:
				this.curCoordIndex += 2;
				break;
			case PathIterator.SEG_QUADTO:
				this.curCoordIndex += 4;
				break;
			case PathIterator.SEG_CUBICTO:
				this.curCoordIndex += 6;
				break;
			}
			this.curRuleIndex++;
		}

		@Override
		public int currentSegment(final double[] c) {
			if (isDone()) {
				throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
			}

			int count = 0;

			switch (Area.this.rules[this.curRuleIndex]) {
			case PathIterator.SEG_CUBICTO:
				c[4] = Area.this.coords[this.curCoordIndex + 4];
				c[5] = Area.this.coords[this.curCoordIndex + 5];
				count = 1;
			case PathIterator.SEG_QUADTO:
				c[2] = Area.this.coords[this.curCoordIndex + 2];
				c[3] = Area.this.coords[this.curCoordIndex + 3];
				count += 1;
			case PathIterator.SEG_MOVETO:
			case PathIterator.SEG_LINETO:
				c[0] = Area.this.coords[this.curCoordIndex];
				c[1] = Area.this.coords[this.curCoordIndex + 1];
				count += 1;
			}

			if (this.transform != null) {
				this.transform.transform(c, 0, c, 0, count);
			}

			return Area.this.rules[this.curRuleIndex];
		}

		@Override
		public int currentSegment(final float[] c) {
			final double[] doubleCoords = new double[6];
			final int rule = currentSegment(doubleCoords);

			for (int i = 0; i < 6; i++) {
				c[i] = (float) doubleCoords[i];
			}
			return rule;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy