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