]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/participant/TransformUtil.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / participant / TransformUtil.java
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/participant/TransformUtil.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/participant/TransformUtil.java
new file mode 100644 (file)
index 0000000..e1c7719
--- /dev/null
@@ -0,0 +1,456 @@
+/*******************************************************************************\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.participant;\r
+\r
+\r
+import static org.simantics.g2d.canvas.Hints.KEY_CANVAS_TRANSFORM;\r
+\r
+import java.awt.Graphics2D;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.NoninvertibleTransformException;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.g2d.canvas.Hints;\r
+import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
+import org.simantics.g2d.canvas.impl.HintReflection.HintListener;\r
+import org.simantics.g2d.scenegraph.SceneGraphConstants;\r
+import org.simantics.g2d.utils.GeometryUtils;\r
+import org.simantics.scenegraph.g2d.IG2DNode;\r
+import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintObservable;\r
+import org.simantics.utils.page.MarginUtils.Margins;\r
+\r
+/**\r
+ * This class contains utilities for coordinate conversions between control\r
+ * and canvas.\r
+ * \r
+ * @author Toni Kalajainen\r
+ * @author J-P Laine\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class TransformUtil extends AbstractCanvasParticipant {\r
+\r
+    /** Set dirty if transform changes */\r
+    @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")\r
+    public void transformChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
+        if (oldValue != null && oldValue.equals(newValue)) return;\r
+        //System.out.println("TransformUtil: CANVAS TRANSFORM CHANGED TO " + newValue);\r
+        setDirty();\r
+    }\r
+\r
+    /**\r
+     * Get viewport rectangle.\r
+     * Transforms controlRect to area on canvas.\r
+     * \r
+     * @param controlRect rectangle on control\r
+     * @return area on canvas\r
+     */\r
+    public Path2D toCanvasArea(Rectangle2D controlRect)\r
+    {\r
+        Point2D p[] = toCanvasCorners(controlRect);\r
+\r
+        Path2D path = new Path2D.Double();\r
+        path.moveTo(p[0].getX(), p[0].getY());\r
+        path.lineTo(p[1].getX(), p[1].getY());\r
+        path.lineTo(p[2].getX(), p[2].getY());\r
+        path.lineTo(p[3].getX(), p[3].getY());\r
+        path.closePath();\r
+\r
+        return path;\r
+    }\r
+\r
+    /**\r
+     * Returns 4 canvas corders in order: top-left, top-right, bottom-right, bottom-left\r
+     * @param controlRect rectangle on control\r
+     * @return area on canvas\r
+     */\r
+    public Point2D[] toCanvasCorners(Rectangle2D controlRect)\r
+    {\r
+        // at·control point = canvas point\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (at==null) at = new AffineTransform();\r
+\r
+        try {\r
+            at = at.createInverse();\r
+        } catch (NoninvertibleTransformException e) {\r
+        }\r
+\r
+        // Get corners\r
+        Point2D p1 = new Point2D.Double(controlRect.getMinX(), controlRect.getMinY());\r
+        Point2D p2 = new Point2D.Double(controlRect.getMaxX(), controlRect.getMinY());\r
+        Point2D p3 = new Point2D.Double(controlRect.getMaxX(), controlRect.getMaxY());\r
+        Point2D p4 = new Point2D.Double(controlRect.getMinX(), controlRect.getMaxY());\r
+\r
+\r
+        // Convert corners\r
+        at.transform(p1, p1);\r
+        at.transform(p2, p2);\r
+        at.transform(p3, p3);\r
+        at.transform(p4, p4);\r
+\r
+        return new Point2D[] {p1, p2, p3, p4};\r
+    }\r
+\r
+    /**\r
+     * Converts control rectangle to a rectangle on canvas that contains control rect.\r
+     * @param controlRect rectangle on control\r
+     * @return rectangle on canvas that contains control rect\r
+     */\r
+    public Rectangle2D toCanvasRect(Rectangle2D controlRect)\r
+    {\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (at==null)\r
+            return (Rectangle2D) controlRect.clone();\r
+\r
+        Path2D path = toCanvasArea(controlRect);\r
+        return path.getBounds2D();\r
+    }\r
+\r
+    /**\r
+     * Transforms from canvas coordinate system to canvas coordinate system (also diagram)\r
+     * @param g2d\r
+     */\r
+    public void controlToCanvas(Graphics2D g2d)\r
+    {\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (at==null) return;\r
+        g2d.transform(at);\r
+    }\r
+\r
+    /**\r
+     * Transforms coordinate system of g2d from canvas to control\r
+     * @param g2d\r
+     */\r
+    public void canvasToControl(Graphics2D g2d)\r
+    {\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (at==null) return;\r
+        try {\r
+            g2d.transform(at.createInverse());\r
+        } catch (NoninvertibleTransformException e) {\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Convert control point to canvas point\r
+     * @param g2d\r
+     */\r
+    public Point2D controlToCanvas(Point2D control, Point2D canvas)\r
+    {\r
+        AffineTransform at = getInverseTransform();\r
+        if (canvas==null) canvas = new Point2D.Double();\r
+        if (at==null) {\r
+            canvas.setLocation(control);\r
+            return canvas;\r
+        }\r
+        at.transform(control, canvas);\r
+        return canvas;\r
+    }\r
+\r
+    /**\r
+     * Convert canvas point to control\r
+     * @param g2d\r
+     */\r
+    public Point2D canvasToControl(Point2D canvas, Point2D control)\r
+    {\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (control==null) control = new Point2D.Double();\r
+        if (at==null) {\r
+            control.setLocation(canvas);\r
+            return canvas;\r
+        }\r
+        at.transform(canvas, control);\r
+        return control;\r
+    }\r
+\r
+    /**\r
+     * Convert canvas vector to control vector\r
+     * @param g2d\r
+     */\r
+    public Point2D canvasVectorToControlVector(Point2D canvas, Point2D control)\r
+    {\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (control==null) control = new Point2D.Double();\r
+        if (at==null) {\r
+            control.setLocation(canvas);\r
+            return canvas;\r
+        }\r
+        double x = canvas.getX();\r
+        double y = canvas.getY();\r
+\r
+        double m00 = at.getScaleX();\r
+        double m11 = at.getScaleY();\r
+        double m01 = at.getShearX();\r
+        double m10 = at.getShearY();\r
+\r
+        control.setLocation(\r
+                x * m00 + y * m01,\r
+                x * m10 + y * m11\r
+        );\r
+        return control;\r
+    }\r
+\r
+    /**\r
+     * Convert control vector to canvas vector\r
+     * @param g2d\r
+     */\r
+    public Point2D controlVectorToCanvasVector(Point2D control, Point2D canvas)\r
+    {\r
+        AffineTransform at = getInverseTransform();\r
+        if (canvas==null) canvas = new Point2D.Double();\r
+        if (at==null) {\r
+            canvas.setLocation(control);\r
+            return canvas;\r
+        }\r
+        double x = control.getX();\r
+        double y = control.getY();\r
+\r
+        double m00 = at.getScaleX();\r
+        double m11 = at.getScaleY();\r
+        double m01 = at.getShearX();\r
+        double m10 = at.getShearY();\r
+\r
+        canvas.setLocation(\r
+                x * m00 + y * m01,\r
+                x * m10 + y * m11\r
+        );\r
+        return canvas;\r
+    }\r
+    /**\r
+     * Get a transform that converts\r
+     * diagram coordinates to control coordinates.\r
+     * \r
+     * @return transform a clone\r
+     */\r
+    public AffineTransform getTransform()\r
+    {\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (at==null) return new AffineTransform();\r
+        return (AffineTransform) at.clone();\r
+    }\r
+\r
+    /**\r
+     * Get a transform that converts\r
+     * diagram vector to control vector.\r
+     * \r
+     * @return transform a clone\r
+     */\r
+    public AffineTransform getVectorTransform()\r
+    {\r
+        AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
+        if (at==null) return new AffineTransform();\r
+        double m00 = at.getScaleX();\r
+        double m11 = at.getScaleY();\r
+        double m01 = at.getShearX();\r
+        double m10 = at.getShearY();\r
+        return new AffineTransform(m00, m10, m01, m11, 0, 0);\r
+    }\r
+\r
+\r
+    /**\r
+     * Transforms control coordinates to diagram coordinates.\r
+     * \r
+     * @return inverted transform or <code>null</code> if inversion failed\r
+     */\r
+    public AffineTransform getInverseTransform()\r
+    {\r
+        AffineTransform at = getTransform();\r
+        if (at==null) return null;\r
+        try {\r
+            at = at.createInverse();\r
+            return at;\r
+        } catch (NoninvertibleTransformException e) {\r
+            return null;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Tries to invert the specified {@link AffineTransform}.\r
+     * \r
+     * @parameter at the transform to invert\r
+     * @return inverted transform or <code>null</code> if inversion failed\r
+     */\r
+    public AffineTransform getInverseTransform(AffineTransform at)\r
+    {\r
+        if (at == null)\r
+            return null;\r
+        try {\r
+            return at.createInverse();\r
+        } catch (NoninvertibleTransformException e) {\r
+            return null;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Transforms control vector to diagram vector\r
+     * @return a clone\r
+     */\r
+    public AffineTransform getInverseVectorTransform()\r
+    {\r
+        AffineTransform at = getTransform();\r
+        if (at==null) return null;\r
+        try {\r
+            at = at.createInverse();\r
+            double m00 = at.getScaleX();\r
+            double m11 = at.getScaleY();\r
+            double m01 = at.getShearX();\r
+            double m10 = at.getShearY();\r
+            return new AffineTransform(m00, m10, m01, m11, 0, 0);\r
+        } catch (NoninvertibleTransformException e) {\r
+            return null;\r
+        }\r
+    }\r
+\r
+\r
+    public void setTransform(AffineTransform at)\r
+    {\r
+        assert(at.getShearX()!=Double.POSITIVE_INFINITY && at.getShearX()!=Double.NEGATIVE_INFINITY);\r
+        assert(at.getShearY()!=Double.POSITIVE_INFINITY && at.getShearY()!=Double.NEGATIVE_INFINITY);\r
+        AffineTransform old = getHint(KEY_CANVAS_TRANSFORM);\r
+        if(at.equals(old) == false) { // Send event only if the transform has really changed\r
+            setHint(KEY_CANVAS_TRANSFORM, at);\r
+\r
+            IG2DNode node = NodeUtil.findNodeById(getContext().getSceneGraph(), SceneGraphConstants.NAVIGATION_NODE_PATH);\r
+            if (node != null)\r
+                // This is not in hintListener, because we don't want to update\r
+                // transform if it already came for scenegraph.\r
+                node.setTransform(at);\r
+        }\r
+    }\r
+\r
+    public void zoom(double scaleFactor)\r
+    {\r
+        if (scaleFactor==1.0) return;\r
+        AffineTransform diagramToControl = getTransform();\r
+        diagramToControl.scale(scaleFactor, scaleFactor);\r
+        setTransform(diagramToControl);\r
+    }\r
+\r
+    public void rotate(Point2D centerPointCanvas, double angle)\r
+    {\r
+        if (angle==0.0) return; // and modulos too\r
+        AffineTransform diagramToControl = getTransform();\r
+        diagramToControl.rotate(-angle, centerPointCanvas.getX(), centerPointCanvas.getY());\r
+        setTransform(diagramToControl);\r
+    }\r
+\r
+    public void restoreOrientation(Point2D centerPointCanvas)\r
+    {\r
+        AffineTransform res = new AffineTransform();\r
+        AffineTransform at = getTransform();\r
+        double m01             = at.getShearX();\r
+        double m11             = at.getScaleY();\r
+        double theta   = Math.atan2(m01, m11);\r
+        at.rotate(-theta, centerPointCanvas.getX(), centerPointCanvas.getY());\r
+        res.translate(at.getTranslateX(), at.getTranslateY());\r
+        res.scale(at.getScaleX(), at.getScaleY());\r
+        setTransform(res);\r
+    }\r
+\r
+    /**\r
+     * Get rotate in radians.\r
+     * @return\r
+     */\r
+    public double getRotate() {\r
+        AffineTransform at = getTransform();\r
+        double m01 = at.getShearX();\r
+        double m11 = at.getScaleY();\r
+        return Math.atan2(m01, m11);\r
+    }\r
+\r
+    /**\r
+     * Zoom the view port\r
+     * @param scaleFactor amount to zoom\r
+     * @param aroundPoint The center point of zoom in diagram coordinates\r
+     */\r
+    public void zoomAroundDiagramPoint(double scaleFactor, Point2D aroundPoint)\r
+    {\r
+        AffineTransform diagramToControl = getTransform();\r
+        // convert to control point\r
+        aroundPoint = diagramToControl.transform(aroundPoint, new Point2D.Double());\r
+        zoomAroundControlPoint(scaleFactor, aroundPoint);\r
+    }\r
+\r
+    /**\r
+     * Zoom the view port\r
+     * @param scaleFactor amount to zoom\r
+     * @param centerPoint the control point to zoom around\r
+     */\r
+    public void zoomAroundControlPoint(double scaleFactor, Point2D centerPoint)\r
+    {\r
+        if (scaleFactor==1.0) return;\r
+        AffineTransform diagramToControl = getTransform();\r
+        try {\r
+            Point2D pointBeforeScale = diagramToControl.inverseTransform(centerPoint, new Point2D.Double());\r
+            diagramToControl.scale(scaleFactor, scaleFactor);\r
+            Point2D pointAfterScale = diagramToControl.inverseTransform(centerPoint, new Point2D.Double());\r
+            double _dx = pointAfterScale.getX() - pointBeforeScale.getX();\r
+            double _dy = pointAfterScale.getY() - pointBeforeScale.getY();\r
+            diagramToControl.translate(_dx, _dy);\r
+        } catch (NoninvertibleTransformException e1) {\r
+        }\r
+        setTransform(diagramToControl);\r
+    }\r
+\r
+    /**\r
+     * Transform the viewport. The transform values are in diagram coordinates\r
+     * @param dx\r
+     * @param dy\r
+     */\r
+    public void translateWithCanvasCoordinates(double dx, double dy)\r
+    {\r
+        AffineTransform controlToDiagram = getTransform();\r
+        controlToDiagram.translate(dx, dy);\r
+        setTransform(controlToDiagram);\r
+    }\r
+\r
+    /**\r
+     * Transform the viewport. The transform values are in control coordinates\r
+     * @param dx\r
+     * @param dy\r
+     */\r
+    public void translateWithControlCoordinates(Point2D offset)\r
+    {\r
+        if (offset.getX()==0 && offset.getY()==0) return;\r
+        AffineTransform at = getInverseTransform();\r
+        offset = at.transform(offset, new Point2D.Double());\r
+        translateWithCanvasCoordinates(offset.getX()-at.getTranslateX(), offset.getY()-at.getTranslateY());\r
+    }\r
+\r
+    /**\r
+     * Get scale ratio between control and canvas\r
+     * @param pt\r
+     * @return\r
+     */\r
+    public Point2D getScale(Point2D pt)\r
+    {\r
+        AffineTransform at = getTransform();\r
+        return GeometryUtils.getScaleXY(at, pt);\r
+    }\r
+\r
+    /**\r
+     * Modifies transform to make canvas area fully visible.\r
+     * @param controlArea\r
+     * @param diagramArea\r
+     * @param margins margins\r
+     */\r
+    public void fitArea(Rectangle2D controlArea, Rectangle2D diagramArea, Margins margins)\r
+    {\r
+        AffineTransform fitTx = org.simantics.scenegraph.utils.GeometryUtils.fitArea(controlArea, diagramArea, margins);\r
+        //System.out.println("fitArea(" + controlArea + ", " + diagramArea + ", " + margins + "): " + fitTx);\r
+        setTransform(fitTx);\r
+    }\r
+\r
+}\r