X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Futils%2FTransformedRectangle.java;h=926304ca16261fb442364806453f14c1719582b1;hb=452670c58399d8054872655841ebb6e66d9c6b6e;hp=e729fa4d5b7b1f6f44e426819e67c8ff591ce323;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/TransformedRectangle.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/TransformedRectangle.java index e729fa4d5..926304ca1 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/TransformedRectangle.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/TransformedRectangle.java @@ -1,505 +1,505 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 Association for Decentralized Information Management - * in Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.scenegraph.utils; - -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Ellipse2D; -import java.awt.geom.NoninvertibleTransformException; -import java.awt.geom.PathIterator; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.Serializable; - -/** - * A rectangle that is transformed. - * - * @see {@link GeometryUtils} Geometry-related tests for Shapes - * @author Toni Kalajainen - */ -public class TransformedRectangle implements Shape, Serializable { - - private static final long serialVersionUID = 5160199078645323706L; - - Rectangle2D rect = new Rectangle2D.Double(); - - // T(rect) -> Canvas - AffineTransform transform = new AffineTransform(); - // T(canvas) -> Rect - AffineTransform inv = new AffineTransform(); - boolean isIdentity = true; - - /** - * Cache - */ - transient Rectangle2D bounds = null; - - public TransformedRectangle() {} - - public TransformedRectangle(Rectangle2D rect) { - if (rect!=null) - setUntransformedRectangle(rect); - } - - public TransformedRectangle(Rectangle2D rect, AffineTransform transform) { - if (rect!=null) - setUntransformedRectangle(rect); - if (transform!=null) - setTransform(transform); - } - - public TransformedRectangle(TransformedRectangle src) { - setUntransformedRectangle(src.rect); - setTransform(src.transform); - } - - public Rectangle2D getUntransformedRectangle() { - return rect; - } - public void setUntransformedRectangle(Rectangle2D rect) { - this.rect.setFrame(rect); - bounds = null; - } - public AffineTransform getTransform() { - return transform; - } - /** - * Set the transform of the rectangle. - * Transform transforms rectangle coordinates to external coordinates. - * - * T = Rect -> Canvas - * - * @param transform - */ - public void setTransform(AffineTransform transform) { - this.transform.setTransform(transform); - bounds = null; - isIdentity = transform.isIdentity(); - try { - inv = transform.createInverse(); - } catch (NoninvertibleTransformException e) { - throw new RuntimeException(e); - } - } - - public void concatenate(AffineTransform transform) { - this.transform.concatenate(transform); - bounds = null; - try { - inv = transform.createInverse(); - } catch (NoninvertibleTransformException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean contains(Point2D p) { - if (isIdentity) - return rect.contains(p); - - Point2D newP = inv.transform(p, new Point2D.Double()); - return rect.contains(newP); - } - @Override - public boolean contains(double x, double y) { - if (isIdentity) - return rect.contains(x, y); - - Point2D p = new Point2D.Double(x, y); - inv.transform(p, p); - return rect.contains(p); - } - @Override - public boolean contains(Rectangle2D r) { - if (isIdentity) - return rect.contains(r); - - Point2D p1 = new Point2D.Double(r.getMinX(), r.getMinY()); - Point2D p2 = new Point2D.Double(r.getMaxX(), r.getMinY()); - Point2D p3 = new Point2D.Double(r.getMaxX(), r.getMaxY()); - Point2D p4 = new Point2D.Double(r.getMinX(), r.getMaxY()); - - inv.transform(p1, p1); - inv.transform(p2, p2); - inv.transform(p3, p3); - inv.transform(p4, p4); - - return rect.contains(p1) && rect.contains(p2) && rect.contains(p3) && rect.contains(p4); - } - public boolean contains(TransformedRectangle r) { - if (isIdentity) - return r.intersects(rect); - - // Convert points of r to rect - Point2D p1 = new Point2D.Double(r.rect.getMinX(), r.rect.getMinY()); - Point2D p2 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMinY()); - Point2D p3 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMaxY()); - Point2D p4 = new Point2D.Double(r.rect.getMinX(), r.rect.getMaxY()); - - r.transform.transform(p1, p1); - r.transform.transform(p2, p2); - r.transform.transform(p3, p3); - r.transform.transform(p4, p4); - - inv.transform(p1, p1); - inv.transform(p2, p2); - inv.transform(p3, p3); - inv.transform(p4, p4); - - return rect.contains(p1) && rect.contains(p2) && rect.contains(p3) && rect.contains(p4); - } - /** - * Tests if the rectangle completely contains the specified shape. - * - * NOTE This test simplifies curves to straight edges. - * - * @param s - * @return - */ - public boolean contains(Shape s) { - return contains(s, Double.MAX_VALUE); - } - public boolean contains(Shape s, double flatness) { - if (s instanceof Rectangle2D) - return contains((Rectangle2D)s); - if (s instanceof TransformedRectangle) - return contains( (TransformedRectangle) s); - - // rectangle contains s if all points of s are in side rect - PathIterator pi = s.getPathIterator(inv, flatness); - double coords[] = new double[6]; - while (!pi.isDone()) { - int type = pi.currentSegment(coords); - if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) - { - if (!rect.contains(coords[0], coords[1])) - return false; - } - assert(type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO); - pi.next(); - } - return true; - } - - @Override - public boolean contains(double x, double y, double w, double h) { - if (isIdentity) - return rect.contains(x, y, w, h); - - return contains(new Rectangle2D.Double(x, y, w, h)); - } - - /** - * - * NOTE This test simplifies curves to straight edges. - * - * @param s - * @return - */ - public boolean intersects(Shape s) { - return intersects(s, Double.MAX_VALUE); - } - - public boolean intersects(Shape s, double flatness) { - if (s instanceof Rectangle2D) - return intersects((Rectangle2D)s); - if (s instanceof TransformedRectangle) - return intersects( (TransformedRectangle) s); - - PathIterator pi = s.getPathIterator(inv, flatness); - double pos[] = new double[2]; - double coords[] = new double[6]; - while (!pi.isDone()) { - int type = pi.currentSegment(coords); - if (type == PathIterator.SEG_MOVETO) - { - pos[0] = coords[0]; - pos[1] = coords[1]; - } - else - if (type == PathIterator.SEG_LINETO) - { - - } - assert(type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO); - pi.next(); - } - return true; - } - - public boolean intersects(TransformedRectangle r) { - if (isIdentity) - return r.intersects(rect); - - // Convert points of r to rect - Point2D p1 = new Point2D.Double(rect.getMinX(), rect.getMinY()); - Point2D p2 = new Point2D.Double(rect.getMaxX(), rect.getMinY()); - Point2D p3 = new Point2D.Double(rect.getMaxX(), rect.getMaxY()); - Point2D p4 = new Point2D.Double(rect.getMinX(), rect.getMaxY()); - - AffineTransform at = new AffineTransform(transform); - at.concatenate(r.inv); - - at.transform(p1, p1); - at.transform(p2, p2); - at.transform(p3, p3); - at.transform(p4, p4); - - if (r.rect.contains(p1) && r.rect.contains(p2) && r.rect.contains(p3) && r.rect.contains(p4)) - return true; - - if (r.rect.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()) || - r.rect.intersectsLine(p2.getX(), p2.getY(), p3.getX(), p3.getY()) || - r.rect.intersectsLine(p3.getX(), p3.getY(), p4.getX(), p4.getY()) || - r.rect.intersectsLine(p4.getX(), p4.getY(), p1.getX(), p1.getY())) - return true; - - p1 = new Point2D.Double(r.rect.getMinX(), r.rect.getMinY()); - p2 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMinY()); - p3 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMaxY()); - p4 = new Point2D.Double(r.rect.getMinX(), r.rect.getMaxY()); - - at = new AffineTransform(r.transform); - at.concatenate(inv); - - at.transform(p1, p1); - at.transform(p2, p2); - at.transform(p3, p3); - at.transform(p4, p4); - - if (rect.contains(p1) && rect.contains(p2) && rect.contains(p3) && rect.contains(p4)) - return true; - - - return false; - } - @Override - public boolean intersects(Rectangle2D r) { - if (isIdentity) - return rect.intersects(r); - - Point2D p1 = new Point2D.Double(r.getMinX(), r.getMinY()); - Point2D p2 = new Point2D.Double(r.getMaxX(), r.getMinY()); - Point2D p3 = new Point2D.Double(r.getMaxX(), r.getMaxY()); - Point2D p4 = new Point2D.Double(r.getMinX(), r.getMaxY()); - - inv.transform(p1, p1); - inv.transform(p2, p2); - inv.transform(p3, p3); - inv.transform(p4, p4); - - if (rect.contains(p1) || rect.contains(p2) || rect.contains(p3) || rect.contains(p4)) - return true; - - if (rect.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()) || - rect.intersectsLine(p2.getX(), p2.getY(), p3.getX(), p3.getY()) || - rect.intersectsLine(p3.getX(), p3.getY(), p4.getX(), p4.getY()) || - rect.intersectsLine(p4.getX(), p4.getY(), p1.getX(), p1.getY())) - return true; - - p1 = new Point2D.Double(rect.getMinX(), rect.getMinY()); - p2 = new Point2D.Double(rect.getMaxX(), rect.getMinY()); - p3 = new Point2D.Double(rect.getMaxX(), rect.getMaxY()); - p4 = new Point2D.Double(rect.getMinX(), rect.getMaxY()); - - transform.transform(p1, p1); - transform.transform(p2, p2); - transform.transform(p3, p3); - transform.transform(p4, p4); - - if (r.contains(p1) || r.contains(p2) || r.contains(p3) || r.contains(p4)) - return true; - - return false; - } - @Override - public boolean intersects(double x, double y, double w, double h) { - if (isIdentity) - return rect.intersects(x, y, w, h); - - return intersects(new Rectangle2D.Double(x, y, w, h)); - } - - @Override - public Rectangle getBounds() { - if (isIdentity) - return rect.getBounds(); - Rectangle2D b = getOrCreateBounds(); - return new Rectangle( - (int) Math.floor(b.getMinX()), - (int) Math.floor(b.getMinY()), - (int) Math.ceil(b.getWidth()), - (int) Math.ceil(b.getHeight()) - ); - } - @Override - public Rectangle2D getBounds2D() { - if (isIdentity) return rect; - return getOrCreateBounds(); - } - @Override - public PathIterator getPathIterator(AffineTransform at) { - if (isIdentity) - return rect.getPathIterator(at); - if (at == null || at.isIdentity()) - return rect.getPathIterator(transform); - // Concatenate both iterators - // IS THIS ORDER CORRECT?! UNTESTED - AffineTransform con = new AffineTransform(transform); - con.preConcatenate(at); - return rect.getPathIterator(con); - } - @Override - public PathIterator getPathIterator(AffineTransform at, double flatness) { - if (isIdentity) - return rect.getPathIterator(at, flatness); - if (at == null || at.isIdentity()) - return rect.getPathIterator(transform, flatness); - // Concatenate both iterators - AffineTransform con = new AffineTransform(transform); - con.concatenate(at); - return rect.getPathIterator(con, flatness); - } - - - Rectangle2D getOrCreateBounds() - { - if (bounds==null) - bounds = transformRectangle(transform, rect); - return bounds; - } - - static Rectangle2D transformRectangle(AffineTransform transform, Rectangle2D rect) { - double x0 = rect.getMinX(); - double y0 = rect.getMinY(); - double x1 = rect.getMaxX(); - double y1 = rect.getMaxY(); - double m00 = transform.getScaleX(); - double m10 = transform.getShearY(); - double m01 = transform.getShearX(); - double m11 = transform.getScaleY(); - double X0, Y0, X1, Y1; - if(m00 > 0.0) { - X0 = m00 * x0; - X1 = m00 * x1; - } - else { - X1 = m00 * x0; - X0 = m00 * x1; - } - if(m01 > 0.0) { - X0 += m01 * y0; - X1 += m01 * y1; - } - else { - X1 += m01 * y0; - X0 += m01 * y1; - } - if(m10 > 0.0) { - Y0 = m10 * x0; - Y1 = m10 * x1; - } - else { - Y1 = m10 * x0; - Y0 = m10 * x1; - } - if(m11 > 0.0) { - Y0 += m11 * y0; - Y1 += m11 * y1; - } - else { - Y1 += m11 * y0; - Y0 += m11 * y1; - } - return new Rectangle2D.Double(X0+transform.getTranslateX(), - Y0+transform.getTranslateY(), X1-X0, Y1-Y0); - } - - @Override - public String toString() { - Point2D p1 = new Point2D.Double(rect.getMinX(), rect.getMinY()); - Point2D p2 = new Point2D.Double(rect.getMaxX(), rect.getMinY()); - Point2D p3 = new Point2D.Double(rect.getMaxX(), rect.getMaxY()); - Point2D p4 = new Point2D.Double(rect.getMinX(), rect.getMaxY()); - - transform.transform(p1, p1); - transform.transform(p2, p2); - transform.transform(p3, p3); - transform.transform(p4, p4); - - StringBuilder sb = new StringBuilder(); - sb.append(p1+"\n"); - sb.append(p2+"\n"); - sb.append(p3+"\n"); - sb.append(p4); - - return sb.toString(); - } - - // Test this class - // Run with VM arg: -ea - public static void main(String[] args) { - Rectangle2D rect = new Rectangle2D.Double(0, 0, 10, 10); - AffineTransform at = new AffineTransform(); - at.setToRotation(Math.PI/4); - TransformedRectangle tr = new TransformedRectangle(rect, at); - - Rectangle2D t1 = new Rectangle2D.Double(5, 5, 5, 5); - Rectangle2D t2 = new Rectangle2D.Double(-2, 4, 3, 2); - Rectangle2D t3 = new Rectangle2D.Double(9, 9, 5, 5); - Rectangle2D t4 = new Rectangle2D.Double(-100, -100, 200, 200); - - // Contains test - assert(!tr.contains(t1)); - assert( tr.contains(t2)); - assert(!tr.contains(t3)); - assert(!tr.contains(t4)); - - // Intersects test - assert( tr.intersects(t1)); - assert( tr.intersects(t2)); - assert(!tr.intersects(t3)); - assert( tr.intersects(t4)); - - Ellipse2D e = new Ellipse2D.Double(-5, 0, 10, 10); - assert( tr.intersects(e) ); - assert(!tr.contains(e) ); - - TransformedRectangle tr1 = new TransformedRectangle(t4, at); - TransformedRectangle tr2 = new TransformedRectangle(new Rectangle2D.Double(3, 3, 2, 2), at); - TransformedRectangle tr3 = new TransformedRectangle(new Rectangle2D.Double(-20, 3, 40, 3), at); - TransformedRectangle tr4 = new TransformedRectangle(new Rectangle2D.Double(8, -6, 4, 8), at); - TransformedRectangle tr5 = new TransformedRectangle(new Rectangle2D.Double(2, 12, 7, 7), at); - - assert(!tr.contains(tr1) ); - assert( tr.contains(tr2) ); - assert(!tr.contains(tr3) ); - assert(!tr.contains(tr4) ); - assert(!tr.contains(tr5) ); - - assert( tr1.contains(tr) ); - assert(!tr2.contains(tr) ); - assert(!tr3.contains(tr) ); - assert(!tr4.contains(tr) ); - assert(!tr5.contains(tr) ); - - assert( tr.intersects(tr1) ); - assert( tr.intersects(tr2) ); - assert( tr.intersects(tr3) ); - assert( tr.intersects(tr4) ); - assert(!tr.intersects(tr5) ); - - assert(false); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.scenegraph.utils; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +/** + * A rectangle that is transformed. + * + * @see {@link GeometryUtils} Geometry-related tests for Shapes + * @author Toni Kalajainen + */ +public class TransformedRectangle implements Shape, Serializable { + + private static final long serialVersionUID = 5160199078645323706L; + + Rectangle2D rect = new Rectangle2D.Double(); + + // T(rect) -> Canvas + AffineTransform transform = new AffineTransform(); + // T(canvas) -> Rect + AffineTransform inv = new AffineTransform(); + boolean isIdentity = true; + + /** + * Cache + */ + transient Rectangle2D bounds = null; + + public TransformedRectangle() {} + + public TransformedRectangle(Rectangle2D rect) { + if (rect!=null) + setUntransformedRectangle(rect); + } + + public TransformedRectangle(Rectangle2D rect, AffineTransform transform) { + if (rect!=null) + setUntransformedRectangle(rect); + if (transform!=null) + setTransform(transform); + } + + public TransformedRectangle(TransformedRectangle src) { + setUntransformedRectangle(src.rect); + setTransform(src.transform); + } + + public Rectangle2D getUntransformedRectangle() { + return rect; + } + public void setUntransformedRectangle(Rectangle2D rect) { + this.rect.setFrame(rect); + bounds = null; + } + public AffineTransform getTransform() { + return transform; + } + /** + * Set the transform of the rectangle. + * Transform transforms rectangle coordinates to external coordinates. + * + * T = Rect -> Canvas + * + * @param transform + */ + public void setTransform(AffineTransform transform) { + this.transform.setTransform(transform); + bounds = null; + isIdentity = transform.isIdentity(); + try { + inv = transform.createInverse(); + } catch (NoninvertibleTransformException e) { + throw new RuntimeException(e); + } + } + + public void concatenate(AffineTransform transform) { + this.transform.concatenate(transform); + bounds = null; + try { + inv = transform.createInverse(); + } catch (NoninvertibleTransformException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean contains(Point2D p) { + if (isIdentity) + return rect.contains(p); + + Point2D newP = inv.transform(p, new Point2D.Double()); + return rect.contains(newP); + } + @Override + public boolean contains(double x, double y) { + if (isIdentity) + return rect.contains(x, y); + + Point2D p = new Point2D.Double(x, y); + inv.transform(p, p); + return rect.contains(p); + } + @Override + public boolean contains(Rectangle2D r) { + if (isIdentity) + return rect.contains(r); + + Point2D p1 = new Point2D.Double(r.getMinX(), r.getMinY()); + Point2D p2 = new Point2D.Double(r.getMaxX(), r.getMinY()); + Point2D p3 = new Point2D.Double(r.getMaxX(), r.getMaxY()); + Point2D p4 = new Point2D.Double(r.getMinX(), r.getMaxY()); + + inv.transform(p1, p1); + inv.transform(p2, p2); + inv.transform(p3, p3); + inv.transform(p4, p4); + + return rect.contains(p1) && rect.contains(p2) && rect.contains(p3) && rect.contains(p4); + } + public boolean contains(TransformedRectangle r) { + if (isIdentity) + return r.intersects(rect); + + // Convert points of r to rect + Point2D p1 = new Point2D.Double(r.rect.getMinX(), r.rect.getMinY()); + Point2D p2 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMinY()); + Point2D p3 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMaxY()); + Point2D p4 = new Point2D.Double(r.rect.getMinX(), r.rect.getMaxY()); + + r.transform.transform(p1, p1); + r.transform.transform(p2, p2); + r.transform.transform(p3, p3); + r.transform.transform(p4, p4); + + inv.transform(p1, p1); + inv.transform(p2, p2); + inv.transform(p3, p3); + inv.transform(p4, p4); + + return rect.contains(p1) && rect.contains(p2) && rect.contains(p3) && rect.contains(p4); + } + /** + * Tests if the rectangle completely contains the specified shape. + * + * NOTE This test simplifies curves to straight edges. + * + * @param s + * @return + */ + public boolean contains(Shape s) { + return contains(s, Double.MAX_VALUE); + } + public boolean contains(Shape s, double flatness) { + if (s instanceof Rectangle2D) + return contains((Rectangle2D)s); + if (s instanceof TransformedRectangle) + return contains( (TransformedRectangle) s); + + // rectangle contains s if all points of s are in side rect + PathIterator pi = s.getPathIterator(inv, flatness); + double coords[] = new double[6]; + while (!pi.isDone()) { + int type = pi.currentSegment(coords); + if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) + { + if (!rect.contains(coords[0], coords[1])) + return false; + } + assert(type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO); + pi.next(); + } + return true; + } + + @Override + public boolean contains(double x, double y, double w, double h) { + if (isIdentity) + return rect.contains(x, y, w, h); + + return contains(new Rectangle2D.Double(x, y, w, h)); + } + + /** + * + * NOTE This test simplifies curves to straight edges. + * + * @param s + * @return + */ + public boolean intersects(Shape s) { + return intersects(s, Double.MAX_VALUE); + } + + public boolean intersects(Shape s, double flatness) { + if (s instanceof Rectangle2D) + return intersects((Rectangle2D)s); + if (s instanceof TransformedRectangle) + return intersects( (TransformedRectangle) s); + + PathIterator pi = s.getPathIterator(inv, flatness); + double pos[] = new double[2]; + double coords[] = new double[6]; + while (!pi.isDone()) { + int type = pi.currentSegment(coords); + if (type == PathIterator.SEG_MOVETO) + { + pos[0] = coords[0]; + pos[1] = coords[1]; + } + else + if (type == PathIterator.SEG_LINETO) + { + + } + assert(type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO); + pi.next(); + } + return true; + } + + public boolean intersects(TransformedRectangle r) { + if (isIdentity) + return r.intersects(rect); + + // Convert points of r to rect + Point2D p1 = new Point2D.Double(rect.getMinX(), rect.getMinY()); + Point2D p2 = new Point2D.Double(rect.getMaxX(), rect.getMinY()); + Point2D p3 = new Point2D.Double(rect.getMaxX(), rect.getMaxY()); + Point2D p4 = new Point2D.Double(rect.getMinX(), rect.getMaxY()); + + AffineTransform at = new AffineTransform(transform); + at.concatenate(r.inv); + + at.transform(p1, p1); + at.transform(p2, p2); + at.transform(p3, p3); + at.transform(p4, p4); + + if (r.rect.contains(p1) && r.rect.contains(p2) && r.rect.contains(p3) && r.rect.contains(p4)) + return true; + + if (r.rect.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()) || + r.rect.intersectsLine(p2.getX(), p2.getY(), p3.getX(), p3.getY()) || + r.rect.intersectsLine(p3.getX(), p3.getY(), p4.getX(), p4.getY()) || + r.rect.intersectsLine(p4.getX(), p4.getY(), p1.getX(), p1.getY())) + return true; + + p1 = new Point2D.Double(r.rect.getMinX(), r.rect.getMinY()); + p2 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMinY()); + p3 = new Point2D.Double(r.rect.getMaxX(), r.rect.getMaxY()); + p4 = new Point2D.Double(r.rect.getMinX(), r.rect.getMaxY()); + + at = new AffineTransform(r.transform); + at.concatenate(inv); + + at.transform(p1, p1); + at.transform(p2, p2); + at.transform(p3, p3); + at.transform(p4, p4); + + if (rect.contains(p1) && rect.contains(p2) && rect.contains(p3) && rect.contains(p4)) + return true; + + + return false; + } + @Override + public boolean intersects(Rectangle2D r) { + if (isIdentity) + return rect.intersects(r); + + Point2D p1 = new Point2D.Double(r.getMinX(), r.getMinY()); + Point2D p2 = new Point2D.Double(r.getMaxX(), r.getMinY()); + Point2D p3 = new Point2D.Double(r.getMaxX(), r.getMaxY()); + Point2D p4 = new Point2D.Double(r.getMinX(), r.getMaxY()); + + inv.transform(p1, p1); + inv.transform(p2, p2); + inv.transform(p3, p3); + inv.transform(p4, p4); + + if (rect.contains(p1) || rect.contains(p2) || rect.contains(p3) || rect.contains(p4)) + return true; + + if (rect.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()) || + rect.intersectsLine(p2.getX(), p2.getY(), p3.getX(), p3.getY()) || + rect.intersectsLine(p3.getX(), p3.getY(), p4.getX(), p4.getY()) || + rect.intersectsLine(p4.getX(), p4.getY(), p1.getX(), p1.getY())) + return true; + + p1 = new Point2D.Double(rect.getMinX(), rect.getMinY()); + p2 = new Point2D.Double(rect.getMaxX(), rect.getMinY()); + p3 = new Point2D.Double(rect.getMaxX(), rect.getMaxY()); + p4 = new Point2D.Double(rect.getMinX(), rect.getMaxY()); + + transform.transform(p1, p1); + transform.transform(p2, p2); + transform.transform(p3, p3); + transform.transform(p4, p4); + + if (r.contains(p1) || r.contains(p2) || r.contains(p3) || r.contains(p4)) + return true; + + return false; + } + @Override + public boolean intersects(double x, double y, double w, double h) { + if (isIdentity) + return rect.intersects(x, y, w, h); + + return intersects(new Rectangle2D.Double(x, y, w, h)); + } + + @Override + public Rectangle getBounds() { + if (isIdentity) + return rect.getBounds(); + Rectangle2D b = getOrCreateBounds(); + return new Rectangle( + (int) Math.floor(b.getMinX()), + (int) Math.floor(b.getMinY()), + (int) Math.ceil(b.getWidth()), + (int) Math.ceil(b.getHeight()) + ); + } + @Override + public Rectangle2D getBounds2D() { + if (isIdentity) return rect; + return getOrCreateBounds(); + } + @Override + public PathIterator getPathIterator(AffineTransform at) { + if (isIdentity) + return rect.getPathIterator(at); + if (at == null || at.isIdentity()) + return rect.getPathIterator(transform); + // Concatenate both iterators + // IS THIS ORDER CORRECT?! UNTESTED + AffineTransform con = new AffineTransform(transform); + con.preConcatenate(at); + return rect.getPathIterator(con); + } + @Override + public PathIterator getPathIterator(AffineTransform at, double flatness) { + if (isIdentity) + return rect.getPathIterator(at, flatness); + if (at == null || at.isIdentity()) + return rect.getPathIterator(transform, flatness); + // Concatenate both iterators + AffineTransform con = new AffineTransform(transform); + con.concatenate(at); + return rect.getPathIterator(con, flatness); + } + + + Rectangle2D getOrCreateBounds() + { + if (bounds==null) + bounds = transformRectangle(transform, rect); + return bounds; + } + + static Rectangle2D transformRectangle(AffineTransform transform, Rectangle2D rect) { + double x0 = rect.getMinX(); + double y0 = rect.getMinY(); + double x1 = rect.getMaxX(); + double y1 = rect.getMaxY(); + double m00 = transform.getScaleX(); + double m10 = transform.getShearY(); + double m01 = transform.getShearX(); + double m11 = transform.getScaleY(); + double X0, Y0, X1, Y1; + if(m00 > 0.0) { + X0 = m00 * x0; + X1 = m00 * x1; + } + else { + X1 = m00 * x0; + X0 = m00 * x1; + } + if(m01 > 0.0) { + X0 += m01 * y0; + X1 += m01 * y1; + } + else { + X1 += m01 * y0; + X0 += m01 * y1; + } + if(m10 > 0.0) { + Y0 = m10 * x0; + Y1 = m10 * x1; + } + else { + Y1 = m10 * x0; + Y0 = m10 * x1; + } + if(m11 > 0.0) { + Y0 += m11 * y0; + Y1 += m11 * y1; + } + else { + Y1 += m11 * y0; + Y0 += m11 * y1; + } + return new Rectangle2D.Double(X0+transform.getTranslateX(), + Y0+transform.getTranslateY(), X1-X0, Y1-Y0); + } + + @Override + public String toString() { + Point2D p1 = new Point2D.Double(rect.getMinX(), rect.getMinY()); + Point2D p2 = new Point2D.Double(rect.getMaxX(), rect.getMinY()); + Point2D p3 = new Point2D.Double(rect.getMaxX(), rect.getMaxY()); + Point2D p4 = new Point2D.Double(rect.getMinX(), rect.getMaxY()); + + transform.transform(p1, p1); + transform.transform(p2, p2); + transform.transform(p3, p3); + transform.transform(p4, p4); + + StringBuilder sb = new StringBuilder(); + sb.append(p1+"\n"); + sb.append(p2+"\n"); + sb.append(p3+"\n"); + sb.append(p4); + + return sb.toString(); + } + + // Test this class + // Run with VM arg: -ea + public static void main(String[] args) { + Rectangle2D rect = new Rectangle2D.Double(0, 0, 10, 10); + AffineTransform at = new AffineTransform(); + at.setToRotation(Math.PI/4); + TransformedRectangle tr = new TransformedRectangle(rect, at); + + Rectangle2D t1 = new Rectangle2D.Double(5, 5, 5, 5); + Rectangle2D t2 = new Rectangle2D.Double(-2, 4, 3, 2); + Rectangle2D t3 = new Rectangle2D.Double(9, 9, 5, 5); + Rectangle2D t4 = new Rectangle2D.Double(-100, -100, 200, 200); + + // Contains test + assert(!tr.contains(t1)); + assert( tr.contains(t2)); + assert(!tr.contains(t3)); + assert(!tr.contains(t4)); + + // Intersects test + assert( tr.intersects(t1)); + assert( tr.intersects(t2)); + assert(!tr.intersects(t3)); + assert( tr.intersects(t4)); + + Ellipse2D e = new Ellipse2D.Double(-5, 0, 10, 10); + assert( tr.intersects(e) ); + assert(!tr.contains(e) ); + + TransformedRectangle tr1 = new TransformedRectangle(t4, at); + TransformedRectangle tr2 = new TransformedRectangle(new Rectangle2D.Double(3, 3, 2, 2), at); + TransformedRectangle tr3 = new TransformedRectangle(new Rectangle2D.Double(-20, 3, 40, 3), at); + TransformedRectangle tr4 = new TransformedRectangle(new Rectangle2D.Double(8, -6, 4, 8), at); + TransformedRectangle tr5 = new TransformedRectangle(new Rectangle2D.Double(2, 12, 7, 7), at); + + assert(!tr.contains(tr1) ); + assert( tr.contains(tr2) ); + assert(!tr.contains(tr3) ); + assert(!tr.contains(tr4) ); + assert(!tr.contains(tr5) ); + + assert( tr1.contains(tr) ); + assert(!tr2.contains(tr) ); + assert(!tr3.contains(tr) ); + assert(!tr4.contains(tr) ); + assert(!tr5.contains(tr) ); + + assert( tr.intersects(tr1) ); + assert( tr.intersects(tr2) ); + assert( tr.intersects(tr3) ); + assert( tr.intersects(tr4) ); + assert(!tr.intersects(tr5) ); + + assert(false); + } + +}