]> gerrit.simantics Code Review - simantics/platform.git/blob - 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
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.participant;\r
13 \r
14 \r
15 import static org.simantics.g2d.canvas.Hints.KEY_CANVAS_TRANSFORM;\r
16 \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
23 \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
34 \r
35 /**\r
36  * This class contains utilities for coordinate conversions between control\r
37  * and canvas.\r
38  * \r
39  * @author Toni Kalajainen\r
40  * @author J-P Laine\r
41  * @author Tuukka Lehtonen\r
42  */\r
43 public class TransformUtil extends AbstractCanvasParticipant {\r
44 \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
50         setDirty();\r
51     }\r
52 \r
53     /**\r
54      * Get viewport rectangle.\r
55      * Transforms controlRect to area on canvas.\r
56      * \r
57      * @param controlRect rectangle on control\r
58      * @return area on canvas\r
59      */\r
60     public Path2D toCanvasArea(Rectangle2D controlRect)\r
61     {\r
62         Point2D p[] = toCanvasCorners(controlRect);\r
63 \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
69         path.closePath();\r
70 \r
71         return path;\r
72     }\r
73 \r
74     /**\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
78      */\r
79     public Point2D[] toCanvasCorners(Rectangle2D controlRect)\r
80     {\r
81         // at·control point = canvas point\r
82         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
83         if (at==null) at = new AffineTransform();\r
84 \r
85         try {\r
86             at = at.createInverse();\r
87         } catch (NoninvertibleTransformException e) {\r
88         }\r
89 \r
90         // Get corners\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
95 \r
96 \r
97         // Convert corners\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
102 \r
103         return new Point2D[] {p1, p2, p3, p4};\r
104     }\r
105 \r
106     /**\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
110      */\r
111     public Rectangle2D toCanvasRect(Rectangle2D controlRect)\r
112     {\r
113         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
114         if (at==null)\r
115             return (Rectangle2D) controlRect.clone();\r
116 \r
117         Path2D path = toCanvasArea(controlRect);\r
118         return path.getBounds2D();\r
119     }\r
120 \r
121     /**\r
122      * Transforms from canvas coordinate system to canvas coordinate system (also diagram)\r
123      * @param g2d\r
124      */\r
125     public void controlToCanvas(Graphics2D g2d)\r
126     {\r
127         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
128         if (at==null) return;\r
129         g2d.transform(at);\r
130     }\r
131 \r
132     /**\r
133      * Transforms coordinate system of g2d from canvas to control\r
134      * @param g2d\r
135      */\r
136     public void canvasToControl(Graphics2D g2d)\r
137     {\r
138         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
139         if (at==null) return;\r
140         try {\r
141             g2d.transform(at.createInverse());\r
142         } catch (NoninvertibleTransformException e) {\r
143         }\r
144     }\r
145 \r
146     /**\r
147      * Convert control point to canvas point\r
148      * @param g2d\r
149      */\r
150     public Point2D controlToCanvas(Point2D control, Point2D canvas)\r
151     {\r
152         AffineTransform at = getInverseTransform();\r
153         if (canvas==null) canvas = new Point2D.Double();\r
154         if (at==null) {\r
155             canvas.setLocation(control);\r
156             return canvas;\r
157         }\r
158         at.transform(control, canvas);\r
159         return canvas;\r
160     }\r
161 \r
162     /**\r
163      * Convert canvas point to control\r
164      * @param g2d\r
165      */\r
166     public Point2D canvasToControl(Point2D canvas, Point2D control)\r
167     {\r
168         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
169         if (control==null) control = new Point2D.Double();\r
170         if (at==null) {\r
171             control.setLocation(canvas);\r
172             return canvas;\r
173         }\r
174         at.transform(canvas, control);\r
175         return control;\r
176     }\r
177 \r
178     /**\r
179      * Convert canvas vector to control vector\r
180      * @param g2d\r
181      */\r
182     public Point2D canvasVectorToControlVector(Point2D canvas, Point2D control)\r
183     {\r
184         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
185         if (control==null) control = new Point2D.Double();\r
186         if (at==null) {\r
187             control.setLocation(canvas);\r
188             return canvas;\r
189         }\r
190         double x = canvas.getX();\r
191         double y = canvas.getY();\r
192 \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
197 \r
198         control.setLocation(\r
199                 x * m00 + y * m01,\r
200                 x * m10 + y * m11\r
201         );\r
202         return control;\r
203     }\r
204 \r
205     /**\r
206      * Convert control vector to canvas vector\r
207      * @param g2d\r
208      */\r
209     public Point2D controlVectorToCanvasVector(Point2D control, Point2D canvas)\r
210     {\r
211         AffineTransform at = getInverseTransform();\r
212         if (canvas==null) canvas = new Point2D.Double();\r
213         if (at==null) {\r
214             canvas.setLocation(control);\r
215             return canvas;\r
216         }\r
217         double x = control.getX();\r
218         double y = control.getY();\r
219 \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
224 \r
225         canvas.setLocation(\r
226                 x * m00 + y * m01,\r
227                 x * m10 + y * m11\r
228         );\r
229         return canvas;\r
230     }\r
231     /**\r
232      * Get a transform that converts\r
233      * diagram coordinates to control coordinates.\r
234      * \r
235      * @return transform a clone\r
236      */\r
237     public AffineTransform getTransform()\r
238     {\r
239         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);\r
240         if (at==null) return new AffineTransform();\r
241         return (AffineTransform) at.clone();\r
242     }\r
243 \r
244     /**\r
245      * Get a transform that converts\r
246      * diagram vector to control vector.\r
247      * \r
248      * @return transform a clone\r
249      */\r
250     public AffineTransform getVectorTransform()\r
251     {\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
259     }\r
260 \r
261 \r
262     /**\r
263      * Transforms control coordinates to diagram coordinates.\r
264      * \r
265      * @return inverted transform or <code>null</code> if inversion failed\r
266      */\r
267     public AffineTransform getInverseTransform()\r
268     {\r
269         AffineTransform at = getTransform();\r
270         if (at==null) return null;\r
271         try {\r
272             at = at.createInverse();\r
273             return at;\r
274         } catch (NoninvertibleTransformException e) {\r
275             return null;\r
276         }\r
277     }\r
278 \r
279     /**\r
280      * Tries to invert the specified {@link AffineTransform}.\r
281      * \r
282      * @parameter at the transform to invert\r
283      * @return inverted transform or <code>null</code> if inversion failed\r
284      */\r
285     public AffineTransform getInverseTransform(AffineTransform at)\r
286     {\r
287         if (at == null)\r
288             return null;\r
289         try {\r
290             return at.createInverse();\r
291         } catch (NoninvertibleTransformException e) {\r
292             return null;\r
293         }\r
294     }\r
295 \r
296     /**\r
297      * Transforms control vector to diagram vector\r
298      * @return a clone\r
299      */\r
300     public AffineTransform getInverseVectorTransform()\r
301     {\r
302         AffineTransform at = getTransform();\r
303         if (at==null) return null;\r
304         try {\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
312             return null;\r
313         }\r
314     }\r
315 \r
316 \r
317     public void setTransform(AffineTransform at)\r
318     {\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
324 \r
325             IG2DNode node = NodeUtil.findNodeById(getContext().getSceneGraph(), SceneGraphConstants.NAVIGATION_NODE_PATH);\r
326             if (node != null)\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
330         }\r
331     }\r
332 \r
333     public void zoom(double scaleFactor)\r
334     {\r
335         if (scaleFactor==1.0) return;\r
336         AffineTransform diagramToControl = getTransform();\r
337         diagramToControl.scale(scaleFactor, scaleFactor);\r
338         setTransform(diagramToControl);\r
339     }\r
340 \r
341     public void rotate(Point2D centerPointCanvas, double angle)\r
342     {\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
347     }\r
348 \r
349     public void restoreOrientation(Point2D centerPointCanvas)\r
350     {\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
359         setTransform(res);\r
360     }\r
361 \r
362     /**\r
363      * Get rotate in radians.\r
364      * @return\r
365      */\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
371     }\r
372 \r
373     /**\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
377      */\r
378     public void zoomAroundDiagramPoint(double scaleFactor, Point2D aroundPoint)\r
379     {\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
384     }\r
385 \r
386     /**\r
387      * Zoom the view port\r
388      * @param scaleFactor amount to zoom\r
389      * @param centerPoint the control point to zoom around\r
390      */\r
391     public void zoomAroundControlPoint(double scaleFactor, Point2D centerPoint)\r
392     {\r
393         if (scaleFactor==1.0) return;\r
394         AffineTransform diagramToControl = getTransform();\r
395         try {\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
403         }\r
404         setTransform(diagramToControl);\r
405     }\r
406 \r
407     /**\r
408      * Transform the viewport. The transform values are in diagram coordinates\r
409      * @param dx\r
410      * @param dy\r
411      */\r
412     public void translateWithCanvasCoordinates(double dx, double dy)\r
413     {\r
414         AffineTransform controlToDiagram = getTransform();\r
415         controlToDiagram.translate(dx, dy);\r
416         setTransform(controlToDiagram);\r
417     }\r
418 \r
419     /**\r
420      * Transform the viewport. The transform values are in control coordinates\r
421      * @param dx\r
422      * @param dy\r
423      */\r
424     public void translateWithControlCoordinates(Point2D offset)\r
425     {\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
430     }\r
431 \r
432     /**\r
433      * Get scale ratio between control and canvas\r
434      * @param pt\r
435      * @return\r
436      */\r
437     public Point2D getScale(Point2D pt)\r
438     {\r
439         AffineTransform at = getTransform();\r
440         return GeometryUtils.getScaleXY(at, pt);\r
441     }\r
442 \r
443     /**\r
444      * Modifies transform to make canvas area fully visible.\r
445      * @param controlArea\r
446      * @param diagramArea\r
447      * @param margins margins\r
448      */\r
449     public void fitArea(Rectangle2D controlArea, Rectangle2D diagramArea, Margins margins)\r
450     {\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
454     }\r
455 \r
456 }\r