X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.g2d%2Fsrc%2Forg%2Fsimantics%2Fg2d%2Fparticipant%2FTransformUtil.java;fp=bundles%2Forg.simantics.g2d%2Fsrc%2Forg%2Fsimantics%2Fg2d%2Fparticipant%2FTransformUtil.java;h=e1c7719e0bbf488d5f4966e1b74379c021a7c6e5;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git 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 index 000000000..e1c7719e0 --- /dev/null +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/participant/TransformUtil.java @@ -0,0 +1,456 @@ +/******************************************************************************* + * 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 null 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 null 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); + } + +}