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