]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/utils/GeometryUtils.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / utils / GeometryUtils.java
index 73d0f54146095d46420fc9847176be5197a110a5..9f668983a34ed3af632d8337e9333c4d864fcdb1 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.g2d.utils;\r
-\r
-import java.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Polygon;\r
-import java.awt.Shape;\r
-import java.awt.Stroke;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Area;\r
-import java.awt.geom.Ellipse2D;\r
-import java.awt.geom.Path2D;\r
-import java.awt.geom.PathIterator;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.List;\r
-\r
-import org.simantics.scenegraph.utils.TransformedRectangle;\r
-\r
-/**\r
- * Geometry related Utilities\r
- *\r
- * @see PathUtils2 Path related utilities\r
- * \r
- * @author Toni Kalajainen\r
- * @author Tuukka Lehtonen\r
- */\r
-public final class GeometryUtils {\r
-\r
-    //public static final double EPSILON = 1e-7;\r
-\r
-    public static final BasicStroke BASIC_STROKE = new BasicStroke();\r
-\r
-    public static BasicStroke scaleStroke(Stroke stroke, float factor)\r
-    {\r
-        BasicStroke s = (BasicStroke) stroke;\r
-        float[] dash = s.getDashArray();\r
-        if (dash!=null) {\r
-            assert(factor!=0);\r
-            dash = scaleArray(factor, dash, new float[dash.length]);\r
-        }\r
-        if (dash==null)\r
-            return new BasicStroke(\r
-                    s.getLineWidth() * factor,\r
-                    s.getEndCap(),\r
-                    s.getLineJoin(),\r
-                    s.getMiterLimit()\r
-            );\r
-        return new BasicStroke(\r
-                s.getLineWidth() * factor,\r
-                s.getEndCap(),\r
-                s.getLineJoin(),\r
-                s.getMiterLimit(),\r
-                dash,\r
-                s.getDashPhase() * factor\r
-        );\r
-    }\r
-\r
-\r
-\r
-    /**\r
-     * Scales every element in array\r
-     * @param array\r
-     * @param factor\r
-     * @return new scaled array\r
-     */\r
-    public static float[] scaleArray(float factor, float [] array, float [] targetArray)\r
-    {\r
-        assert(array!=null);\r
-        if (targetArray==null)\r
-            targetArray = new float[array.length];\r
-        for (int i=0; i<array.length; i++)\r
-        {\r
-            targetArray[i] = array[i] * factor;\r
-        }\r
-        return targetArray;\r
-    }\r
-\r
-    /**\r
-     * Get x/y scale of a transform\r
-     * @param at\r
-     * @param pt\r
-     * @return\r
-     */\r
-    public static Point2D getScaleXY(AffineTransform at, Point2D pt)\r
-    {\r
-        double m00 = at.getScaleX();\r
-        double m11 = at.getScaleY();\r
-        double m10 = at.getShearY();\r
-        double m01 = at.getShearX();\r
-        // Project unit vector to canvas\r
-        double sx = Math.sqrt( m00*m00+m10*m10 );\r
-        double sy = Math.sqrt( m01*m01+m11*m11 );\r
-        if (pt==null) pt = new Point2D.Double();\r
-        pt.setLocation(sx, sy);\r
-        return pt;\r
-    }\r
-\r
-    /**\r
-     * Get scale of a transform\r
-     * @param at\r
-     * @param pt\r
-     * @return\r
-     */\r
-    /*\r
-       public static double getScale(AffineTransform at)\r
-       {\r
-               double m00 = at.getScaleX();\r
-               double m11 = at.getScaleY();\r
-               double m10 = at.getShearY();\r
-               double m01 = at.getShearX();\r
-               // Project unit vector to canvas\r
-               double sx = Math.sqrt( m00*m00+m10*m10 );\r
-               double sy = Math.sqrt( m01*m01+m11*m11 );\r
-               return Math.sqrt(sx*sx+sy*sy);\r
-       }   */\r
-\r
-    /*\r
-       public static double getScale(AffineTransform at)\r
-       {\r
-               double m00 = at.getScaleX();\r
-               double m11 = at.getScaleY();\r
-               double m10 = at.getShearY();\r
-               double m01 = at.getShearX();\r
-\r
-               double a = m00 + m11;\r
-               double b = 4.0 * m10*m01 + (m00 - m11) * (m00 - m11);\r
-               if(b <= 0.0)\r
-                       return 0.5 * Math.sqrt(a*a + b);\r
-               else\r
-                       return 0.5 * (Math.abs(a) + Math.sqrt(b));\r
-       }*/\r
-\r
-    public static double getScale(AffineTransform at)\r
-    {\r
-        double m00 = at.getScaleX();\r
-        double m11 = at.getScaleY();\r
-        double m10 = at.getShearY();\r
-        double m01 = at.getShearX();\r
-\r
-        return Math.sqrt(Math.abs(m00*m11 - m10*m01));\r
-    }\r
-\r
-    /**\r
-     * Computes the greatest absolute value of the eigenvalues\r
-     * of the matrix.\r
-     */\r
-    public static double getMaxScale(AffineTransform at)\r
-    {\r
-        double m00 = at.getScaleX();\r
-        double m11 = at.getScaleY();\r
-        double m10 = at.getShearY();\r
-        double m01 = at.getShearX();\r
-\r
-        /*\r
-         * If a and b are the eigenvalues of the matrix,\r
-         * then\r
-         *   trace       = a + b\r
-         *   determinant = a*b\r
-         */\r
-        double trace = m00 + m11;\r
-        double determinant = m00*m11 - m10*m01;\r
-\r
-        double dd = trace*trace*0.25 - determinant;\r
-        if(dd >= 0.0) {\r
-            /*\r
-             * trace/2 +- sqrt(trace^2 / 4 - determinant)\r
-             * = (a+b)/2 +- sqrt((a+b)^2 / 4 - a b)\r
-             * = (a+b)/2 +- sqrt(a^2 / 4 + a b / 2 + b^2 / 4 - a b)\r
-             * = (a+b)/2 +- sqrt(a^2 / 4 - a b / 2 + b^2 / 4)\r
-             * = (a+b)/2 +- (a-b)/2\r
-             * = a or b\r
-             * \r
-             * Thus the formula below calculates the greatest\r
-             * absolute value of the eigenvalues max(abs(a), abs(b))\r
-             */\r
-            return Math.abs(trace*0.5) + Math.sqrt(dd);\r
-        }\r
-        else {\r
-            /*\r
-             * If dd < 0, then the eigenvalues a and b are not real.\r
-             * Because both trace and determinant are real, a and b\r
-             * have form:\r
-             *   a = x + i y\r
-             *   b = x - i y\r
-             * \r
-             * Then\r
-             *    sqrt(determinant)\r
-             *    = sqrt(a b)\r
-             *    = sqrt(x^2 + y^2),\r
-             * which is the absolute value of the eigenvalues.\r
-             */\r
-            return Math.sqrt(determinant);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Intersect test of two shapes\r
-     * @param s1\r
-     * @param s2\r
-     * @return\r
-     */\r
-    public static boolean intersects(Shape s1, Shape s2)\r
-    {\r
-        if (s1==s2) return true;\r
-        if (s1.equals(s2)) return true;\r
-        if (s1 instanceof Rectangle2D)\r
-            return s2.intersects((Rectangle2D) s1);\r
-        if (s2 instanceof Rectangle2D)\r
-            return s1.intersects((Rectangle2D) s2);\r
-        if (s1 instanceof TransformedRectangle)\r
-            return ((TransformedRectangle)s1).intersects(s2);\r
-        if (s2 instanceof TransformedRectangle)\r
-            return ((TransformedRectangle)s2).intersects(s1);\r
-\r
-        // VERY SLOW IMPLEMENTATION\r
-        // Convert shapes to areas and intersect them\r
-        Area a1 = new Area(s1);\r
-        Area a2 = new Area(s2);\r
-        a1.intersect(a2);\r
-        return !a1.isEmpty();\r
-    }\r
-\r
-    /**\r
-     * Contains test of two shapes\r
-     * @param s1\r
-     * @param s2\r
-     * @return <code>true</code> if s1 contains s2, else <code>false</code>\r
-     */\r
-    public static boolean contains(Shape s1, Shape s2)\r
-    {\r
-        if (s1==s2) return true;\r
-        if (s1.equals(s2)) return true;\r
-        if (s2 instanceof Rectangle2D)\r
-            return s1.contains((Rectangle2D) s2);\r
-        if (s1 instanceof Rectangle2D)\r
-            return contains((Rectangle2D)s1, s2);\r
-        if (s1 instanceof TransformedRectangle)\r
-            return ((TransformedRectangle)s1).contains(s2);\r
-\r
-        // VERY SLOW IMPLEMENTATION\r
-        // Convert shapes to areas and intersect them\r
-        Area a1 = new Area(s1);\r
-        Area a2 = new Area(s2);\r
-        a2.subtract(a1);\r
-        return a2.isEmpty();\r
-    }\r
-\r
-    /**\r
-     * Tests if rectangle contains a shape\r
-     * @param r rectangle\r
-     * @param s shape to be tested\r
-     * @return <code>true</code> if r contains s, else <code>false</code>\r
-     */\r
-    public static boolean contains(Rectangle2D r, Shape s)\r
-    {\r
-        // Rectangle contains a shape if\r
-        //  all points of the shape are contained in r\r
-        PathIterator pi = s.getPathIterator(null, Double.MAX_VALUE);\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 (!r.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
-    /**\r
-     * Creates new transformed shape\r
-     * @param s shape to be transformed\r
-     * @param t transform\r
-     * @return new transformed shape\r
-     */\r
-    public static Shape transformShape(Shape s, AffineTransform t)\r
-    {\r
-        if (t.isIdentity()) return s;\r
-        if (s instanceof Rectangle2D) {\r
-            Rectangle2D rect = (Rectangle2D) s;\r
-            int type = t.getType();\r
-            if (type == AffineTransform.TYPE_IDENTITY) {\r
-                Rectangle2D r = new Rectangle2D.Double();\r
-                r.setFrame(rect);\r
-                return r;\r
-            }\r
-            if ((type & (AffineTransform.TYPE_GENERAL_ROTATION|AffineTransform.TYPE_GENERAL_TRANSFORM) ) != 0)\r
-                return new TransformedRectangle(rect, t);\r
-            return org.simantics.scenegraph.utils.GeometryUtils.transformRectangle(t, rect);\r
-        }\r
-        if (s instanceof TransformedRectangle)\r
-        {\r
-            TransformedRectangle tr = (TransformedRectangle) s;\r
-            TransformedRectangle result = new TransformedRectangle(tr);\r
-            result.concatenate(t);\r
-            return result;\r
-        }\r
-\r
-        //return t.createTransformedShape(s);\r
-        //return new Area(s).createTransformedArea(t);\r
-        Area result = new Area(s);\r
-        result.transform(t);\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Get compass direction. 0 = north, clockwise\r
-     * @param pt\r
-     * @return\r
-     */\r
-    public static double getCompassDirection(Point2D pt) {\r
-        return getCompassDirection(pt.getX(), pt.getY());\r
-    }\r
-\r
-    /**\r
-     * Get compass direction for the vector p1&rarr;p2. 0 = north, clockwise\r
-     * \r
-     * @param p1 the start of the vector\r
-     * @param p2 the end of the vector\r
-     * @return compass direction\r
-     */\r
-    public static double getCompassDirection(Point2D p1, Point2D p2) {\r
-        double dx = p2.getX() - p1.getX();\r
-        double dy = p2.getY() - p1.getY();\r
-        return getCompassDirection(dx, dy);\r
-    }\r
-\r
-    /**\r
-     * Get compass direction. 0 = north, clockwise\r
-     * @param pt\r
-     * @return\r
-     */\r
-    public static double getCompassDirection(double x, double y) {\r
-        double rad = Math.atan2(y, x);\r
-        double deg = rad*180.0 / Math.PI + 90.0;\r
-        if (deg<0) deg = 360+deg;\r
-        return deg;\r
-    }\r
-\r
-    /**\r
-     * Converts compass direction to unit vector\r
-     * @param deg compass direction\r
-     * @param uv\r
-     * @return\r
-     */\r
-    public static Point2D toUnitVector(double deg, Point2D uv)\r
-    {\r
-        if (uv==null) uv = new Point2D.Double();\r
-        double x=0, y=0;\r
-        if (deg==0) {\r
-            y=-1;\r
-        } else if (deg==90) {\r
-            x=1;\r
-        } else if (deg==180) {\r
-            y=1;\r
-        } else if (deg==270) {\r
-            x=-1;\r
-        } else {\r
-            double rad = (deg-90)*Math.PI/180.0;\r
-            y = Math.sin(rad);\r
-            x = Math.cos(rad);\r
-        }\r
-        uv.setLocation(x, y);\r
-        return uv;\r
-    }\r
-\r
-    /**\r
-     * Convert compass direction to radian presentation (0=right, CCW)\r
-     * @param deg\r
-     * @return radians\r
-     */\r
-    public static double compassToRad(double deg) {\r
-        double rad = (deg-90)*Math.PI/180.0;\r
-        return rad;\r
-\r
-    }\r
-\r
-    /**\r
-     * Interpolate between two colors\r
-     * @param c1\r
-     * @param c2\r
-     * @param phase 0..1 (0=c1, 1=c2)\r
-     * @return\r
-     */\r
-    public static Color interpolate(Color c1, Color c2, double phase)\r
-    {\r
-        float r = (c1.getRed()/255.0f)*(1-(float)phase) + (c2.getRed()/255.0f)*((float)phase);\r
-        float g = (c1.getGreen()/255.0f)*(1-(float)phase) + (c2.getGreen()/255.0f)*((float)phase);\r
-        float b = (c1.getBlue()/255.0f)*(1-(float)phase) + (c2.getBlue()/255.0f)*((float)phase);\r
-        float a = (c1.getAlpha()/255.0f)*(1-(float)phase) + (c2.getAlpha()/255.0f)*((float)phase);\r
-        return new Color(r, g, b, a);\r
-    }\r
-\r
-    public static Path2D buildPath(List<Point2D> positions)\r
-    {\r
-        Path2D result = new Path2D.Double();\r
-        if (positions.size()==0) return result;\r
-        Point2D pos = positions.get(0);\r
-        result.moveTo(pos.getX(), pos.getY());\r
-        for (int i=1; i<positions.size(); i++)\r
-        {\r
-            pos = positions.get(i);\r
-            result.lineTo(pos.getX(), pos.getY());\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Get path positions\r
-     * \r
-     * @param path path\r
-     * @param positions positions\r
-     */\r
-    public static void getPoints(Path2D path, List<Point2D> positions)\r
-    {\r
-        PathIterator pi = path.getPathIterator(null);\r
-        double mat[] = new double[6];\r
-        while (!pi.isDone()) {\r
-            pi.currentSegment(mat);\r
-            positions.add(new Point2D.Double(mat[0], mat[1]));\r
-            pi.next();\r
-        }\r
-    }\r
-\r
-    // Tests intersects and contains\r
-    public static void main(String[] args) {\r
-/*\r
-               System.out.println(toUnitVector(getCompassDirection(0, -1), null));\r
-               System.out.println(toUnitVector(getCompassDirection(1, -1), null));\r
-               System.out.println(toUnitVector(getCompassDirection(1,  0), null));\r
-               System.out.println(toUnitVector(getCompassDirection(1,  1), null));\r
-               System.out.println(toUnitVector(getCompassDirection(0,  1), null));\r
-               System.out.println(toUnitVector(getCompassDirection(-1, 1), null));\r
-               System.out.println(toUnitVector(getCompassDirection(-1, 0), null));\r
-               System.out.println(toUnitVector(getCompassDirection(-1,-1), null));\r
-\r
-               System.out.println(getCompassDirection(0, -1));\r
-               System.out.println(getCompassDirection(1, -1));\r
-               System.out.println(getCompassDirection(1,  0));\r
-               System.out.println(getCompassDirection(1,  1));\r
-               System.out.println(getCompassDirection(0,  1));\r
-               System.out.println(getCompassDirection(-1, 1));\r
-               System.out.println(getCompassDirection(-1, 0));\r
-               System.out.println(getCompassDirection(-1,-1));\r
-\r
-        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(0, -1)));\r
-        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(1, 0)));\r
-        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(0, 1)));\r
-        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(-1, 0)));\r
- */\r
-        Shape s1 = new Polygon(new int[]{10,10,0}, new int[]{0,10,10}, 3);\r
-        Shape s2 = new Ellipse2D.Double(0,0,5,5);\r
-        Shape s3 = new Ellipse2D.Double(8,8,4,4);\r
-        Shape s4 = new Rectangle2D.Double(-5, 3, 20, 2);\r
-        Shape s5 = new Rectangle2D.Double(-100, -100, 200, 200);\r
-        Shape s6 = new Ellipse2D.Double(-100, -100, 200, 200);\r
-\r
-        assert(!intersects(s1, s2) );\r
-        assert( intersects(s1, s3) );\r
-        assert( intersects(s1, s4) );\r
-        assert( intersects(s1, s5) );\r
-\r
-        assert(!intersects(s2, s1) );\r
-        assert(!intersects(s2, s3) );\r
-        assert( intersects(s2, s4) );\r
-        assert( intersects(s2, s5) );\r
-\r
-        assert( intersects(s3, s1) );\r
-        assert(!intersects(s3, s2) );\r
-        assert(!intersects(s3, s4) );\r
-        assert( intersects(s3, s5) );\r
-\r
-        assert( intersects(s4, s1) );\r
-        assert( intersects(s4, s2) );\r
-        assert(!intersects(s4, s3) );\r
-        assert( intersects(s4, s5) );\r
-\r
-        assert( intersects(s5, s1) );\r
-        assert( intersects(s5, s2) );\r
-        assert( intersects(s5, s3) );\r
-        assert( intersects(s5, s4) );\r
-\r
-        assert(!contains(s1, s2) );\r
-        assert(!contains(s1, s3) );\r
-        assert(!contains(s1, s4) );\r
-        assert(!contains(s1, s5) );\r
-\r
-        assert(!contains(s2, s1) );\r
-        assert(!contains(s2, s3) );\r
-        assert(!contains(s2, s4) );\r
-        assert(!contains(s2, s5) );\r
-\r
-        assert(!contains(s3, s1) );\r
-        assert(!contains(s3, s2) );\r
-        assert(!contains(s3, s4) );\r
-        assert(!contains(s3, s5) );\r
-\r
-        assert(!contains(s4, s1) );\r
-        assert(!contains(s4, s2) );\r
-        assert(!contains(s4, s3) );\r
-        assert(!contains(s4, s5) );\r
-\r
-        assert( contains(s5, s1) );\r
-        assert( contains(s5, s2) );\r
-        assert( contains(s5, s3) );\r
-        assert( contains(s5, s4) );\r
-\r
-        assert( contains(s6, s1) );\r
-        assert( contains(s6, s2) );\r
-        assert( contains(s6, s3) );\r
-        assert( contains(s6, s4) );\r
-\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.g2d.utils;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.List;
+
+import org.simantics.scenegraph.utils.TransformedRectangle;
+
+/**
+ * Geometry related Utilities
+ *
+ * @see PathUtils2 Path related utilities
+ * 
+ * @author Toni Kalajainen
+ * @author Tuukka Lehtonen
+ */
+public final class GeometryUtils {
+
+    //public static final double EPSILON = 1e-7;
+
+    public static final BasicStroke BASIC_STROKE = new BasicStroke();
+
+    public static BasicStroke scaleStroke(Stroke stroke, float factor)
+    {
+        BasicStroke s = (BasicStroke) stroke;
+        float[] dash = s.getDashArray();
+        if (dash!=null) {
+            assert(factor!=0);
+            dash = scaleArray(factor, dash, new float[dash.length]);
+        }
+        if (dash==null)
+            return new BasicStroke(
+                    s.getLineWidth() * factor,
+                    s.getEndCap(),
+                    s.getLineJoin(),
+                    s.getMiterLimit()
+            );
+        return new BasicStroke(
+                s.getLineWidth() * factor,
+                s.getEndCap(),
+                s.getLineJoin(),
+                s.getMiterLimit(),
+                dash,
+                s.getDashPhase() * factor
+        );
+    }
+
+
+
+    /**
+     * Scales every element in array
+     * @param array
+     * @param factor
+     * @return new scaled array
+     */
+    public static float[] scaleArray(float factor, float [] array, float [] targetArray)
+    {
+        assert(array!=null);
+        if (targetArray==null)
+            targetArray = new float[array.length];
+        for (int i=0; i<array.length; i++)
+        {
+            targetArray[i] = array[i] * factor;
+        }
+        return targetArray;
+    }
+
+    /**
+     * Get x/y scale of a transform
+     * @param at
+     * @param pt
+     * @return
+     */
+    public static Point2D getScaleXY(AffineTransform at, Point2D pt)
+    {
+        double m00 = at.getScaleX();
+        double m11 = at.getScaleY();
+        double m10 = at.getShearY();
+        double m01 = at.getShearX();
+        // Project unit vector to canvas
+        double sx = Math.sqrt( m00*m00+m10*m10 );
+        double sy = Math.sqrt( m01*m01+m11*m11 );
+        if (pt==null) pt = new Point2D.Double();
+        pt.setLocation(sx, sy);
+        return pt;
+    }
+
+    /**
+     * Get scale of a transform
+     * @param at
+     * @param pt
+     * @return
+     */
+    /*
+       public static double getScale(AffineTransform at)
+       {
+               double m00 = at.getScaleX();
+               double m11 = at.getScaleY();
+               double m10 = at.getShearY();
+               double m01 = at.getShearX();
+               // Project unit vector to canvas
+               double sx = Math.sqrt( m00*m00+m10*m10 );
+               double sy = Math.sqrt( m01*m01+m11*m11 );
+               return Math.sqrt(sx*sx+sy*sy);
+       }   */
+
+    /*
+       public static double getScale(AffineTransform at)
+       {
+               double m00 = at.getScaleX();
+               double m11 = at.getScaleY();
+               double m10 = at.getShearY();
+               double m01 = at.getShearX();
+
+               double a = m00 + m11;
+               double b = 4.0 * m10*m01 + (m00 - m11) * (m00 - m11);
+               if(b <= 0.0)
+                       return 0.5 * Math.sqrt(a*a + b);
+               else
+                       return 0.5 * (Math.abs(a) + Math.sqrt(b));
+       }*/
+
+    public static double getScale(AffineTransform at)
+    {
+        double m00 = at.getScaleX();
+        double m11 = at.getScaleY();
+        double m10 = at.getShearY();
+        double m01 = at.getShearX();
+
+        return Math.sqrt(Math.abs(m00*m11 - m10*m01));
+    }
+
+    /**
+     * Computes the greatest absolute value of the eigenvalues
+     * of the matrix.
+     */
+    public static double getMaxScale(AffineTransform at)
+    {
+        double m00 = at.getScaleX();
+        double m11 = at.getScaleY();
+        double m10 = at.getShearY();
+        double m01 = at.getShearX();
+
+        /*
+         * If a and b are the eigenvalues of the matrix,
+         * then
+         *   trace       = a + b
+         *   determinant = a*b
+         */
+        double trace = m00 + m11;
+        double determinant = m00*m11 - m10*m01;
+
+        double dd = trace*trace*0.25 - determinant;
+        if(dd >= 0.0) {
+            /*
+             * trace/2 +- sqrt(trace^2 / 4 - determinant)
+             * = (a+b)/2 +- sqrt((a+b)^2 / 4 - a b)
+             * = (a+b)/2 +- sqrt(a^2 / 4 + a b / 2 + b^2 / 4 - a b)
+             * = (a+b)/2 +- sqrt(a^2 / 4 - a b / 2 + b^2 / 4)
+             * = (a+b)/2 +- (a-b)/2
+             * = a or b
+             * 
+             * Thus the formula below calculates the greatest
+             * absolute value of the eigenvalues max(abs(a), abs(b))
+             */
+            return Math.abs(trace*0.5) + Math.sqrt(dd);
+        }
+        else {
+            /*
+             * If dd < 0, then the eigenvalues a and b are not real.
+             * Because both trace and determinant are real, a and b
+             * have form:
+             *   a = x + i y
+             *   b = x - i y
+             * 
+             * Then
+             *    sqrt(determinant)
+             *    = sqrt(a b)
+             *    = sqrt(x^2 + y^2),
+             * which is the absolute value of the eigenvalues.
+             */
+            return Math.sqrt(determinant);
+        }
+    }
+
+    /**
+     * Intersect test of two shapes
+     * @param s1
+     * @param s2
+     * @return
+     */
+    public static boolean intersects(Shape s1, Shape s2)
+    {
+        if (s1==s2) return true;
+        if (s1.equals(s2)) return true;
+        if (s1 instanceof Rectangle2D)
+            return s2.intersects((Rectangle2D) s1);
+        if (s2 instanceof Rectangle2D)
+            return s1.intersects((Rectangle2D) s2);
+        if (s1 instanceof TransformedRectangle)
+            return ((TransformedRectangle)s1).intersects(s2);
+        if (s2 instanceof TransformedRectangle)
+            return ((TransformedRectangle)s2).intersects(s1);
+
+        // VERY SLOW IMPLEMENTATION
+        // Convert shapes to areas and intersect them
+        Area a1 = new Area(s1);
+        Area a2 = new Area(s2);
+        a1.intersect(a2);
+        return !a1.isEmpty();
+    }
+
+    /**
+     * Contains test of two shapes
+     * @param s1
+     * @param s2
+     * @return <code>true</code> if s1 contains s2, else <code>false</code>
+     */
+    public static boolean contains(Shape s1, Shape s2)
+    {
+        if (s1==s2) return true;
+        if (s1.equals(s2)) return true;
+        if (s2 instanceof Rectangle2D)
+            return s1.contains((Rectangle2D) s2);
+        if (s1 instanceof Rectangle2D)
+            return contains((Rectangle2D)s1, s2);
+        if (s1 instanceof TransformedRectangle)
+            return ((TransformedRectangle)s1).contains(s2);
+
+        // VERY SLOW IMPLEMENTATION
+        // Convert shapes to areas and intersect them
+        Area a1 = new Area(s1);
+        Area a2 = new Area(s2);
+        a2.subtract(a1);
+        return a2.isEmpty();
+    }
+
+    /**
+     * Tests if rectangle contains a shape
+     * @param r rectangle
+     * @param s shape to be tested
+     * @return <code>true</code> if r contains s, else <code>false</code>
+     */
+    public static boolean contains(Rectangle2D r, Shape s)
+    {
+        // Rectangle contains a shape if
+        //  all points of the shape are contained in r
+        PathIterator pi = s.getPathIterator(null, Double.MAX_VALUE);
+        double coords[] = new double[6];
+        while (!pi.isDone()) {
+            int type = pi.currentSegment(coords);
+            if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO)
+            {
+                if (!r.contains(coords[0], coords[1]))
+                    return false;
+            }
+            assert(type != PathIterator.SEG_CUBICTO && type != PathIterator.SEG_QUADTO);
+            pi.next();
+        }
+        return true;
+    }
+
+    /**
+     * Creates new transformed shape
+     * @param s shape to be transformed
+     * @param t transform
+     * @return new transformed shape
+     */
+    public static Shape transformShape(Shape s, AffineTransform t)
+    {
+        if (t.isIdentity()) return s;
+        if (s instanceof Rectangle2D) {
+            Rectangle2D rect = (Rectangle2D) s;
+            int type = t.getType();
+            if (type == AffineTransform.TYPE_IDENTITY) {
+                Rectangle2D r = new Rectangle2D.Double();
+                r.setFrame(rect);
+                return r;
+            }
+            if ((type & (AffineTransform.TYPE_GENERAL_ROTATION|AffineTransform.TYPE_GENERAL_TRANSFORM) ) != 0)
+                return new TransformedRectangle(rect, t);
+            return org.simantics.scenegraph.utils.GeometryUtils.transformRectangle(t, rect);
+        }
+        if (s instanceof TransformedRectangle)
+        {
+            TransformedRectangle tr = (TransformedRectangle) s;
+            TransformedRectangle result = new TransformedRectangle(tr);
+            result.concatenate(t);
+            return result;
+        }
+
+        //return t.createTransformedShape(s);
+        //return new Area(s).createTransformedArea(t);
+        Area result = new Area(s);
+        result.transform(t);
+        return result;
+    }
+
+    /**
+     * Get compass direction. 0 = north, clockwise
+     * @param pt
+     * @return
+     */
+    public static double getCompassDirection(Point2D pt) {
+        return getCompassDirection(pt.getX(), pt.getY());
+    }
+
+    /**
+     * Get compass direction for the vector p1&rarr;p2. 0 = north, clockwise
+     * 
+     * @param p1 the start of the vector
+     * @param p2 the end of the vector
+     * @return compass direction
+     */
+    public static double getCompassDirection(Point2D p1, Point2D p2) {
+        double dx = p2.getX() - p1.getX();
+        double dy = p2.getY() - p1.getY();
+        return getCompassDirection(dx, dy);
+    }
+
+    /**
+     * Get compass direction. 0 = north, clockwise
+     * @param pt
+     * @return
+     */
+    public static double getCompassDirection(double x, double y) {
+        double rad = Math.atan2(y, x);
+        double deg = rad*180.0 / Math.PI + 90.0;
+        if (deg<0) deg = 360+deg;
+        return deg;
+    }
+
+    /**
+     * Converts compass direction to unit vector
+     * @param deg compass direction
+     * @param uv
+     * @return
+     */
+    public static Point2D toUnitVector(double deg, Point2D uv)
+    {
+        if (uv==null) uv = new Point2D.Double();
+        double x=0, y=0;
+        if (deg==0) {
+            y=-1;
+        } else if (deg==90) {
+            x=1;
+        } else if (deg==180) {
+            y=1;
+        } else if (deg==270) {
+            x=-1;
+        } else {
+            double rad = (deg-90)*Math.PI/180.0;
+            y = Math.sin(rad);
+            x = Math.cos(rad);
+        }
+        uv.setLocation(x, y);
+        return uv;
+    }
+
+    /**
+     * Convert compass direction to radian presentation (0=right, CCW)
+     * @param deg
+     * @return radians
+     */
+    public static double compassToRad(double deg) {
+        double rad = (deg-90)*Math.PI/180.0;
+        return rad;
+
+    }
+
+    /**
+     * Interpolate between two colors
+     * @param c1
+     * @param c2
+     * @param phase 0..1 (0=c1, 1=c2)
+     * @return
+     */
+    public static Color interpolate(Color c1, Color c2, double phase)
+    {
+        float r = (c1.getRed()/255.0f)*(1-(float)phase) + (c2.getRed()/255.0f)*((float)phase);
+        float g = (c1.getGreen()/255.0f)*(1-(float)phase) + (c2.getGreen()/255.0f)*((float)phase);
+        float b = (c1.getBlue()/255.0f)*(1-(float)phase) + (c2.getBlue()/255.0f)*((float)phase);
+        float a = (c1.getAlpha()/255.0f)*(1-(float)phase) + (c2.getAlpha()/255.0f)*((float)phase);
+        return new Color(r, g, b, a);
+    }
+
+    public static Path2D buildPath(List<Point2D> positions)
+    {
+        Path2D result = new Path2D.Double();
+        if (positions.size()==0) return result;
+        Point2D pos = positions.get(0);
+        result.moveTo(pos.getX(), pos.getY());
+        for (int i=1; i<positions.size(); i++)
+        {
+            pos = positions.get(i);
+            result.lineTo(pos.getX(), pos.getY());
+        }
+        return result;
+    }
+
+    /**
+     * Get path positions
+     * 
+     * @param path path
+     * @param positions positions
+     */
+    public static void getPoints(Path2D path, List<Point2D> positions)
+    {
+        PathIterator pi = path.getPathIterator(null);
+        double mat[] = new double[6];
+        while (!pi.isDone()) {
+            pi.currentSegment(mat);
+            positions.add(new Point2D.Double(mat[0], mat[1]));
+            pi.next();
+        }
+    }
+
+    // Tests intersects and contains
+    public static void main(String[] args) {
+/*
+               System.out.println(toUnitVector(getCompassDirection(0, -1), null));
+               System.out.println(toUnitVector(getCompassDirection(1, -1), null));
+               System.out.println(toUnitVector(getCompassDirection(1,  0), null));
+               System.out.println(toUnitVector(getCompassDirection(1,  1), null));
+               System.out.println(toUnitVector(getCompassDirection(0,  1), null));
+               System.out.println(toUnitVector(getCompassDirection(-1, 1), null));
+               System.out.println(toUnitVector(getCompassDirection(-1, 0), null));
+               System.out.println(toUnitVector(getCompassDirection(-1,-1), null));
+
+               System.out.println(getCompassDirection(0, -1));
+               System.out.println(getCompassDirection(1, -1));
+               System.out.println(getCompassDirection(1,  0));
+               System.out.println(getCompassDirection(1,  1));
+               System.out.println(getCompassDirection(0,  1));
+               System.out.println(getCompassDirection(-1, 1));
+               System.out.println(getCompassDirection(-1, 0));
+               System.out.println(getCompassDirection(-1,-1));
+
+        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(0, -1)));
+        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(1, 0)));
+        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(0, 1)));
+        System.out.println(getCompassDirection(new Point2D.Double(0, 0), new Point2D.Double(-1, 0)));
+ */
+        Shape s1 = new Polygon(new int[]{10,10,0}, new int[]{0,10,10}, 3);
+        Shape s2 = new Ellipse2D.Double(0,0,5,5);
+        Shape s3 = new Ellipse2D.Double(8,8,4,4);
+        Shape s4 = new Rectangle2D.Double(-5, 3, 20, 2);
+        Shape s5 = new Rectangle2D.Double(-100, -100, 200, 200);
+        Shape s6 = new Ellipse2D.Double(-100, -100, 200, 200);
+
+        assert(!intersects(s1, s2) );
+        assert( intersects(s1, s3) );
+        assert( intersects(s1, s4) );
+        assert( intersects(s1, s5) );
+
+        assert(!intersects(s2, s1) );
+        assert(!intersects(s2, s3) );
+        assert( intersects(s2, s4) );
+        assert( intersects(s2, s5) );
+
+        assert( intersects(s3, s1) );
+        assert(!intersects(s3, s2) );
+        assert(!intersects(s3, s4) );
+        assert( intersects(s3, s5) );
+
+        assert( intersects(s4, s1) );
+        assert( intersects(s4, s2) );
+        assert(!intersects(s4, s3) );
+        assert( intersects(s4, s5) );
+
+        assert( intersects(s5, s1) );
+        assert( intersects(s5, s2) );
+        assert( intersects(s5, s3) );
+        assert( intersects(s5, s4) );
+
+        assert(!contains(s1, s2) );
+        assert(!contains(s1, s3) );
+        assert(!contains(s1, s4) );
+        assert(!contains(s1, s5) );
+
+        assert(!contains(s2, s1) );
+        assert(!contains(s2, s3) );
+        assert(!contains(s2, s4) );
+        assert(!contains(s2, s5) );
+
+        assert(!contains(s3, s1) );
+        assert(!contains(s3, s2) );
+        assert(!contains(s3, s4) );
+        assert(!contains(s3, s5) );
+
+        assert(!contains(s4, s1) );
+        assert(!contains(s4, s2) );
+        assert(!contains(s4, s3) );
+        assert(!contains(s4, s5) );
+
+        assert( contains(s5, s1) );
+        assert( contains(s5, s2) );
+        assert( contains(s5, s3) );
+        assert( contains(s5, s4) );
+
+        assert( contains(s6, s1) );
+        assert( contains(s6, s2) );
+        assert( contains(s6, s3) );
+        assert( contains(s6, s4) );
+
+    }
+
+}