--- /dev/null
+/*******************************************************************************\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