]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/utils/GeometryUtils.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / utils / GeometryUtils.java
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/GeometryUtils.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/utils/GeometryUtils.java
new file mode 100644 (file)
index 0000000..73d0f54
--- /dev/null
@@ -0,0 +1,529 @@
+/*******************************************************************************\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