blob: aea1b7ae4072bb181634be9c1cfd27003ea82c5c [file] [log] [blame]
/*
* 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.sis.internal.feature.j2d;
import java.util.Arrays;
import java.util.Iterator;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;
import java.awt.geom.AffineTransform;
import org.apache.sis.internal.referencing.j2d.IntervalRectangle;
/**
* Collection of polylines or polygons as a Java2D {@link Shape}.
* This class has some similarities with {@link java.awt.geom.Path2D}
* with the following differences:
*
* <ul>
* <li>No synchronization.</li>
* <li>Line segments only (no Bézier curves).</li>
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.1
* @since 1.1
* @module
*/
final class MultiPolylines extends FlatShape {
/**
* The polylines or polygons in this collection.
*
* @todo Store in a RTree or QuadTree with {@link Polyline#bounds} as keys.
* Replace loops in {@code contains(…)} and {@code intersect(…)} methods by use of this tree.
*/
private final Polyline[] polylines;
/**
* Creates a collection of polylines.
* The given argument is stored by reference; it is not cloned.
*
* @param polylines the polylines. This array is not cloned.
*/
public MultiPolylines(final Polyline[] polylines) {
super(new IntervalRectangle());
this.polylines = polylines;
bounds.setRect(polylines[0].bounds);
for (int i=1; i<polylines.length; i++) {
bounds.add(polylines[i].bounds);
}
}
/**
* Tests if the given coordinates are inside the boundary of this shape.
*/
@Override
public boolean contains(final double x, final double y) {
if (bounds.contains(x, y)) {
for (final Polyline p : polylines) {
if (p.contains(x, y)) {
return true;
}
}
}
return false;
}
/**
* Tests if the interior of this shape intersects the interior of the given rectangle.
* May conservatively return {@code true} if an intersection is probable but accurate
* answer would be too costly to compute.
*/
@Override
public boolean intersects(final double x, final double y, final double w, final double h) {
if (bounds.intersects(x, y, w, h)) {
for (final Polyline p : polylines) {
if (p.intersects(x, y, w, h)) {
return true;
}
}
}
return false;
}
/**
* Tests if the interior of this shape intersects the interior of the given rectangle.
* May conservatively return {@code true} if an intersection is probable but accurate
* answer would be too costly to compute.
*/
@Override
public boolean intersects(final Rectangle2D r) {
if (bounds.intersects(r)) {
for (final Polyline p : polylines) {
if (p.intersects(r)) {
return true;
}
}
}
return false;
}
/**
* Tests if the interior of this shape entirely contains the interior of the given rectangle.
* May conservatively return {@code false} if an accurate answer would be too costly to compute.
*/
@Override
public boolean contains(final double x, final double y, final double w, final double h) {
if (bounds.contains(x, y, w, h)) {
for (final Polyline p : polylines) {
if (p.contains(x, y, w, h)) {
return true;
}
}
}
return false;
}
/**
* Tests if the interior of this shape entirely contains the interior of the given rectangle.
* May conservatively return {@code false} if an accurate answer would be too costly to compute.
*/
@Override
public boolean contains(final Rectangle2D r) {
if (bounds.contains(r)) {
for (final Polyline p : polylines) {
if (p.contains(r)) {
return true;
}
}
}
return false;
}
/**
* Returns an iterator over coordinates in this multi-polylines.
*/
@Override
public PathIterator getPathIterator(final AffineTransform at) {
final Iterator<Polyline> it = Arrays.asList(polylines).iterator();
return it.hasNext() ? new Polyline.Iter(at, it.next(), it) : new Polyline.Iter();
}
/**
* Returns a potentially smaller shape containing all polylines that intersect the given area of interest.
* This method performs only a quick check based on bounds intersections.
* The returned shape may still have many points outside the given bounds.
*/
@Override
public FlatShape fastClip(final Rectangle2D areaOfInterest) {
if (bounds.intersects(areaOfInterest)) {
final Polyline[] clipped = new Polyline[polylines.length];
int count = 0;
for (final Polyline p : polylines) {
if (p.bounds.intersects(areaOfInterest)) {
clipped[count++] = p;
}
}
if (count != 0) {
if (count == polylines.length) {
return this;
}
return new MultiPolylines(Arrays.copyOf(clipped, count));
}
}
return null;
}
}