]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/participant/TransformUtil.java
Fixed invalid comparisons which were identified by Eclipse IDE 2018-09
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / participant / TransformUtil.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g2d.participant;
13
14
15 import static org.simantics.g2d.canvas.Hints.KEY_CANVAS_TRANSFORM;
16
17 import java.awt.Graphics2D;
18 import java.awt.geom.AffineTransform;
19 import java.awt.geom.NoninvertibleTransformException;
20 import java.awt.geom.Path2D;
21 import java.awt.geom.Point2D;
22 import java.awt.geom.Rectangle2D;
23
24 import org.simantics.g2d.canvas.Hints;
25 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
26 import org.simantics.g2d.canvas.impl.HintReflection.HintListener;
27 import org.simantics.g2d.scenegraph.SceneGraphConstants;
28 import org.simantics.g2d.utils.GeometryUtils;
29 import org.simantics.scenegraph.g2d.IG2DNode;
30 import org.simantics.scenegraph.utils.NodeUtil;
31 import org.simantics.utils.datastructures.hints.IHintContext.Key;
32 import org.simantics.utils.datastructures.hints.IHintObservable;
33 import org.simantics.utils.page.MarginUtils.Margins;
34
35 /**
36  * This class contains utilities for coordinate conversions between control
37  * and canvas.
38  * 
39  * @author Toni Kalajainen
40  * @author J-P Laine
41  * @author Tuukka Lehtonen
42  */
43 public class TransformUtil extends AbstractCanvasParticipant {
44
45     /** Set dirty if transform changes */
46     @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")
47     public void transformChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
48         if (oldValue != null && oldValue.equals(newValue)) return;
49         //System.out.println("TransformUtil: CANVAS TRANSFORM CHANGED TO " + newValue);
50         setDirty();
51     }
52
53     /**
54      * Get viewport rectangle.
55      * Transforms controlRect to area on canvas.
56      * 
57      * @param controlRect rectangle on control
58      * @return area on canvas
59      */
60     public Path2D toCanvasArea(Rectangle2D controlRect)
61     {
62         Point2D p[] = toCanvasCorners(controlRect);
63
64         Path2D path = new Path2D.Double();
65         path.moveTo(p[0].getX(), p[0].getY());
66         path.lineTo(p[1].getX(), p[1].getY());
67         path.lineTo(p[2].getX(), p[2].getY());
68         path.lineTo(p[3].getX(), p[3].getY());
69         path.closePath();
70
71         return path;
72     }
73
74     /**
75      * Returns 4 canvas corders in order: top-left, top-right, bottom-right, bottom-left
76      * @param controlRect rectangle on control
77      * @return area on canvas
78      */
79     public Point2D[] toCanvasCorners(Rectangle2D controlRect)
80     {
81         // at·control point = canvas point
82         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
83         if (at==null) at = new AffineTransform();
84
85         try {
86             at = at.createInverse();
87         } catch (NoninvertibleTransformException e) {
88         }
89
90         // Get corners
91         Point2D p1 = new Point2D.Double(controlRect.getMinX(), controlRect.getMinY());
92         Point2D p2 = new Point2D.Double(controlRect.getMaxX(), controlRect.getMinY());
93         Point2D p3 = new Point2D.Double(controlRect.getMaxX(), controlRect.getMaxY());
94         Point2D p4 = new Point2D.Double(controlRect.getMinX(), controlRect.getMaxY());
95
96
97         // Convert corners
98         at.transform(p1, p1);
99         at.transform(p2, p2);
100         at.transform(p3, p3);
101         at.transform(p4, p4);
102
103         return new Point2D[] {p1, p2, p3, p4};
104     }
105
106     /**
107      * Converts control rectangle to a rectangle on canvas that contains control rect.
108      * @param controlRect rectangle on control
109      * @return rectangle on canvas that contains control rect
110      */
111     public Rectangle2D toCanvasRect(Rectangle2D controlRect)
112     {
113         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
114         if (at==null)
115             return (Rectangle2D) controlRect.clone();
116
117         Path2D path = toCanvasArea(controlRect);
118         return path.getBounds2D();
119     }
120
121     /**
122      * Transforms from canvas coordinate system to canvas coordinate system (also diagram)
123      * @param g2d
124      */
125     public void controlToCanvas(Graphics2D g2d)
126     {
127         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
128         if (at==null) return;
129         g2d.transform(at);
130     }
131
132     /**
133      * Transforms coordinate system of g2d from canvas to control
134      * @param g2d
135      */
136     public void canvasToControl(Graphics2D g2d)
137     {
138         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
139         if (at==null) return;
140         try {
141             g2d.transform(at.createInverse());
142         } catch (NoninvertibleTransformException e) {
143         }
144     }
145
146     /**
147      * Convert control point to canvas point
148      * @param g2d
149      */
150     public Point2D controlToCanvas(Point2D control, Point2D canvas)
151     {
152         AffineTransform at = getInverseTransform();
153         if (canvas==null) canvas = new Point2D.Double();
154         if (at==null) {
155             canvas.setLocation(control);
156             return canvas;
157         }
158         at.transform(control, canvas);
159         return canvas;
160     }
161
162     /**
163      * Convert canvas point to control
164      * @param g2d
165      */
166     public Point2D canvasToControl(Point2D canvas, Point2D control)
167     {
168         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
169         if (control==null) control = new Point2D.Double();
170         if (at==null) {
171             control.setLocation(canvas);
172             return canvas;
173         }
174         at.transform(canvas, control);
175         return control;
176     }
177
178     /**
179      * Convert canvas vector to control vector
180      * @param g2d
181      */
182     public Point2D canvasVectorToControlVector(Point2D canvas, Point2D control)
183     {
184         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
185         if (control==null) control = new Point2D.Double();
186         if (at==null) {
187             control.setLocation(canvas);
188             return canvas;
189         }
190         double x = canvas.getX();
191         double y = canvas.getY();
192
193         double m00 = at.getScaleX();
194         double m11 = at.getScaleY();
195         double m01 = at.getShearX();
196         double m10 = at.getShearY();
197
198         control.setLocation(
199                 x * m00 + y * m01,
200                 x * m10 + y * m11
201         );
202         return control;
203     }
204
205     /**
206      * Convert control vector to canvas vector
207      * @param g2d
208      */
209     public Point2D controlVectorToCanvasVector(Point2D control, Point2D canvas)
210     {
211         AffineTransform at = getInverseTransform();
212         if (canvas==null) canvas = new Point2D.Double();
213         if (at==null) {
214             canvas.setLocation(control);
215             return canvas;
216         }
217         double x = control.getX();
218         double y = control.getY();
219
220         double m00 = at.getScaleX();
221         double m11 = at.getScaleY();
222         double m01 = at.getShearX();
223         double m10 = at.getShearY();
224
225         canvas.setLocation(
226                 x * m00 + y * m01,
227                 x * m10 + y * m11
228         );
229         return canvas;
230     }
231     /**
232      * Get a transform that converts
233      * diagram coordinates to control coordinates.
234      * 
235      * @return transform a clone
236      */
237     public AffineTransform getTransform()
238     {
239         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
240         if (at==null) return new AffineTransform();
241         return (AffineTransform) at.clone();
242     }
243
244     /**
245      * Get a transform that converts
246      * diagram vector to control vector.
247      * 
248      * @return transform a clone
249      */
250     public AffineTransform getVectorTransform()
251     {
252         AffineTransform at = getHint(KEY_CANVAS_TRANSFORM);
253         if (at==null) return new AffineTransform();
254         double m00 = at.getScaleX();
255         double m11 = at.getScaleY();
256         double m01 = at.getShearX();
257         double m10 = at.getShearY();
258         return new AffineTransform(m00, m10, m01, m11, 0, 0);
259     }
260
261
262     /**
263      * Transforms control coordinates to diagram coordinates.
264      * 
265      * @return inverted transform or <code>null</code> if inversion failed
266      */
267     public AffineTransform getInverseTransform()
268     {
269         AffineTransform at = getTransform();
270         if (at==null) return null;
271         try {
272             at = at.createInverse();
273             return at;
274         } catch (NoninvertibleTransformException e) {
275             return null;
276         }
277     }
278
279     /**
280      * Tries to invert the specified {@link AffineTransform}.
281      * 
282      * @parameter at the transform to invert
283      * @return inverted transform or <code>null</code> if inversion failed
284      */
285     public AffineTransform getInverseTransform(AffineTransform at)
286     {
287         if (at == null)
288             return null;
289         try {
290             return at.createInverse();
291         } catch (NoninvertibleTransformException e) {
292             return null;
293         }
294     }
295
296     /**
297      * Transforms control vector to diagram vector
298      * @return a clone
299      */
300     public AffineTransform getInverseVectorTransform()
301     {
302         AffineTransform at = getTransform();
303         if (at==null) return null;
304         try {
305             at = at.createInverse();
306             double m00 = at.getScaleX();
307             double m11 = at.getScaleY();
308             double m01 = at.getShearX();
309             double m10 = at.getShearY();
310             return new AffineTransform(m00, m10, m01, m11, 0, 0);
311         } catch (NoninvertibleTransformException e) {
312             return null;
313         }
314     }
315
316
317     public void setTransform(AffineTransform at)
318     {
319         assert(at.getShearX()!=Double.POSITIVE_INFINITY && at.getShearX()!=Double.NEGATIVE_INFINITY);
320         assert(at.getShearY()!=Double.POSITIVE_INFINITY && at.getShearY()!=Double.NEGATIVE_INFINITY);
321         AffineTransform old = getHint(KEY_CANVAS_TRANSFORM);
322         if(at.equals(old) == false) { // Send event only if the transform has really changed
323             setHint(KEY_CANVAS_TRANSFORM, at);
324
325             IG2DNode node = NodeUtil.findNodeById(getContext().getSceneGraph(), SceneGraphConstants.NAVIGATION_NODE_PATH);
326             if (node != null)
327                 // This is not in hintListener, because we don't want to update
328                 // transform if it already came for scenegraph.
329                 node.setTransform(at);
330         }
331     }
332
333     public void zoom(double scaleFactor)
334     {
335         if (scaleFactor==1.0) return;
336         AffineTransform diagramToControl = getTransform();
337         diagramToControl.scale(scaleFactor, scaleFactor);
338         setTransform(diagramToControl);
339     }
340
341     public void rotate(Point2D centerPointCanvas, double angle)
342     {
343         if (angle==0.0) return; // and modulos too
344         AffineTransform diagramToControl = getTransform();
345         diagramToControl.rotate(-angle, centerPointCanvas.getX(), centerPointCanvas.getY());
346         setTransform(diagramToControl);
347     }
348
349     public void restoreOrientation(Point2D centerPointCanvas)
350     {
351         AffineTransform res = new AffineTransform();
352         AffineTransform at = getTransform();
353         double m01              = at.getShearX();
354         double m11              = at.getScaleY();
355         double theta    = Math.atan2(m01, m11);
356         at.rotate(-theta, centerPointCanvas.getX(), centerPointCanvas.getY());
357         res.translate(at.getTranslateX(), at.getTranslateY());
358         res.scale(at.getScaleX(), at.getScaleY());
359         setTransform(res);
360     }
361
362     /**
363      * Get rotate in radians.
364      * @return
365      */
366     public double getRotate() {
367         AffineTransform at = getTransform();
368         double m01 = at.getShearX();
369         double m11 = at.getScaleY();
370         return Math.atan2(m01, m11);
371     }
372
373     /**
374      * Zoom the view port
375      * @param scaleFactor amount to zoom
376      * @param aroundPoint The center point of zoom in diagram coordinates
377      */
378     public void zoomAroundDiagramPoint(double scaleFactor, Point2D aroundPoint)
379     {
380         AffineTransform diagramToControl = getTransform();
381         // convert to control point
382         aroundPoint = diagramToControl.transform(aroundPoint, new Point2D.Double());
383         zoomAroundControlPoint(scaleFactor, aroundPoint);
384     }
385
386     /**
387      * Zoom the view port
388      * @param scaleFactor amount to zoom
389      * @param centerPoint the control point to zoom around
390      */
391     public void zoomAroundControlPoint(double scaleFactor, Point2D centerPoint)
392     {
393         if (scaleFactor==1.0) return;
394         AffineTransform diagramToControl = getTransform();
395         try {
396             Point2D pointBeforeScale = diagramToControl.inverseTransform(centerPoint, new Point2D.Double());
397             diagramToControl.scale(scaleFactor, scaleFactor);
398             Point2D pointAfterScale = diagramToControl.inverseTransform(centerPoint, new Point2D.Double());
399             double _dx = pointAfterScale.getX() - pointBeforeScale.getX();
400             double _dy = pointAfterScale.getY() - pointBeforeScale.getY();
401             diagramToControl.translate(_dx, _dy);
402         } catch (NoninvertibleTransformException e1) {
403         }
404         setTransform(diagramToControl);
405     }
406
407     /**
408      * Transform the viewport. The transform values are in diagram coordinates
409      * @param dx
410      * @param dy
411      */
412     public void translateWithCanvasCoordinates(double dx, double dy)
413     {
414         AffineTransform controlToDiagram = getTransform();
415         controlToDiagram.translate(dx, dy);
416         setTransform(controlToDiagram);
417     }
418
419     /**
420      * Transform the viewport. The transform values are in control coordinates
421      * @param dx
422      * @param dy
423      */
424     public void translateWithControlCoordinates(Point2D offset)
425     {
426         if (offset.getX()==0 && offset.getY()==0) return;
427         AffineTransform at = getInverseTransform();
428         offset = at.transform(offset, new Point2D.Double());
429         translateWithCanvasCoordinates(offset.getX()-at.getTranslateX(), offset.getY()-at.getTranslateY());
430     }
431
432     /**
433      * Get scale ratio between control and canvas
434      * @param pt
435      * @return
436      */
437     public Point2D getScale(Point2D pt)
438     {
439         AffineTransform at = getTransform();
440         return GeometryUtils.getScaleXY(at, pt);
441     }
442
443     /**
444      * Modifies transform to make canvas area fully visible.
445      * @param controlArea
446      * @param diagramArea
447      * @param margins margins
448      */
449     public void fitArea(Rectangle2D controlArea, Rectangle2D diagramArea, Margins margins)
450     {
451         AffineTransform fitTx = org.simantics.scenegraph.utils.GeometryUtils.fitArea(controlArea, diagramArea, margins);
452         //System.out.println("fitArea(" + controlArea + ", " + diagramArea + ", " + margins + "): " + fitTx);
453         setTransform(fitTx);
454     }
455
456 }