1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.participant;
\r
15 import static org.simantics.g2d.canvas.Hints.KEY_CANVAS_TRANSFORM;
\r
17 import java.awt.Graphics2D;
\r
18 import java.awt.geom.AffineTransform;
\r
19 import java.awt.geom.NoninvertibleTransformException;
\r
20 import java.awt.geom.Path2D;
\r
21 import java.awt.geom.Point2D;
\r
22 import java.awt.geom.Rectangle2D;
\r
24 import org.simantics.g2d.canvas.Hints;
\r
25 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
\r
26 import org.simantics.g2d.canvas.impl.HintReflection.HintListener;
\r
27 import org.simantics.g2d.scenegraph.SceneGraphConstants;
\r
28 import org.simantics.g2d.utils.GeometryUtils;
\r
29 import org.simantics.scenegraph.g2d.IG2DNode;
\r
30 import org.simantics.scenegraph.utils.NodeUtil;
\r
31 import org.simantics.utils.datastructures.hints.IHintContext.Key;
\r
32 import org.simantics.utils.datastructures.hints.IHintObservable;
\r
33 import org.simantics.utils.page.MarginUtils.Margins;
\r
36 * This class contains utilities for coordinate conversions between control
\r
39 * @author Toni Kalajainen
\r
41 * @author Tuukka Lehtonen
\r
43 public class TransformUtil extends AbstractCanvasParticipant {
\r
45 /** Set dirty if transform changes */
\r
46 @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")
\r
47 public void transformChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
\r
48 if (oldValue != null && oldValue.equals(newValue)) return;
\r
49 //System.out.println("TransformUtil: CANVAS TRANSFORM CHANGED TO " + newValue);
\r
54 * Get viewport rectangle.
\r
55 * Transforms controlRect to area on canvas.
\r
57 * @param controlRect rectangle on control
\r
58 * @return area on canvas
\r
60 public Path2D toCanvasArea(Rectangle2D controlRect)
\r
62 Point2D p[] = toCanvasCorners(controlRect);
\r
64 Path2D path = new Path2D.Double();
\r
65 path.moveTo(p[0].getX(), p[0].getY());
\r
66 path.lineTo(p[1].getX(), p[1].getY());
\r
67 path.lineTo(p[2].getX(), p[2].getY());
\r
68 path.lineTo(p[3].getX(), p[3].getY());
\r
75 * Returns 4 canvas corders in order: top-left, top-right, bottom-right, bottom-left
\r
76 * @param controlRect rectangle on control
\r
77 * @return area on canvas
\r
79 public Point2D[] toCanvasCorners(Rectangle2D controlRect)
\r
81 // at·control point = canvas point
\r
82 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
83 if (at==null) at = new AffineTransform();
\r
86 at = at.createInverse();
\r
87 } catch (NoninvertibleTransformException e) {
\r
91 Point2D p1 = new Point2D.Double(controlRect.getMinX(), controlRect.getMinY());
\r
92 Point2D p2 = new Point2D.Double(controlRect.getMaxX(), controlRect.getMinY());
\r
93 Point2D p3 = new Point2D.Double(controlRect.getMaxX(), controlRect.getMaxY());
\r
94 Point2D p4 = new Point2D.Double(controlRect.getMinX(), controlRect.getMaxY());
\r
98 at.transform(p1, p1);
\r
99 at.transform(p2, p2);
\r
100 at.transform(p3, p3);
\r
101 at.transform(p4, p4);
\r
103 return new Point2D[] {p1, p2, p3, p4};
\r
107 * Converts control rectangle to a rectangle on canvas that contains control rect.
\r
108 * @param controlRect rectangle on control
\r
109 * @return rectangle on canvas that contains control rect
\r
111 public Rectangle2D toCanvasRect(Rectangle2D controlRect)
\r
113 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
115 return (Rectangle2D) controlRect.clone();
\r
117 Path2D path = toCanvasArea(controlRect);
\r
118 return path.getBounds2D();
\r
122 * Transforms from canvas coordinate system to canvas coordinate system (also diagram)
\r
125 public void controlToCanvas(Graphics2D g2d)
\r
127 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
128 if (at==null) return;
\r
133 * Transforms coordinate system of g2d from canvas to control
\r
136 public void canvasToControl(Graphics2D g2d)
\r
138 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
139 if (at==null) return;
\r
141 g2d.transform(at.createInverse());
\r
142 } catch (NoninvertibleTransformException e) {
\r
147 * Convert control point to canvas point
\r
150 public Point2D controlToCanvas(Point2D control, Point2D canvas)
\r
152 AffineTransform at = getInverseTransform();
\r
153 if (canvas==null) canvas = new Point2D.Double();
\r
155 canvas.setLocation(control);
\r
158 at.transform(control, canvas);
\r
163 * Convert canvas point to control
\r
166 public Point2D canvasToControl(Point2D canvas, Point2D control)
\r
168 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
169 if (control==null) control = new Point2D.Double();
\r
171 control.setLocation(canvas);
\r
174 at.transform(canvas, control);
\r
179 * Convert canvas vector to control vector
\r
182 public Point2D canvasVectorToControlVector(Point2D canvas, Point2D control)
\r
184 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
185 if (control==null) control = new Point2D.Double();
\r
187 control.setLocation(canvas);
\r
190 double x = canvas.getX();
\r
191 double y = canvas.getY();
\r
193 double m00 = at.getScaleX();
\r
194 double m11 = at.getScaleY();
\r
195 double m01 = at.getShearX();
\r
196 double m10 = at.getShearY();
\r
198 control.setLocation(
\r
206 * Convert control vector to canvas vector
\r
209 public Point2D controlVectorToCanvasVector(Point2D control, Point2D canvas)
\r
211 AffineTransform at = getInverseTransform();
\r
212 if (canvas==null) canvas = new Point2D.Double();
\r
214 canvas.setLocation(control);
\r
217 double x = control.getX();
\r
218 double y = control.getY();
\r
220 double m00 = at.getScaleX();
\r
221 double m11 = at.getScaleY();
\r
222 double m01 = at.getShearX();
\r
223 double m10 = at.getShearY();
\r
225 canvas.setLocation(
\r
232 * Get a transform that converts
\r
233 * diagram coordinates to control coordinates.
\r
235 * @return transform a clone
\r
237 public AffineTransform getTransform()
\r
239 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
240 if (at==null) return new AffineTransform();
\r
241 return (AffineTransform) at.clone();
\r
245 * Get a transform that converts
\r
246 * diagram vector to control vector.
\r
248 * @return transform a clone
\r
250 public AffineTransform getVectorTransform()
\r
252 AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
\r
253 if (at==null) return new AffineTransform();
\r
254 double m00 = at.getScaleX();
\r
255 double m11 = at.getScaleY();
\r
256 double m01 = at.getShearX();
\r
257 double m10 = at.getShearY();
\r
258 return new AffineTransform(m00, m10, m01, m11, 0, 0);
\r
263 * Transforms control coordinates to diagram coordinates.
\r
265 * @return inverted transform or <code>null</code> if inversion failed
\r
267 public AffineTransform getInverseTransform()
\r
269 AffineTransform at = getTransform();
\r
270 if (at==null) return null;
\r
272 at = at.createInverse();
\r
274 } catch (NoninvertibleTransformException e) {
\r
280 * Tries to invert the specified {@link AffineTransform}.
\r
282 * @parameter at the transform to invert
\r
283 * @return inverted transform or <code>null</code> if inversion failed
\r
285 public AffineTransform getInverseTransform(AffineTransform at)
\r
290 return at.createInverse();
\r
291 } catch (NoninvertibleTransformException e) {
\r
297 * Transforms control vector to diagram vector
\r
300 public AffineTransform getInverseVectorTransform()
\r
302 AffineTransform at = getTransform();
\r
303 if (at==null) return null;
\r
305 at = at.createInverse();
\r
306 double m00 = at.getScaleX();
\r
307 double m11 = at.getScaleY();
\r
308 double m01 = at.getShearX();
\r
309 double m10 = at.getShearY();
\r
310 return new AffineTransform(m00, m10, m01, m11, 0, 0);
\r
311 } catch (NoninvertibleTransformException e) {
\r
317 public void setTransform(AffineTransform at)
\r
319 assert(at.getShearX()!=Double.POSITIVE_INFINITY && at.getShearX()!=Double.NEGATIVE_INFINITY);
\r
320 assert(at.getShearY()!=Double.POSITIVE_INFINITY && at.getShearY()!=Double.NEGATIVE_INFINITY);
\r
321 AffineTransform old = getHint(KEY_CANVAS_TRANSFORM);
\r
322 if(at.equals(old) == false) { // Send event only if the transform has really changed
\r
323 setHint(KEY_CANVAS_TRANSFORM, at);
\r
325 IG2DNode node = NodeUtil.findNodeById(getContext().getSceneGraph(), SceneGraphConstants.NAVIGATION_NODE_PATH);
\r
327 // This is not in hintListener, because we don't want to update
\r
328 // transform if it already came for scenegraph.
\r
329 node.setTransform(at);
\r
333 public void zoom(double scaleFactor)
\r
335 if (scaleFactor==1.0) return;
\r
336 AffineTransform diagramToControl = getTransform();
\r
337 diagramToControl.scale(scaleFactor, scaleFactor);
\r
338 setTransform(diagramToControl);
\r
341 public void rotate(Point2D centerPointCanvas, double angle)
\r
343 if (angle==0.0) return; // and modulos too
\r
344 AffineTransform diagramToControl = getTransform();
\r
345 diagramToControl.rotate(-angle, centerPointCanvas.getX(), centerPointCanvas.getY());
\r
346 setTransform(diagramToControl);
\r
349 public void restoreOrientation(Point2D centerPointCanvas)
\r
351 AffineTransform res = new AffineTransform();
\r
352 AffineTransform at = getTransform();
\r
353 double m01 = at.getShearX();
\r
354 double m11 = at.getScaleY();
\r
355 double theta = Math.atan2(m01, m11);
\r
356 at.rotate(-theta, centerPointCanvas.getX(), centerPointCanvas.getY());
\r
357 res.translate(at.getTranslateX(), at.getTranslateY());
\r
358 res.scale(at.getScaleX(), at.getScaleY());
\r
363 * Get rotate in radians.
\r
366 public double getRotate() {
\r
367 AffineTransform at = getTransform();
\r
368 double m01 = at.getShearX();
\r
369 double m11 = at.getScaleY();
\r
370 return Math.atan2(m01, m11);
\r
374 * Zoom the view port
\r
375 * @param scaleFactor amount to zoom
\r
376 * @param aroundPoint The center point of zoom in diagram coordinates
\r
378 public void zoomAroundDiagramPoint(double scaleFactor, Point2D aroundPoint)
\r
380 AffineTransform diagramToControl = getTransform();
\r
381 // convert to control point
\r
382 aroundPoint = diagramToControl.transform(aroundPoint, new Point2D.Double());
\r
383 zoomAroundControlPoint(scaleFactor, aroundPoint);
\r
387 * Zoom the view port
\r
388 * @param scaleFactor amount to zoom
\r
389 * @param centerPoint the control point to zoom around
\r
391 public void zoomAroundControlPoint(double scaleFactor, Point2D centerPoint)
\r
393 if (scaleFactor==1.0) return;
\r
394 AffineTransform diagramToControl = getTransform();
\r
396 Point2D pointBeforeScale = diagramToControl.inverseTransform(centerPoint, new Point2D.Double());
\r
397 diagramToControl.scale(scaleFactor, scaleFactor);
\r
398 Point2D pointAfterScale = diagramToControl.inverseTransform(centerPoint, new Point2D.Double());
\r
399 double _dx = pointAfterScale.getX() - pointBeforeScale.getX();
\r
400 double _dy = pointAfterScale.getY() - pointBeforeScale.getY();
\r
401 diagramToControl.translate(_dx, _dy);
\r
402 } catch (NoninvertibleTransformException e1) {
\r
404 setTransform(diagramToControl);
\r
408 * Transform the viewport. The transform values are in diagram coordinates
\r
412 public void translateWithCanvasCoordinates(double dx, double dy)
\r
414 AffineTransform controlToDiagram = getTransform();
\r
415 controlToDiagram.translate(dx, dy);
\r
416 setTransform(controlToDiagram);
\r
420 * Transform the viewport. The transform values are in control coordinates
\r
424 public void translateWithControlCoordinates(Point2D offset)
\r
426 if (offset.getX()==0 && offset.getY()==0) return;
\r
427 AffineTransform at = getInverseTransform();
\r
428 offset = at.transform(offset, new Point2D.Double());
\r
429 translateWithCanvasCoordinates(offset.getX()-at.getTranslateX(), offset.getY()-at.getTranslateY());
\r
433 * Get scale ratio between control and canvas
\r
437 public Point2D getScale(Point2D pt)
\r
439 AffineTransform at = getTransform();
\r
440 return GeometryUtils.getScaleXY(at, pt);
\r
444 * Modifies transform to make canvas area fully visible.
\r
445 * @param controlArea
\r
446 * @param diagramArea
\r
447 * @param margins margins
\r
449 public void fitArea(Rectangle2D controlArea, Rectangle2D diagramArea, Margins margins)
\r
451 AffineTransform fitTx = org.simantics.scenegraph.utils.GeometryUtils.fitArea(controlArea, diagramArea, margins);
\r
452 //System.out.println("fitArea(" + controlArea + ", " + diagramArea + ", " + margins + "): " + fitTx);
\r
453 setTransform(fitTx);
\r