]> gerrit.simantics Code Review - simantics/3d.git/blob - dev/org.simantics.proconf.g3d/src/org/simantics/proconf/g3d/actions/RotateAction.java
Release
[simantics/3d.git] / dev / org.simantics.proconf.g3d / src / org / simantics / proconf / g3d / actions / RotateAction.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.\r
3  * All rights reserved. This program and the accompanying materials\r
4  * are made available under the terms of the Eclipse Public License v1.0\r
5  * which accompanies this distribution, and is available at\r
6  * http://www.eclipse.org/legal/epl-v10.html\r
7  *\r
8  * Contributors:\r
9  *     VTT Technical Research Centre of Finland - initial API and implementation\r
10  *******************************************************************************/\r
11 package org.simantics.proconf.g3d.actions;\r
12 \r
13 import java.awt.event.KeyEvent;\r
14 import java.awt.event.MouseEvent;\r
15 import java.util.Collection;\r
16 import java.util.HashMap;\r
17 import java.util.List;\r
18 import java.util.Map;\r
19 \r
20 import javax.vecmath.AxisAngle4d;\r
21 import javax.vecmath.AxisAngle4f;\r
22 import javax.vecmath.Quat4d;\r
23 import javax.vecmath.Vector3d;\r
24 \r
25 import org.eclipse.jface.action.Action;\r
26 import org.eclipse.jface.action.IToolBarManager;\r
27 import org.simantics.db.Graph;\r
28 import org.simantics.db.GraphRequest;\r
29 import org.simantics.db.GraphRequestAdapter;\r
30 import org.simantics.db.GraphRequestStatus;\r
31 import org.simantics.db.Session;\r
32 import org.simantics.db.Resource;\r
33 import org.simantics.layer0.utils.IEntity;\r
34 import org.simantics.layer0.utils.EntityFactory;\r
35 import org.simantics.proconf.g3d.Activator;\r
36 import org.simantics.proconf.g3d.Resources;\r
37 import org.simantics.proconf.g3d.base.G3DTools;\r
38 import org.simantics.proconf.g3d.base.JmeRenderingComponent;\r
39 import org.simantics.proconf.g3d.base.MathTools;\r
40 import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase;\r
41 import org.simantics.proconf.g3d.base.VecmathJmeTools;\r
42 import org.simantics.proconf.g3d.common.OrbitalCamera;\r
43 import org.simantics.proconf.g3d.gizmo.RotateGizmo;\r
44 import org.simantics.proconf.g3d.scenegraph.IGraphicsNode;\r
45 import org.simantics.proconf.g3d.stubs.G3DNode;\r
46 \r
47 public class RotateAction extends WriteInteractiveAction {\r
48         \r
49         private JmeRenderingComponent component;\r
50         \r
51     private RotateGizmo gizmo;\r
52 \r
53     private OrbitalCamera camera;\r
54     \r
55     private Map<IGraphicsNode, AxisAngle4d > rotations = new HashMap<IGraphicsNode, AxisAngle4d>();\r
56     \r
57     private int steps; \r
58     private double angles[];\r
59     \r
60     private Action csAction;\r
61     private boolean worldCoord = true;\r
62     private IToolBarManager manager;\r
63     \r
64     private List<IGraphicsNode> mos;\r
65     AxisAngle4d aa;\r
66     Quat4d q;\r
67     \r
68     public RotateAction(ThreeDimensionalEditorBase parent) {\r
69         super(parent,true);\r
70         component = parent.getRenderingComponent();\r
71         camera = parent.getCamera();\r
72         gizmo = new RotateGizmo(component.getDisplaySystem().getRenderer());\r
73         csAction = new Action("World",Action.AS_CHECK_BOX) {\r
74                 public void run() {\r
75                         GraphRequest r = new GraphRequestAdapter() {\r
76                                 @Override\r
77                                 public GraphRequestStatus perform(Graph g) throws Exception {\r
78                                         setWorldCoord(g,!isChecked());\r
79                                         return GraphRequestStatus.transactionComplete();\r
80                                 }\r
81                         };\r
82                         RotateAction.this.parent.getSession().asyncRead(r);\r
83                         \r
84                 }\r
85         };\r
86     }\r
87     \r
88     public void init() {\r
89         this.setText("Rotate");\r
90         this.setToolTipText("Rotate the object");\r
91         this.setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/rotate.png"));\r
92         steps = 36;\r
93         angles = new double[steps+1];\r
94         for (int i = 0; i < angles.length; i++) {\r
95             angles[i] = - Math.PI + (Math.PI * i * 2.0 / steps);\r
96         }\r
97     }\r
98     \r
99     @Override\r
100     public boolean usable(Graph graph, List<Resource> resources) {\r
101         if (resources.size() == 0)\r
102             return false;\r
103         for (Resource r : resources) {\r
104                 IEntity t = EntityFactory.create(graph,r);\r
105             if (t.isInstanceOf(Resources.g3dResource.G3DNode)) {\r
106                 Collection<IEntity> p = t.getRelatedObjects(Resources.g3dResource.HasLocalOrientation);\r
107                 if (p == null || p.size() != 1)\r
108                     return false;\r
109             }\r
110                \r
111         }\r
112         return true;\r
113         \r
114     }\r
115     \r
116     @Override\r
117     public void deactivate() {\r
118         inputType = InputType.NONE;\r
119         parent.setGizmo(null);\r
120         mos = null;\r
121 \r
122     }\r
123     \r
124     public void fillToolBar(IToolBarManager manager) {\r
125         super.fillToolBar(manager);\r
126         this.manager = manager;\r
127         csAction.setChecked(!worldCoord);\r
128         manager.add(csAction);\r
129         \r
130     }\r
131     \r
132     private void setWorldCoord(Graph graph,boolean b) {\r
133         if (worldCoord == b)\r
134                 return;\r
135         worldCoord = b;\r
136         updateWorldCoord(graph);\r
137     }\r
138     \r
139     private void updateWorldCoord(Graph graph) {\r
140         if (worldCoord) {\r
141                         csAction.setText("World");\r
142                         gizmo.setRotation(new AxisAngle4f());\r
143                         aa = null;\r
144                         q = null;\r
145         } else {\r
146                         csAction.setText("Local");\r
147                         aa = G3DTools.getOrientation(mos.get(0).getParent().getG3DNode(graph).getWorldOrientation());\r
148                         if (aa == null)\r
149                                 aa = new AxisAngle4d();\r
150                         gizmo.setRotation(new AxisAngle4f(aa));\r
151                         q = new Quat4d();\r
152                 q.set(aa);\r
153         }\r
154         if (manager != null)\r
155                 manager.update(true);\r
156         this.parent.setViewChanged(true);\r
157     }\r
158     \r
159     @Override\r
160     public void activate() {\r
161         \r
162         Session session = parent.getSession();\r
163         \r
164         GraphRequest r = new GraphRequestAdapter() {\r
165                 @Override\r
166                 public GraphRequestStatus perform(Graph g) throws Exception {\r
167                         parent.setGizmo(gizmo);\r
168                 \r
169                 component.getNoShadowRoot().attachChild(gizmo.getNode());\r
170                 updateGizmo();\r
171 \r
172                 String text = "";\r
173                 mos = parent.getSelectionAdapter().getSelectedObjects();\r
174                 rotations = new HashMap<IGraphicsNode, AxisAngle4d>();\r
175                 for (IGraphicsNode mo : mos) {\r
176                         G3DNode n = mo.getG3DNode(g);\r
177                     rotations.put(mo,G3DTools.getOrientation(n.getLocalOrientation()));\r
178                     text += G3DTools.getOrientation(n.getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(g).getLocalOrientation())) + " ";\r
179                 }\r
180                 setInfoText(text);\r
181                 parent.setViewChanged(true);\r
182                 inputType = InputType.NONE;\r
183                 return GraphRequestStatus.transactionComplete();\r
184                 }\r
185         };\r
186         \r
187         session.syncRead(r);\r
188         \r
189         \r
190     }\r
191     \r
192     \r
193     private Vector3d getRotationAxis() {\r
194         switch (gizmo.getSelected()) {\r
195         case RotateGizmo.X:\r
196             return new Vector3d(1.0,0.0,0.0);\r
197         case RotateGizmo.Y:\r
198             return new Vector3d(0.0,1.0,0.0);\r
199         case RotateGizmo.Z:\r
200             return new Vector3d(0.0,0.0,1.0);\r
201         case RotateGizmo.XYZ:\r
202             Vector3d axis = camera.getUnNormalizedHeading();\r
203             axis.normalize();\r
204             return axis;\r
205         default:\r
206             return null;\r
207         }\r
208     }\r
209     private double prevS = 0.0;\r
210     \r
211     private Vector3d i = new Vector3d();\r
212     private Vector3d j = new Vector3d();\r
213     private double prevAngle = 0;\r
214 \r
215     enum InputType{INTERSECT,NONINTERSECT,KEY,NONE};\r
216     InputType inputType;\r
217     private boolean useStep = false;\r
218     \r
219     @Override\r
220     public void doChanges(Graph graph) throws Exception {\r
221         \r
222         if (input.mousePressed()) {\r
223             Vector3d axis = getRotationAxis();\r
224             if (axis != null) {\r
225                 if (!worldCoord) {\r
226                         MathTools.rotate(q, axis, axis);\r
227                 }\r
228                 Vector3d o = new Vector3d();\r
229                 Vector3d d = new Vector3d();\r
230                 parent.createPickRay(o, d);\r
231                 Vector3d p = gizmo.getPosition();\r
232                 double s[] = new double[2];\r
233                 Vector3d i2 = new Vector3d();\r
234                 if ((input.pressModifiers() & MouseEvent.CTRL_MASK) > 0) {\r
235                     useStep = true;\r
236                 } else {\r
237                     useStep = false;\r
238                 }\r
239                 if (MathTools.intersectStraightPlane(o, d, p, axis, i2, s) && Math.abs(d.dot(axis)) > 0.2)\r
240                         inputType = InputType.INTERSECT;\r
241                 else\r
242                         inputType = InputType.NONINTERSECT;\r
243                         \r
244                 \r
245                 if (inputType == InputType.INTERSECT) {\r
246                     // picking ray and plane defined by gizmo's center point and\r
247                     // rotation axis can intersect\r
248                     // vector from center point to intersection point\r
249                     i2.sub(p);\r
250                     // creating vectors i and j that are lying on the plane and\r
251                     // are perpendicular\r
252                     // vectors are used to calculate polar coordinate for\r
253                     // intersection point\r
254                     j.set(i2);\r
255                     i.cross(j, axis);\r
256                     double angleI = i2.angle(i);\r
257                     double angleJ = i2.angle(j);\r
258                     prevAngle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));\r
259                 } else {\r
260                     // picking ray and plane defined by gizmo's center point and\r
261                     // rotation axis are parallel,\r
262                     // so we'll use cross product of rotation axis and picking\r
263                     // ray to detect amount of rotation\r
264                     i.cross(d, axis);\r
265                     MathTools.intersectStraightStraight(o, d, p, i, new Vector3d(), new Vector3d(), s);\r
266                     prevS = s[1];\r
267                 }\r
268             }\r
269            \r
270             \r
271         }\r
272         if (input.mouseClicked()) {\r
273             end();\r
274             return;\r
275         }\r
276         Vector3d axis = null;\r
277         if (input.keyPressed(KeyEvent.VK_LEFT)) {\r
278                 inputType = InputType.KEY;\r
279                 axis = new Vector3d(0.0,1.0,0.0);\r
280         } else if (input.keyPressed(KeyEvent.VK_RIGHT)) {\r
281                 inputType = InputType.KEY;\r
282                 axis = new Vector3d(0.0,-1.0,0.0);\r
283         } else if (input.keyPressed(KeyEvent.VK_UP)) {\r
284                 inputType = InputType.KEY;\r
285                 axis = new Vector3d(1.0,0.0,0.0);\r
286         } else if (input.keyPressed(KeyEvent.VK_DOWN)) {\r
287                 inputType = InputType.KEY;\r
288                 axis = new Vector3d(-1.0,0.0,0.0);\r
289         } else if (!input.mouseDragged()) {\r
290             parent.getDefaultAction().update();\r
291             return;\r
292         }\r
293         parent.setViewChanged(true);\r
294         \r
295         \r
296         updateGizmo();\r
297         List<IGraphicsNode> mos = parent.getSelectionAdapter().getSelectedObjects();\r
298         if (inputType != InputType.KEY)\r
299                 axis = getRotationAxis();\r
300         if (axis == null) {\r
301             parent.getDefaultAction().update();\r
302             return;   \r
303         }\r
304         Vector3d taxis = null;\r
305         if (!worldCoord) {\r
306                 taxis = new Vector3d(axis);\r
307                 MathTools.rotate(q, axis, axis);\r
308         }\r
309         String text = "";\r
310         if (inputType == InputType.INTERSECT) {\r
311             Vector3d o = new Vector3d();\r
312             Vector3d d = new Vector3d();\r
313             parent.createPickRay(o, d);\r
314             Vector3d p = gizmo.getPosition();\r
315             double s[] = new double[2];\r
316             Vector3d i2 = new Vector3d();\r
317             MathTools.intersectStraightPlane(o, d, p, axis, i2, s);\r
318             i2.sub(p);\r
319             double angleI = i2.angle(i);\r
320             double angleJ = i2.angle(j);\r
321             double angle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));\r
322             if(!worldCoord)\r
323                 axis = taxis;\r
324             if (false && useStep) {\r
325                 for (IGraphicsNode mo : mos) {\r
326                     G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));\r
327                     // FIXME : commit\r
328                     G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));\r
329                     //mo.setRotation(rotations.get(mo));\r
330                     //mo.modifyWorldRotation(axis, angle - prevAngle);\r
331                     AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation());\r
332                     rotations.put(mo, aa);\r
333                     prevAngle = angle;\r
334                     Vector3d euler = MathTools.getEuler(aa);\r
335                     euler.x = roundAngle(euler.x);\r
336                     euler.y = roundAngle(euler.y);\r
337                     euler.z = roundAngle(euler.z);\r
338                     aa = MathTools.getFromEuler2(euler);\r
339                     G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa);\r
340                     //mo.setRotation(aa);\r
341                     Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()));\r
342                     e.scale(180.0/Math.PI);\r
343                     text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " ";\r
344                 }\r
345             } else  {\r
346                 for (IGraphicsNode mo : mos) {\r
347                     if (worldCoord)\r
348                                 G3DTools.multiplyOrientation\r
349                                 (mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));\r
350                         else\r
351                                 G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,angle-prevAngle));\r
352                         text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";\r
353                 }\r
354                 prevAngle = angle;\r
355             }\r
356             \r
357         } else if (inputType == InputType.NONINTERSECT){\r
358             Vector3d o = new Vector3d();\r
359             Vector3d d = new Vector3d();\r
360             parent.createPickRay(o, d);\r
361             Vector3d p = gizmo.getPosition();\r
362             double s[] = new double[2];\r
363             MathTools.intersectStraightStraight(o, d, p, i, new Vector3d(), new Vector3d(), s);\r
364             if(!worldCoord)\r
365                 axis = taxis;\r
366             if (false && useStep) {\r
367                 for (IGraphicsNode mo : mos) {\r
368                     G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));\r
369                     G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));\r
370                     //mo.setRotation(rotations.get(mo));\r
371                     //mo.modifyWorldRotation(axis, s[1] - prevS);\r
372                     AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation());\r
373                     rotations.put(mo, aa);\r
374                     Vector3d euler = MathTools.getEuler(aa);\r
375                     euler.x = roundAngle(euler.x);\r
376                     euler.y = roundAngle(euler.y);\r
377                     euler.z = roundAngle(euler.z);\r
378                     aa = MathTools.getFromEuler2(euler);\r
379                     prevS = s[1];\r
380                     G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa);\r
381                     //mo.setRotation(aa);\r
382                     Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()));\r
383                     e.scale(180.0/Math.PI);\r
384                     text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " ";\r
385                 }\r
386             } else {\r
387                 for (IGraphicsNode mo : mos) {\r
388                         if (worldCoord)\r
389                                 G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));\r
390                         else\r
391                                 G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,s[1] - prevS));\r
392                     text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";\r
393                 }\r
394                 prevS = s[1];\r
395                 \r
396             }\r
397            \r
398         } else {\r
399                 for (IGraphicsNode mo : mos) {\r
400                 if (worldCoord)\r
401                         G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));\r
402                 else\r
403                         G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));\r
404                 text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";\r
405             }\r
406         }\r
407         setInfoText(text);\r
408  \r
409     }\r
410     /*\r
411     private double roundAngle(double current, double modify) {\r
412         double angle = roundAngle(current+modify);\r
413         if (Double.isNaN(angle)) {\r
414             angle = current+modify;\r
415         }\r
416         //System.out.println(angle + " " + (current+modify));\r
417         return angle;\r
418     }\r
419     */\r
420     \r
421     private double roundAngle(double angle) {\r
422         while (angle < - Math.PI)\r
423             angle += Math.PI*2.0;\r
424         while (angle > Math.PI)\r
425             angle -= Math.PI*2.0;\r
426         \r
427         \r
428         int index = 0;\r
429         while (angle > angles[index])\r
430             index++;\r
431         if (index == 0) {\r
432             angle = angles[0];\r
433         } else {\r
434             double d = angle - angles[index - 1];\r
435             double d2 = angles[index] - angle;\r
436             if (d < d2)\r
437                 angle = angles[index - 1];\r
438             else\r
439                 angle = angles[index];\r
440         }\r
441         return angle;\r
442     }\r
443     \r
444     private void updateGizmo() {\r
445         List<IGraphicsNode> mos = parent.getSelectionAdapter().getSelectedObjects();\r
446         //gizmo.update(XithTools.getPosition(mos.get(0).getGroup()),camera.getCameraPos(),component);\r
447         gizmo.update(VecmathJmeTools.getD(mos.get(0).getGroup().getWorldTranslation()),camera.getCameraPos(),component);\r
448     }\r
449     \r
450     public void setInfoText(String text) {\r
451         \r
452     }\r
453 }