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.events.SelectionAdapter;
25 import org.eclipse.swt.events.SelectionEvent;
26 import org.eclipse.swt.graphics.Cursor;
27 import org.eclipse.swt.widgets.Button;
28 import org.eclipse.swt.widgets.Combo;
29 import org.eclipse.swt.widgets.Display;
30 import org.eclipse.swt.widgets.Label;
31 import org.simantics.g3d.math.EulerTools;
32 import org.simantics.g3d.math.EulerTools.Order;
33 import org.simantics.g3d.math.MathTools;
34 import org.simantics.g3d.math.Ray;
35 import org.simantics.g3d.preferences.PreferenceConstants;
36 import org.simantics.g3d.scenegraph.IG3DNode;
37 import org.simantics.g3d.scenegraph.base.INode;
38 import org.simantics.g3d.scenegraph.structural.IStructuralNode;
39 import org.simantics.g3d.toolbar.ToolComposite;
40 import org.simantics.g3d.vtk.Activator;
41 import org.simantics.g3d.vtk.common.VTKNodeMap;
42 import org.simantics.g3d.vtk.gizmo.RotateAxisGizmo;
43 import org.simantics.g3d.vtk.utils.vtkUtil;
44 import org.simantics.utils.threads.ThreadUtils;
48 * FIXME: complete rewrite.
50 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
53 public class RotateAction extends vtkSwtAction{
55 public static final int X = 0;
56 public static final int Y = 1;
57 public static final int Z = 2;
58 public static final int P = 3;
60 private VTKNodeMap<?, ? extends INode> nodeMap;
61 //private TranslateGizmo gizmo = new TranslateGizmo();
62 private RotateAxisGizmo gizmo = new RotateAxisGizmo();
63 private IG3DNode node;
67 private Cursor activeCursor;// = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
68 private Cursor dragCursor;// = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
72 Order order = Order.YXZ;
75 private double angles[];
78 boolean valid = false;
79 private boolean worldCoord = true;
80 //private AxisAngle4d aa = null;
81 private Quat4d parentWorldOrientation = null;
83 //AxisAngle4d rotation = new AxisAngle4d();
84 Quat4d worldOrientation = new Quat4d();
86 protected ToolComposite toolComposite;
87 protected Combo axisCombo;
89 public void setNode(IG3DNode node) {
91 if ((node instanceof IStructuralNode) && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()) {
97 String set = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.ORIENTATION_PRESENTATION);
98 if (set.equals("aa")) {
100 } else if (set.equals("euler")){
102 String eulerOrder = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.EULER_ANGLE_ORDER);
104 order = Order.valueOf(eulerOrder);
105 } catch (Exception e) {
113 public IG3DNode getNode() {
117 public RotateAction(InteractiveVtkComposite panel, VTKNodeMap<?, ? extends INode> nodeMap, ToolComposite toolComposite) {
119 setImageDescriptor(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/arrow_rotate_clockwise.png"));
121 this.nodeMap = nodeMap;
122 this.toolComposite = toolComposite;
125 angles = new double[steps+1];
126 for (int i = 0; i < angles.length; i++) {
127 angles[i] = - Math.PI + (Math.PI * i * 2.0 / steps);
130 activeCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_HAND);
131 dragCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_CROSS);
134 protected void createTools(ToolComposite toolComposite) {
135 Label label = new Label(toolComposite, SWT.READ_ONLY);
136 label.setText("Rotate axis:");
137 axisCombo = new Combo(toolComposite, SWT.READ_ONLY);
141 axisCombo.add("Camera");
142 axisCombo.addSelectionListener(new SelectionAdapter() {
144 public void widgetSelected(SelectionEvent e) {
145 Combo c = (Combo)e.getSource();
146 index = c.getSelectionIndex();
148 panel.getComponent().setFocus();
151 axisCombo.select(index);
153 Button close = new Button(toolComposite, SWT.PUSH);
154 close.setText("Close");
155 close.addSelectionListener(new SelectionAdapter() {
156 public void widgetSelected(SelectionEvent e) {
157 panel.useDefaultAction();
160 toolComposite.relayout();
163 public void attach() {
166 if (toolComposite != null) {
167 createTools(toolComposite);
170 ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() {
181 public void deattach() {
182 if (toolComposite != null) {
183 toolComposite.clear();
187 nodeMap.commit("Rotate");
193 private void attachUI() {
194 panel.getComponent().setCursor(activeCursor);
198 private void deattachUI() {
199 panel.getComponent().setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_ARROW));
204 public boolean keyPressed(KeyEvent e) {
205 if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
206 panel.useDefaultAction();
209 if (e.getKeyCode() == KeyEvent.VK_X) {
215 if (e.getKeyCode() == KeyEvent.VK_Y) {
221 if (e.getKeyCode() == KeyEvent.VK_Z) {
227 if (e.getKeyCode() == KeyEvent.VK_G) {
228 worldCoord = !worldCoord;
236 private void updateLock() {
237 gizmo.setType(index);
238 if (axisCombo != null)
239 axisCombo.select(index);
244 public boolean keyReleased(KeyEvent e) {
251 public boolean mouseClicked(MouseEvent e) {
252 if (e.getClickCount() > 1) {
256 panel.useDefaultAction();
257 //if(!gizmo.isPartOf(actor))
258 // panel.useDefaultAction();
270 public void setWorldCoord(boolean b) {
279 private void update() {
280 Vector3d nodePos = node.getWorldPosition();
281 // System.out.println(nodePos);
282 gizmo.setPosition(nodePos);
284 gizmo.setRotation(new AxisAngle4d());
285 parentWorldOrientation = null;
287 AxisAngle4d aa = new AxisAngle4d();
288 parentWorldOrientation = ((IG3DNode)node.getParent()).getWorldOrientation();
289 aa.set(parentWorldOrientation);
290 gizmo.setRotation(aa);
293 Point3d camPos = new Point3d(panel.getRenderer().GetActiveCamera().GetPosition());
294 Vector3d p = new Vector3d(nodePos);
297 if (parentWorldOrientation != null) {
298 Quat4d qi = new Quat4d(parentWorldOrientation);
300 MathTools.rotate(parentWorldOrientation, p, p);
302 if (panel.getRenderer().GetActiveCamera().GetParallelProjection() == 0) {
303 double distance = p.length();
305 double fov = panel.getRenderer().GetActiveCamera().GetViewAngle();
306 float s = (float) (Math.sin(fov) * distance * 0.1);
308 Vector3d scale = new Vector3d(1., 1., 1.);
317 gizmo.setScale(scale);
320 Vector3d scale = new Vector3d(1.f, 1.f, 1.f);
321 double s = panel.getRenderer().GetActiveCamera().GetParallelScale() / 5.;
329 gizmo.setScale(scale);
335 private boolean isOverNode(MouseEvent e) {
336 vtkProp picked[] = panel.pick(e.getX(), e.getY());
338 for (int i = 0; i < picked.length; i++) {
339 if (node.equals(nodeMap.getNode(picked[i])))
349 public boolean mousePressed(MouseEvent e) {
350 if (e.getButton() == MouseEvent.BUTTON1) {
355 if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) {
360 worldOrientation = node.getWorldOrientation();
361 doChanges(true, e.getX(), e.getY());
363 panel.getComponent().setCursor(dragCursor);
366 getDefaultAction().mousePressed(e);
367 panel.getComponent().setCursor(activeCursor);
370 getDefaultAction().mousePressed(e);
378 public boolean mouseReleased(MouseEvent e) {
379 if (e.getButton() == MouseEvent.BUTTON1) {
381 worldOrientation = null;
382 panel.getComponent().setCursor(activeCursor);
384 getDefaultAction().mouseReleased(e);
390 public boolean mouseDragged(MouseEvent e) {
391 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) > 0 && valid) {
392 if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) {
397 doChanges(false, e.getX(), e.getY());
399 //nodeMap.modified(node);
402 getDefaultAction().mouseDragged(e);
408 Vector3d axis = null;
411 public boolean keyTyped(KeyEvent e) {
412 if (e.getKeyCode() == KeyEvent.VK_LEFT) {
413 inputType = InputType.KEY;
414 axis = new Vector3d(0.0,1.0,0.0);
415 } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
416 inputType = InputType.KEY;
417 axis = new Vector3d(0.0,-1.0,0.0);
418 } else if (e.getKeyCode() ==KeyEvent.VK_UP) {
419 inputType = InputType.KEY;
420 axis = new Vector3d(1.0,0.0,0.0);
421 } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
422 inputType = InputType.KEY;
423 axis = new Vector3d(-1.0,0.0,0.0);
428 public void doChanges(boolean pressed, int x, int y) {
429 Ray ray = vtkUtil.createMouseRay(panel.getRenderer(),x, y);
430 Vector3d p = node.getWorldPosition();
433 Vector3d axis = getRotationAxis();
436 MathTools.rotate(parentWorldOrientation, axis, axis);
440 double s[] = new double[2];
441 Vector3d i2 = new Vector3d();
443 boolean intersect = MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s);
444 double dot = Math.abs(ray.dir.dot(axis));
445 if (intersect && dot > 0.4)
446 inputType = InputType.INTERSECT;
448 inputType = InputType.NONINTERSECT;
451 if (inputType == InputType.INTERSECT) {
452 // picking ray and plane defined by gizmo's center point and
453 // rotation axis can intersect
454 // vector from center point to intersection point
456 // creating vectors i and j that are lying on the plane and
458 // vectors are used to calculate polar coordinate for
459 // intersection point
462 // System.out.println("I,J " + i + " " + j);
463 double angleI = i2.angle(i);
464 double angleJ = i2.angle(j);
465 prevAngle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));
467 // picking ray and plane defined by gizmo's center point and
468 // rotation axis are parallel,
469 // so we'll use cross product of rotation axis and picking
470 // ray to detect amount of rotation
471 i.cross(ray.dir, axis);
472 MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s);
480 if (inputType != InputType.KEY)
481 axis = getRotationAxis();
485 Vector3d taxis = null;
487 taxis = new Vector3d(axis);
488 MathTools.rotate(parentWorldOrientation, axis, axis);
490 // System.out.println(inputType);
491 if (inputType == InputType.INTERSECT) {
493 double s[] = new double[2];
494 Vector3d i2 = new Vector3d();
495 MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s);
497 double angleI = i2.angle(i);
498 double angleJ = i2.angle(j);
499 double angle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));
500 // System.out.println("Angle " + angle + " i " + angleI + " j " + angleJ + " prev " + prevAngle);
505 //setOrientation(MathTools.getQuat(rotation));
506 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
507 Quat4d qrot = new Quat4d();
508 MathTools.getQuat(rot, qrot);
510 qrot.mulInverse(worldOrientation);
513 if (stepMethod == 0) {
515 rot.angle = roundAngle(rot.angle);
517 MathTools.getQuat(rot,qrot);
518 setOrientation(qrot);
519 } else if (stepMethod == 1){
521 //Vector3d euler = MathTools.getEuler(qrot);
522 Vector3d euler = EulerTools.getEulerFromQuat(order, qrot);
523 euler.x = roundAngle(euler.x);
524 euler.y = roundAngle(euler.y);
525 euler.z = roundAngle(euler.z);
526 //Quat4d q = MathTools.getQuat(euler);
527 Quat4d q = EulerTools.getQuatFromEuler(order, euler);
529 // System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q);
531 setOrientation(qrot);
536 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));
537 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
538 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
539 MathTools.multiplyOrientation(aa, rot);
540 setWorldOrientation(MathTools.getQuat(rot));
542 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
543 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
544 MathTools.multiplyOrientation(aa, rot);
545 setOrientation(MathTools.getQuat(rot));
546 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,angle-prevAngle));
551 } else if (inputType == InputType.NONINTERSECT){
553 double s[] = new double[2];
554 MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s);
558 //setOrientation(MathTools.getQuat(rotation));
559 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
561 Quat4d qrot = new Quat4d();
563 MathTools.getQuat(rot, qrot);
565 qrot.mulInverse(worldOrientation);
568 if (stepMethod == 0) {
570 rot.angle = roundAngle(rot.angle);
572 MathTools.getQuat(rot,qrot);
573 setOrientation(qrot);
574 } else if (stepMethod == 1){
576 //Vector3d euler = MathTools.getEuler(qrot);
577 Vector3d euler = EulerTools.getEulerFromQuat(order, qrot);
578 euler.x = roundAngle(euler.x);
579 euler.y = roundAngle(euler.y);
580 euler.z = roundAngle(euler.z);
581 //Quat4d q = MathTools.getQuat(euler);
582 Quat4d q = EulerTools.getQuatFromEuler(order, euler);
584 // System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q);
586 setOrientation(qrot);
590 // G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));
591 // G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));
592 // AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation());
593 // rotations.put(mo, aa);
594 // Vector3d euler = MathTools.getEuler(aa);
595 // euler.x = roundAngle(euler.x);
596 // euler.y = roundAngle(euler.y);
597 // euler.z = roundAngle(euler.z);
598 // aa = MathTools.getFromEuler2(euler);
600 // G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa);
601 // Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()));
602 // e.scale(180.0/Math.PI);
603 // text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " ";
608 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
609 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
610 MathTools.multiplyOrientation(aa, rot);
611 setWorldOrientation(MathTools.getQuat(rot));
612 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));
614 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
615 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
616 MathTools.multiplyOrientation(aa, rot);
617 setOrientation(MathTools.getQuat(rot));
618 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,s[1] - prevS));
620 //text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";
627 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
628 AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5);
629 MathTools.multiplyOrientation(aa, rot);
630 setWorldOrientation(MathTools.getQuat(rot));
631 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));
633 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
634 AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5);
635 MathTools.multiplyOrientation(aa, rot);
636 setOrientation(MathTools.getQuat(rot));
637 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));
639 // text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";
646 protected void setOrientation(Quat4d q) {
647 node.setOrientation(q);
650 protected void setWorldOrientation(Quat4d q) {
651 node.setWorldOrientation(q);
655 public boolean mouseMoved(MouseEvent e) {
656 return getDefaultAction().mouseMoved(e);
659 private Vector3d getRotationAxis() {
662 return new Vector3d(1.0, 0.0, 0.0);
664 return new Vector3d(0.0, 1.0, 0.0);
666 return new Vector3d(0.0, 0.0, 1.0);
668 Vector3d axis = new Vector3d(panel.getRenderer().GetActiveCamera().GetDirectionOfProjection());
676 private double prevS = 0.0;
678 private Vector3d i = new Vector3d();
679 private Vector3d j = new Vector3d();
680 private double prevAngle = 0;
682 enum InputType{INTERSECT,NONINTERSECT,KEY,NONE};
684 private boolean useStep = false;
688 private double roundAngle(double angle) {
689 while (angle < - Math.PI)
690 angle += Math.PI*2.0;
691 while (angle > Math.PI)
692 angle -= Math.PI*2.0;
696 while (angle > angles[index])
701 double d = angle - angles[index - 1];
702 double d2 = angles[index] - angle;
704 angle = angles[index - 1];
706 angle = angles[index];