/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.geom;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.geom.transform.NoninvertibleTransformException;
TransformedShape
objects are transformed "views" onto existing Shape
objects. These objects make no attempt to clone or protect against access to the original Shape
so any modifications to the original Shape
will be immediately reflected in the behaviors and results of this object. /**
* {@code TransformedShape} objects are transformed "views" onto existing
* {@code Shape} objects. These objects make no attempt to clone or protect
* against access to the original {@code Shape} so any modifications to the
* original {@code Shape} will be immediately reflected in the behaviors
* and results of this object.
*
*/
public abstract class TransformedShape extends Shape {
Returns a Shape
object backed by the specified original but with the indicated transform applied to all of its coordinates. The original Shape
is not cloned or copied by this factory method so any changes to the original object will be immediately reflected in the methods of the return value. The original BaseTransform
object is, however, copied since transform objects are far more likely to be scratch objects and a lot less costly to clone. In addition, the underlying implementation may dig some values out of the transform and then discard the actual object in some cases for optimization purposes. Since that would make those cases independent of future changes to the transform, we copy the transform so that all return values from this method are independent of further mutations to the transform object. Params: - original – the backing
Shape
- tx – the
BaseTransform
to apply to all of the coordinates of the backing Shape
on the fly
Returns: a transformed view of the backing Shape
/**
* Returns a {@link Shape} object backed by the specified original
* but with the indicated transform applied to all of its coordinates.
* The original {@code Shape} is not cloned or copied by this factory
* method so any changes to the original object will be immediately
* reflected in the methods of the return value.
* The original {@code BaseTransform} object is, however, copied since
* transform objects are far more likely to be scratch objects and a
* lot less costly to clone. In addition, the underlying implementation
* may dig some values out of the transform and then discard the actual
* object in some cases for optimization purposes. Since that would
* make those cases independent of future changes to the transform, we
* copy the transform so that all return values from this method are
* independent of further mutations to the transform object.
*
* @param original the backing {@code Shape}
* @param tx the {@code BaseTransform} to apply to all of the
* coordinates of the backing {@code Shape} on the fly
* @return a transformed view of the backing {@code Shape}
*/
public static TransformedShape transformedShape(Shape original, BaseTransform tx) {
if (tx.isTranslateOrIdentity()) {
return translatedShape(original, tx.getMxt(), tx.getMyt());
}
return new General(original, tx.copy());
}
Returns a Shape
object backed by the specified original but with the indicated translation applied to all of its coordinates. The original Shape
is not cloned or copied by this factory method so any changes to the original object will be immediately reflected in the methods of the return value. Params: - original – the backing
Shape
- tx – the X coordinate translation to apply to all of the coordinates of the backing
Shape
on the fly - ty – the Y coordinate translation to apply to all of the coordinates of the backing
Shape
on the fly
Returns: a translated view of the backing Shape
/**
* Returns a {@link Shape} object backed by the specified original
* but with the indicated translation applied to all of its coordinates.
* The original {@code Shape} is not cloned or copied by this factory
* method so any changes to the original object will be immediately
* reflected in the methods of the return value.
*
* @param original the backing {@code Shape}
* @param tx the X coordinate translation to apply to all of the
* coordinates of the backing {@code Shape} on the fly
* @param ty the Y coordinate translation to apply to all of the
* coordinates of the backing {@code Shape} on the fly
* @return a translated view of the backing {@code Shape}
*/
public static TransformedShape translatedShape(Shape original, double tx, double ty) {
return new Translate(original, (float) tx, (float) ty);
}
final protected Shape delegate;
protected TransformedShape(Shape delegate) {
this.delegate = delegate;
}
public Shape getDelegateNoClone() {
return delegate;
}
public abstract BaseTransform getTransformNoClone();
Returns a new transform that represents the specified transform with the transform of this TransformedShape
concatenated. Params: - tx – the specified contextual transform
Returns: the concatenated transform
/**
* Returns a new transform that represents the specified transform with
* the transform of this {@code TransformedShape} concatenated.
* @param tx the specified contextual transform
* @return the concatenated transform
*/
public abstract BaseTransform adjust(BaseTransform tx);
protected Point2D untransform(float x, float y) {
Point2D p = new Point2D(x, y);
try {
p = getTransformNoClone().inverseTransform(p, p);
} catch (NoninvertibleTransformException e) {
// No point should intersect such a shape in the first place
// so it is not likely to matter what point we test with...
}
return p;
}
protected BaseBounds untransformedBounds(float x, float y, float w, float h) {
RectBounds b = new RectBounds(x, y, x+w, y+h);
try {
return getTransformNoClone().inverseTransform(b, b);
} catch (NoninvertibleTransformException e) {
return b.makeEmpty();
}
}
@Override
public RectBounds getBounds() {
float box[] = new float[4];
Shape.accumulate(box, delegate, getTransformNoClone());
return new RectBounds(box[0], box[1], box[2], box[3]);
}
// Note that we are not expecting contains() and intersects() to be
// called very often, if at all, for the generated Shape objects so these
// implementations are basic (not heavily optimized and generate garbage).
@Override
public boolean contains(float x, float y) {
return delegate.contains(untransform(x, y));
}
// intersects(rect) and contains(rect) are especially non-optimal
// because there is no good way to test if a Shape contains or
// intersects a transformed rectangle and transforming the rect
// through any other than a rectilinear transform increases its
// scope which changes the answer.
private Shape cachedTransformedShape;
private Shape getCachedTransformedShape() {
if (cachedTransformedShape == null) {
cachedTransformedShape = copy();
}
return cachedTransformedShape;
}
@Override
public boolean intersects(float x, float y, float w, float h) {
// TODO: Could check for rectilinear shapes, if it matters (RT-26884)
return getCachedTransformedShape().intersects(x, y, w, h);
}
@Override
public boolean contains(float x, float y, float w, float h) {
// TODO: Could check for rectilinear shapes, if it matters (RT-26884)
return getCachedTransformedShape().contains(x, y, w, h);
}
@Override
public PathIterator getPathIterator(BaseTransform transform) {
return delegate.getPathIterator(adjust(transform));
}
@Override
public PathIterator getPathIterator(BaseTransform transform,
float flatness)
{
return delegate.getPathIterator(adjust(transform), flatness);
}
@Override
public Shape copy() {
return getTransformNoClone().createTransformedShape(delegate);
}
static final class General extends TransformedShape {
BaseTransform transform;
General(Shape delegate, BaseTransform transform) {
super(delegate);
this.transform = transform;
}
public BaseTransform getTransformNoClone() {
return transform;
}
public BaseTransform adjust(BaseTransform transform) {
if (transform == null || transform.isIdentity()) {
return this.transform.copy();
} else {
return transform.copy().deriveWithConcatenation(this.transform);
}
}
}
static final class Translate extends TransformedShape {
private final float tx, ty;
private BaseTransform cachedTx;
public Translate(Shape delegate, float tx, float ty) {
super(delegate);
this.tx = tx;
this.ty = ty;
}
@Override
public BaseTransform getTransformNoClone() {
if (cachedTx == null) {
cachedTx = BaseTransform.getTranslateInstance(tx, ty);
}
return cachedTx;
}
public BaseTransform adjust(BaseTransform transform) {
if (transform == null || transform.isIdentity()) {
return BaseTransform.getTranslateInstance(tx, ty);
} else {
return transform.copy().deriveWithTranslation(tx, ty);
}
}
@Override
public RectBounds getBounds() {
RectBounds rb = delegate.getBounds();
rb.setBounds(rb.getMinX() + tx, rb.getMinY() + ty,
rb.getMaxX() + tx, rb.getMaxY() + ty);
return rb;
}
@Override
public boolean contains(float x, float y) {
return delegate.contains(x - tx, y - ty);
}
@Override
public boolean intersects(float x, float y, float w, float h) {
return delegate.intersects(x - tx, y - ty, w, h);
}
@Override
public boolean contains(float x, float y, float w, float h) {
return delegate.contains(x - tx, y - ty, w, h);
}
}
}