/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed 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.constellation.gui.map;
// Geometry
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.IllegalPathStateException;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
/**
* A path iterator iterating only over a single polygon in a shape (i.e. all
* segments until the next {@link #SEG_CLOSE} instruction). This iterator also
* implements the {@link Shape} interface in order to allows it to be passed
* as argument to {@link java.awt.Graphics2D#fill}.
* <p>
* Note: this class make some assumption about {@link java.awt.Graphics2D#fill}
* implementation. We assume that {@code draw/fill} method invokes
* {@code getPathIterator(...)} only once, and that the same flavor
* of {@code getPathIterator(...)} is invoked each time. If one of ours
* assumptions is false, {@link IllegalPathStateException} will be thrown.
*
* @version $Id$
* @author Martin Desruisseaux
*/
final class ShapeBroker implements Shape, PathIterator {
/**
* The underlying shape.
*/
private final Shape shape;
/**
* {@code true} if the iterator is iterating through a flatenned fiew of the shape.
*/
private boolean flat;
/**
* Tells if a {@link #SEG_CLOSE} instruction has been encounter during
* iteration. In this case, iteration is considered finished. Only a
* portion of the underlying shape will have been drawn.
*/
private boolean closed = true;
/**
* The iterator for the underlying shape. This iterator will
* be constructed only the first time {@link #getPathIterator}
* is invoked.
*/
private PathIterator iterator;
/**
* Construct a {@code ShapeBroker} for the specified shape.
*/
public ShapeBroker(final Shape shape) {
this.shape = shape;
}
/**
* Returns the shape's bounding box. This method
* just forward the call to the underlying shape.
*/
public Rectangle getBounds() {
return shape.getBounds();
}
/**
* Returns the shape's bounding box. This method
* just forward the call to the underlying shape.
*/
public Rectangle2D getBounds2D() {
return shape.getBounds2D();
}
/**
* Check the specified point for inclusion. This method
* just forward the call to the underlying shape.
*/
public boolean contains(double x, double y) {
return shape.contains(x,y);
}
/**
* Check the specified point for inclusion. This method
* just forward the call to the underlying shape.
*/
public boolean contains(Point2D point) {
return shape.contains(point);
}
/**
* Check the specified retangle for intersection. This
* method just forward the call to the underlying shape.
*/
public boolean intersects(double x, double y, double w, double h) {
return shape.intersects(x,y,w,h);
}
/**
* Check the specified retangle for intersection. This
* method just forward the call to the underlying shape.
*/
public boolean intersects(Rectangle2D rect) {
return shape.intersects(rect);
}
/**
* Check the specified retangle for inclusion. This
* method just forward the call to the underlying shape.
*/
public boolean contains(double x, double y, double w, double h) {
return shape.contains(x,y,w,h);
}
/**
* Check the specified retangle for inclusion. This
* method just forward the call to the underlying shape.
*/
public boolean contains(Rectangle2D rect) {
return shape.contains(rect);
}
/**
* Returns the winding rule. This method just
* forward the call to the underlying iterator.
*/
public int getWindingRule() {
return iterator.getWindingRule();
}
/**
* Test if there is no more polygon to drawn.
*/
public boolean finished() {
return iterator.isDone();
}
/**
* Test if the iteration is completed. The iteration is considered completed when
* a {@link #SEG_CLOSE} instruction is meets, event if the underlying shape still
* has more points. In this case, field {@link #closed} will have to bet set to
* {@code false} before iteration can continue.
*/
public boolean isDone() {
return closed || iterator.isDone();
}
/**
* Move to the next segment.
*/
public void next() {
if (!closed) {
iterator.next();
}
}
/**
* Get coordinate points for the current segments. If this method returns
* {@link #SEG_CLOSE}, then the next call to {@link #isDone()} will returns
* {@code true} even if the underlying shape still has more points.
*/
public int currentSegment(final float[] coords) {
ensure(!closed);
final int code=iterator.currentSegment(coords);
closed = (code==SEG_CLOSE);
return code;
}
/**
* Get coordinate points for the current segments. If this method returns
* {@link #SEG_CLOSE}, then the next call to {@link #isDone()} will returns
* {@code true} even if the underlying shape still has more points.
*/
public int currentSegment(final double[] coords) {
ensure(!closed);
final int code=iterator.currentSegment(coords);
closed = (code==SEG_CLOSE);
return code;
}
/**
* Returns a path iterator for this shape. This method can be
* invoked only once until the next {@link #SEG_CLOSE}.
*/
public PathIterator getPathIterator(final AffineTransform at) {
return getPathIterator(at, 0);
}
/**
* Returns a path iterator for this shape. This method can be
* invoked only once until the next {@link #SEG_CLOSE}.
*/
public PathIterator getPathIterator(final AffineTransform at, final double flatness) {
ensure(closed);
final boolean isFlatnessSet = (flatness > 0);
if (iterator == null) {
iterator = (isFlatnessSet) ? shape.getPathIterator(at, flatness)
: shape.getPathIterator(at);
flat = isFlatnessSet;
} else {
ensure(flat == isFlatnessSet);
iterator.next();
}
closed = false;
return this;
}
/**
* Make sure the specified condition is true.
* Otherwise, throws a {@link IllegalPathStateException}.
*/
private static void ensure(final boolean check) {
if (!check) {
throw new IllegalPathStateException();
}
}
}