1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 Association for Decentralized Information Management in
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g3d.vtk.swt;
15 import java.awt.event.KeyEvent;
16 import java.awt.event.MouseEvent;
18 import javax.vecmath.AxisAngle4d;
19 import javax.vecmath.Point3d;
20 import javax.vecmath.Quat4d;
21 import javax.vecmath.Vector3d;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.graphics.Cursor;
25 import org.eclipse.swt.widgets.Display;
26 import org.simantics.g3d.math.EulerTools;
27 import org.simantics.g3d.math.EulerTools.Order;
28 import org.simantics.g3d.math.MathTools;
29 import org.simantics.g3d.math.Ray;
30 import org.simantics.g3d.preferences.PreferenceConstants;
31 import org.simantics.g3d.scenegraph.IG3DNode;
32 import org.simantics.g3d.scenegraph.structural.IStructuralNode;
33 import org.simantics.g3d.vtk.Activator;
34 import org.simantics.g3d.vtk.common.VTKNodeMap;
35 import org.simantics.g3d.vtk.gizmo.RotateAxisGizmo;
36 import org.simantics.g3d.vtk.utils.vtkUtil;
37 import org.simantics.utils.threads.AWTThread;
38 import org.simantics.utils.threads.ThreadUtils;
42 * FIXME: complete rewrite.
44 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
47 public class RotateAction extends vtkSwtAction{
49 public static final int X = 0;
50 public static final int Y = 1;
51 public static final int Z = 2;
52 public static final int P = 3;
54 private VTKNodeMap nodeMap;
55 //private TranslateGizmo gizmo = new TranslateGizmo();
56 private RotateAxisGizmo gizmo = new RotateAxisGizmo();
57 private IG3DNode node;
61 private Cursor activeCursor;// = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
62 private Cursor dragCursor;// = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
66 Order order = Order.YXZ;
69 private double angles[];
72 boolean valid = false;
73 private boolean worldCoord = true;
74 //private AxisAngle4d aa = null;
75 private Quat4d parentWorldOrientation = null;
77 //AxisAngle4d rotation = new AxisAngle4d();
78 Quat4d worldOrientation = new Quat4d();
80 public void setNode(IG3DNode node) {
82 if ((node instanceof IStructuralNode) && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()) {
88 String set = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.ORIENTATION_PRESENTATION);
89 if (set.equals("aa")) {
91 } else if (set.equals("euler")){
93 String eulerOrder = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.EULER_ANGLE_ORDER);
95 order = Order.valueOf(eulerOrder);
96 } catch (Exception e) {
104 public IG3DNode getNode() {
108 public RotateAction(InteractiveVtkComposite panel, VTKNodeMap nodeMap) {
110 setImageDescriptor(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/arrow_rotate_clockwise.png"));
112 this.nodeMap = nodeMap;
116 angles = new double[steps+1];
117 for (int i = 0; i < angles.length; i++) {
118 angles[i] = - Math.PI + (Math.PI * i * 2.0 / steps);
121 activeCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_HAND);
122 dragCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_CROSS);
125 public void attach() {
130 ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() {
141 public void deattach() {
150 private void attachUI() {
151 panel.getComponent().setCursor(activeCursor);
155 private void deattachUI() {
156 panel.getComponent().setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_ARROW));
161 public boolean keyPressed(KeyEvent e) {
162 if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
163 panel.useDefaultAction();
166 if (e.getKeyCode() == KeyEvent.VK_X) {
172 if (e.getKeyCode() == KeyEvent.VK_Y) {
178 if (e.getKeyCode() == KeyEvent.VK_Z) {
184 if (e.getKeyCode() == KeyEvent.VK_G) {
185 worldCoord = !worldCoord;
187 gizmo.setType(index);
193 public boolean keyReleased(KeyEvent e) {
200 public boolean mouseClicked(MouseEvent e) {
201 if (e.getClickCount() > 1) {
205 panel.useDefaultAction();
206 //if(!gizmo.isPartOf(actor))
207 // panel.useDefaultAction();
219 public void setWorldCoord(boolean b) {
228 private void update() {
229 Vector3d nodePos = node.getWorldPosition();
230 System.out.println(nodePos);
231 gizmo.setPosition(nodePos);
233 gizmo.setRotation(new AxisAngle4d());
234 parentWorldOrientation = null;
236 AxisAngle4d aa = new AxisAngle4d();
237 parentWorldOrientation = ((IG3DNode)node.getParent()).getWorldOrientation();
238 aa.set(parentWorldOrientation);
239 gizmo.setRotation(aa);
242 Point3d camPos = new Point3d(panel.getRenderer().GetActiveCamera().GetPosition());
243 Vector3d p = new Vector3d(nodePos);
246 if (parentWorldOrientation != null) {
247 Quat4d qi = new Quat4d(parentWorldOrientation);
249 MathTools.rotate(parentWorldOrientation, p, p);
251 if (panel.getRenderer().GetActiveCamera().GetParallelProjection() == 0) {
252 double distance = p.length();
254 double fov = panel.getRenderer().GetActiveCamera().GetViewAngle();
255 float s = (float) (Math.sin(fov) * distance * 0.1);
257 Vector3d scale = new Vector3d(1., 1., 1.);
266 gizmo.setScale(scale);
269 Vector3d scale = new Vector3d(1.f, 1.f, 1.f);
270 double s = panel.getRenderer().GetActiveCamera().GetParallelScale() / 5.;
278 gizmo.setScale(scale);
284 private boolean isOverNode(MouseEvent e) {
285 vtkProp picked[] = panel.pick(e.getX(), e.getY());
287 for (int i = 0; i < picked.length; i++) {
288 if (node.equals(nodeMap.getNode(picked[i])))
298 public boolean mousePressed(MouseEvent e) {
299 if (e.getButton() == MouseEvent.BUTTON1) {
304 if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) {
309 worldOrientation = node.getWorldOrientation();
310 doChanges(true, e.getX(), e.getY());
312 panel.getComponent().setCursor(dragCursor);
315 getDefaultAction().mousePressed(e);
316 panel.getComponent().setCursor(activeCursor);
319 getDefaultAction().mousePressed(e);
327 public boolean mouseReleased(MouseEvent e) {
328 if (e.getButton() == MouseEvent.BUTTON1) {
330 worldOrientation = null;
331 panel.getComponent().setCursor(activeCursor);
333 getDefaultAction().mouseReleased(e);
339 public boolean mouseDragged(MouseEvent e) {
340 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) > 0 && valid) {
341 if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) {
346 doChanges(false, e.getX(), e.getY());
348 //nodeMap.modified(node);
351 getDefaultAction().mouseDragged(e);
357 Vector3d axis = null;
360 public boolean keyTyped(KeyEvent e) {
361 if (e.getKeyCode() == KeyEvent.VK_LEFT) {
362 inputType = InputType.KEY;
363 axis = new Vector3d(0.0,1.0,0.0);
364 } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
365 inputType = InputType.KEY;
366 axis = new Vector3d(0.0,-1.0,0.0);
367 } else if (e.getKeyCode() ==KeyEvent.VK_UP) {
368 inputType = InputType.KEY;
369 axis = new Vector3d(1.0,0.0,0.0);
370 } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
371 inputType = InputType.KEY;
372 axis = new Vector3d(-1.0,0.0,0.0);
377 public void doChanges(boolean pressed, int x, int y) {
378 Ray ray = vtkUtil.createMouseRay(panel.getRenderer(),x, y);
379 Vector3d p = node.getWorldPosition();
382 Vector3d axis = getRotationAxis();
385 MathTools.rotate(parentWorldOrientation, axis, axis);
389 double s[] = new double[2];
390 Vector3d i2 = new Vector3d();
392 boolean intersect = MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s);
393 double dot = Math.abs(ray.dir.dot(axis));
394 if (intersect && dot > 0.4)
395 inputType = InputType.INTERSECT;
397 inputType = InputType.NONINTERSECT;
400 if (inputType == InputType.INTERSECT) {
401 // picking ray and plane defined by gizmo's center point and
402 // rotation axis can intersect
403 // vector from center point to intersection point
405 // creating vectors i and j that are lying on the plane and
407 // vectors are used to calculate polar coordinate for
408 // intersection point
411 System.out.println("I,J " + i + " " + j);
412 double angleI = i2.angle(i);
413 double angleJ = i2.angle(j);
414 prevAngle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));
416 // picking ray and plane defined by gizmo's center point and
417 // rotation axis are parallel,
418 // so we'll use cross product of rotation axis and picking
419 // ray to detect amount of rotation
420 i.cross(ray.dir, axis);
421 MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s);
429 if (inputType != InputType.KEY)
430 axis = getRotationAxis();
434 Vector3d taxis = null;
436 taxis = new Vector3d(axis);
437 MathTools.rotate(parentWorldOrientation, axis, axis);
439 System.out.println(inputType);
440 if (inputType == InputType.INTERSECT) {
442 double s[] = new double[2];
443 Vector3d i2 = new Vector3d();
444 MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s);
446 double angleI = i2.angle(i);
447 double angleJ = i2.angle(j);
448 double angle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));
449 System.out.println("Angle " + angle + " i " + angleI + " j " + angleJ + " prev " + prevAngle);
454 //setOrientation(MathTools.getQuat(rotation));
455 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
456 Quat4d qrot = new Quat4d();
457 MathTools.getQuat(rot, qrot);
459 qrot.mulInverse(worldOrientation);
462 if (stepMethod == 0) {
464 rot.angle = roundAngle(rot.angle);
466 MathTools.getQuat(rot,qrot);
467 setOrientation(qrot);
468 } else if (stepMethod == 1){
470 //Vector3d euler = MathTools.getEuler(qrot);
471 Vector3d euler = EulerTools.getEulerFromQuat(order, qrot);
472 euler.x = roundAngle(euler.x);
473 euler.y = roundAngle(euler.y);
474 euler.z = roundAngle(euler.z);
475 //Quat4d q = MathTools.getQuat(euler);
476 Quat4d q = EulerTools.getQuatFromEuler(order, euler);
478 System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q);
480 setOrientation(qrot);
485 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));
486 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
487 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
488 MathTools.multiplyOrientation(aa, rot);
489 setWorldOrientation(MathTools.getQuat(rot));
491 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
492 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
493 MathTools.multiplyOrientation(aa, rot);
494 setOrientation(MathTools.getQuat(rot));
495 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,angle-prevAngle));
500 } else if (inputType == InputType.NONINTERSECT){
502 double s[] = new double[2];
503 MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s);
507 //setOrientation(MathTools.getQuat(rotation));
508 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
510 Quat4d qrot = new Quat4d();
512 MathTools.getQuat(rot, qrot);
514 qrot.mulInverse(worldOrientation);
517 if (stepMethod == 0) {
519 rot.angle = roundAngle(rot.angle);
521 MathTools.getQuat(rot,qrot);
522 setOrientation(qrot);
523 } else if (stepMethod == 1){
525 //Vector3d euler = MathTools.getEuler(qrot);
526 Vector3d euler = EulerTools.getEulerFromQuat(order, qrot);
527 euler.x = roundAngle(euler.x);
528 euler.y = roundAngle(euler.y);
529 euler.z = roundAngle(euler.z);
530 //Quat4d q = MathTools.getQuat(euler);
531 Quat4d q = EulerTools.getQuatFromEuler(order, euler);
533 System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q);
535 setOrientation(qrot);
539 // G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));
540 // G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));
541 // AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation());
542 // rotations.put(mo, aa);
543 // Vector3d euler = MathTools.getEuler(aa);
544 // euler.x = roundAngle(euler.x);
545 // euler.y = roundAngle(euler.y);
546 // euler.z = roundAngle(euler.z);
547 // aa = MathTools.getFromEuler2(euler);
549 // G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa);
550 // Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()));
551 // e.scale(180.0/Math.PI);
552 // text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " ";
557 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
558 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
559 MathTools.multiplyOrientation(aa, rot);
560 setWorldOrientation(MathTools.getQuat(rot));
561 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));
563 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
564 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
565 MathTools.multiplyOrientation(aa, rot);
566 setOrientation(MathTools.getQuat(rot));
567 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,s[1] - prevS));
569 //text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";
576 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
577 AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5);
578 MathTools.multiplyOrientation(aa, rot);
579 setWorldOrientation(MathTools.getQuat(rot));
580 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));
582 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
583 AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5);
584 MathTools.multiplyOrientation(aa, rot);
585 setOrientation(MathTools.getQuat(rot));
586 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));
588 // text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";
595 protected void setOrientation(Quat4d q) {
596 node.setOrientation(q);
599 protected void setWorldOrientation(Quat4d q) {
600 node.setWorldOrientation(q);
604 public boolean mouseMoved(MouseEvent e) {
605 return getDefaultAction().mouseMoved(e);
608 private Vector3d getRotationAxis() {
611 return new Vector3d(1.0, 0.0, 0.0);
613 return new Vector3d(0.0, 1.0, 0.0);
615 return new Vector3d(0.0, 0.0, 1.0);
617 Vector3d axis = new Vector3d(panel.getRenderer().GetActiveCamera()
618 .GetDirectionOfProjection());
626 private double prevS = 0.0;
628 private Vector3d i = new Vector3d();
629 private Vector3d j = new Vector3d();
630 private double prevAngle = 0;
632 enum InputType{INTERSECT,NONINTERSECT,KEY,NONE};
634 private boolean useStep = false;
638 private double roundAngle(double angle) {
639 while (angle < - Math.PI)
640 angle += Math.PI*2.0;
641 while (angle > Math.PI)
642 angle -= Math.PI*2.0;
646 while (angle > angles[index])
651 double d = angle - angles[index - 1];
652 double d2 = angles[index] - angle;
654 angle = angles[index - 1];
656 angle = angles[index];