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
9 * VTT Technical Research Centre of Finland - initial API and implementation
\r
10 *******************************************************************************/
\r
11 package org.simantics.proconf.g3d.actions;
\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
20 import javax.vecmath.AxisAngle4d;
\r
21 import javax.vecmath.AxisAngle4f;
\r
22 import javax.vecmath.Quat4d;
\r
23 import javax.vecmath.Vector3d;
\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
47 public class RotateAction extends WriteInteractiveAction {
\r
49 private JmeRenderingComponent component;
\r
51 private RotateGizmo gizmo;
\r
53 private OrbitalCamera camera;
\r
55 private Map<IGraphicsNode, AxisAngle4d > rotations = new HashMap<IGraphicsNode, AxisAngle4d>();
\r
58 private double angles[];
\r
60 private Action csAction;
\r
61 private boolean worldCoord = true;
\r
62 private IToolBarManager manager;
\r
64 private List<IGraphicsNode> mos;
\r
68 public RotateAction(ThreeDimensionalEditorBase parent) {
\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
75 GraphRequest r = new GraphRequestAdapter() {
\r
77 public GraphRequestStatus perform(Graph g) throws Exception {
\r
78 setWorldCoord(g,!isChecked());
\r
79 return GraphRequestStatus.transactionComplete();
\r
82 RotateAction.this.parent.getSession().asyncRead(r);
\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
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
100 public boolean usable(Graph graph, List<Resource> resources) {
\r
101 if (resources.size() == 0)
\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
117 public void deactivate() {
\r
118 inputType = InputType.NONE;
\r
119 parent.setGizmo(null);
\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
132 private void setWorldCoord(Graph graph,boolean b) {
\r
133 if (worldCoord == b)
\r
136 updateWorldCoord(graph);
\r
139 private void updateWorldCoord(Graph graph) {
\r
141 csAction.setText("World");
\r
142 gizmo.setRotation(new AxisAngle4f());
\r
146 csAction.setText("Local");
\r
147 aa = G3DTools.getOrientation(mos.get(0).getParent().getG3DNode(graph).getWorldOrientation());
\r
149 aa = new AxisAngle4d();
\r
150 gizmo.setRotation(new AxisAngle4f(aa));
\r
154 if (manager != null)
\r
155 manager.update(true);
\r
156 this.parent.setViewChanged(true);
\r
160 public void activate() {
\r
162 Session session = parent.getSession();
\r
164 GraphRequest r = new GraphRequestAdapter() {
\r
166 public GraphRequestStatus perform(Graph g) throws Exception {
\r
167 parent.setGizmo(gizmo);
\r
169 component.getNoShadowRoot().attachChild(gizmo.getNode());
\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
181 parent.setViewChanged(true);
\r
182 inputType = InputType.NONE;
\r
183 return GraphRequestStatus.transactionComplete();
\r
187 session.syncRead(r);
\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
209 private double prevS = 0.0;
\r
211 private Vector3d i = new Vector3d();
\r
212 private Vector3d j = new Vector3d();
\r
213 private double prevAngle = 0;
\r
215 enum InputType{INTERSECT,NONINTERSECT,KEY,NONE};
\r
216 InputType inputType;
\r
217 private boolean useStep = false;
\r
220 public void doChanges(Graph graph) throws Exception {
\r
222 if (input.mousePressed()) {
\r
223 Vector3d axis = getRotationAxis();
\r
224 if (axis != null) {
\r
226 MathTools.rotate(q, axis, axis);
\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
239 if (MathTools.intersectStraightPlane(o, d, p, axis, i2, s) && Math.abs(d.dot(axis)) > 0.2)
\r
240 inputType = InputType.INTERSECT;
\r
242 inputType = InputType.NONINTERSECT;
\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
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
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
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
265 MathTools.intersectStraightStraight(o, d, p, i, new Vector3d(), new Vector3d(), s);
\r
272 if (input.mouseClicked()) {
\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
293 parent.setViewChanged(true);
\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
304 Vector3d taxis = null;
\r
306 taxis = new Vector3d(axis);
\r
307 MathTools.rotate(q, axis, axis);
\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
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
324 if (false && useStep) {
\r
325 for (IGraphicsNode mo : mos) {
\r
326 G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));
\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
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
346 for (IGraphicsNode mo : mos) {
\r
348 G3DTools.multiplyOrientation
\r
349 (mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));
\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
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
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
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
387 for (IGraphicsNode mo : mos) {
\r
389 G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));
\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
399 for (IGraphicsNode mo : mos) {
\r
401 G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));
\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
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
416 //System.out.println(angle + " " + (current+modify));
\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
429 while (angle > angles[index])
\r
434 double d = angle - angles[index - 1];
\r
435 double d2 = angles[index] - angle;
\r
437 angle = angles[index - 1];
\r
439 angle = angles[index];
\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
450 public void setInfoText(String text) {
\r