1 /*******************************************************************************
\r
2 * Copyright (c) 2012, 2013 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g3d.vtk.action;
\r
14 import java.awt.Cursor;
\r
15 import java.awt.event.KeyEvent;
\r
16 import java.awt.event.MouseEvent;
\r
18 import javax.vecmath.AxisAngle4d;
\r
19 import javax.vecmath.Point3d;
\r
20 import javax.vecmath.Quat4d;
\r
21 import javax.vecmath.Vector3d;
\r
23 import org.simantics.g3d.math.EulerTools;
\r
24 import org.simantics.g3d.math.MathTools;
\r
25 import org.simantics.g3d.math.Ray;
\r
26 import org.simantics.g3d.math.EulerTools.Order;
\r
27 import org.simantics.g3d.preferences.PreferenceConstants;
\r
28 import org.simantics.g3d.scenegraph.IG3DNode;
\r
29 import org.simantics.g3d.scenegraph.structural.IStructuralNode;
\r
30 import org.simantics.g3d.vtk.Activator;
\r
31 import org.simantics.g3d.vtk.common.InteractiveVtkPanel;
\r
32 import org.simantics.g3d.vtk.common.VTKNodeMap;
\r
33 import org.simantics.g3d.vtk.gizmo.RotateAxisGizmo;
\r
34 import org.simantics.g3d.vtk.utils.vtkUtil;
\r
35 import org.simantics.utils.threads.AWTThread;
\r
36 import org.simantics.utils.threads.ThreadUtils;
\r
40 * FIXME: complete rewrite.
\r
42 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
\r
45 public class RotateAction extends vtkAction{
\r
47 public static final int X = 0;
\r
48 public static final int Y = 1;
\r
49 public static final int Z = 2;
\r
50 public static final int P = 3;
\r
52 private VTKNodeMap nodeMap;
\r
53 //private TranslateGizmo gizmo = new TranslateGizmo();
\r
54 private RotateAxisGizmo gizmo = new RotateAxisGizmo();
\r
55 private IG3DNode node;
\r
59 private Cursor activeCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
\r
60 private Cursor dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
\r
64 Order order = Order.YXZ;
\r
67 private double angles[];
\r
70 boolean valid = false;
\r
71 private boolean worldCoord = true;
\r
72 //private AxisAngle4d aa = null;
\r
73 private Quat4d parentWorldOrientation = null;
\r
75 //AxisAngle4d rotation = new AxisAngle4d();
\r
76 Quat4d worldOrientation = new Quat4d();
\r
78 public void setNode(IG3DNode node) {
\r
80 if ((node instanceof IStructuralNode) && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()) {
\r
86 String set = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.ORIENTATION_PRESENTATION);
\r
87 if (set.equals("aa")) {
\r
89 } else if (set.equals("euler")){
\r
91 String eulerOrder = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.EULER_ANGLE_ORDER);
\r
93 order = Order.valueOf(eulerOrder);
\r
94 } catch (Exception e) {
\r
102 public IG3DNode getNode() {
\r
106 public RotateAction(InteractiveVtkPanel panel, VTKNodeMap nodeMap) {
\r
108 setImageDescriptor(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/arrow_rotate_clockwise.png"));
\r
110 this.nodeMap = nodeMap;
\r
114 angles = new double[steps+1];
\r
115 for (int i = 0; i < angles.length; i++) {
\r
116 angles[i] = - Math.PI + (Math.PI * i * 2.0 / steps);
\r
120 public void attach() {
\r
125 ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
\r
126 public void run() {
\r
136 public void deattach() {
\r
145 private void attachUI() {
\r
146 panel.setCursor(activeCursor);
\r
147 gizmo.attach(panel.GetRenderer());
\r
150 private void deattachUI() {
\r
151 panel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
\r
156 public void keyPressed(KeyEvent e) {
\r
157 if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
\r
158 panel.useDefaultAction();
\r
161 if (e.getKeyCode() == KeyEvent.VK_X) {
\r
167 if (e.getKeyCode() == KeyEvent.VK_Y) {
\r
173 if (e.getKeyCode() == KeyEvent.VK_Z) {
\r
179 if (e.getKeyCode() == KeyEvent.VK_G) {
\r
180 worldCoord = !worldCoord;
\r
182 gizmo.setType(index);
\r
187 public void keyReleased(KeyEvent e) {
\r
194 public void mouseClicked(MouseEvent e) {
\r
195 if (e.getClickCount() > 1) {
\r
196 if (isOverNode(e)) {
\r
199 panel.useDefaultAction();
\r
200 //if(!gizmo.isPartOf(actor))
\r
201 // panel.useDefaultAction();
\r
207 public void mouseEntered(MouseEvent e) {
\r
212 public void mouseExited(MouseEvent e) {
\r
220 public void setWorldCoord(boolean b) {
\r
221 if (worldCoord == b)
\r
229 private void update() {
\r
230 Vector3d nodePos = node.getWorldPosition();
\r
231 System.out.println(nodePos);
\r
232 gizmo.setPosition(nodePos);
\r
234 gizmo.setRotation(new AxisAngle4d());
\r
235 parentWorldOrientation = null;
\r
237 AxisAngle4d aa = new AxisAngle4d();
\r
238 parentWorldOrientation = ((IG3DNode)node.getParent()).getWorldOrientation();
\r
239 aa.set(parentWorldOrientation);
\r
240 gizmo.setRotation(aa);
\r
243 Point3d camPos = new Point3d(panel.GetRenderer().GetActiveCamera().GetPosition());
\r
244 Vector3d p = new Vector3d(nodePos);
\r
247 if (parentWorldOrientation != null) {
\r
248 Quat4d qi = new Quat4d(parentWorldOrientation);
\r
250 MathTools.rotate(parentWorldOrientation, p, p);
\r
252 if (panel.GetRenderer().GetActiveCamera().GetParallelProjection() == 0) {
\r
253 double distance = p.length();
\r
255 double fov = panel.GetRenderer().GetActiveCamera().GetViewAngle();
\r
256 float s = (float) (Math.sin(fov) * distance * 0.1);
\r
258 Vector3d scale = new Vector3d(1., 1., 1.);
\r
267 gizmo.setScale(scale);
\r
270 Vector3d scale = new Vector3d(1.f, 1.f, 1.f);
\r
271 double s = panel.GetRenderer().GetActiveCamera().GetParallelScale() / 5.;
\r
279 gizmo.setScale(scale);
\r
285 private boolean isOverNode(MouseEvent e) {
\r
286 vtkProp picked[] = panel.pick(e.getX(), e.getY());
\r
287 if (picked !=null) {
\r
288 for (int i = 0; i < picked.length; i++) {
\r
289 if (node.equals(nodeMap.getNode(picked[i])))
\r
299 public void mousePressed(MouseEvent e) {
\r
300 if (e.getButton() == MouseEvent.BUTTON1) {
\r
303 if (isOverNode(e)) {
\r
305 if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) {
\r
310 worldOrientation = node.getWorldOrientation();
\r
311 doChanges(true, e.getX(), e.getY());
\r
313 panel.setCursor(dragCursor);
\r
316 panel.getDefaultAction().mousePressed(e);
\r
317 panel.setCursor(activeCursor);
\r
320 panel.getDefaultAction().mousePressed(e);
\r
327 public void mouseReleased(MouseEvent e) {
\r
328 if (e.getButton() == MouseEvent.BUTTON1) {
\r
330 worldOrientation = null;
\r
331 panel.setCursor(activeCursor);
\r
333 panel.getDefaultAction().mouseReleased(e);
\r
338 public void mouseDragged(MouseEvent e) {
\r
339 if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) > 0 && valid) {
\r
340 if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) {
\r
345 doChanges(false, e.getX(), e.getY());
\r
347 //nodeMap.modified(node);
\r
350 panel.getDefaultAction().mouseDragged(e);
\r
355 Vector3d axis = null;
\r
358 public void keyTyped(KeyEvent e) {
\r
359 if (e.getKeyCode() == KeyEvent.VK_LEFT) {
\r
360 inputType = InputType.KEY;
\r
361 axis = new Vector3d(0.0,1.0,0.0);
\r
362 } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
\r
363 inputType = InputType.KEY;
\r
364 axis = new Vector3d(0.0,-1.0,0.0);
\r
365 } else if (e.getKeyCode() ==KeyEvent.VK_UP) {
\r
366 inputType = InputType.KEY;
\r
367 axis = new Vector3d(1.0,0.0,0.0);
\r
368 } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
\r
369 inputType = InputType.KEY;
\r
370 axis = new Vector3d(-1.0,0.0,0.0);
\r
374 public void doChanges(boolean pressed, int x, int y) {
\r
375 Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(),x, y);
\r
376 Vector3d p = node.getWorldPosition();
\r
379 Vector3d axis = getRotationAxis();
\r
380 if (axis != null) {
\r
382 MathTools.rotate(parentWorldOrientation, axis, axis);
\r
386 double s[] = new double[2];
\r
387 Vector3d i2 = new Vector3d();
\r
389 boolean intersect = MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s);
\r
390 double dot = Math.abs(ray.dir.dot(axis));
\r
391 if (intersect && dot > 0.4)
\r
392 inputType = InputType.INTERSECT;
\r
394 inputType = InputType.NONINTERSECT;
\r
397 if (inputType == InputType.INTERSECT) {
\r
398 // picking ray and plane defined by gizmo's center point and
\r
399 // rotation axis can intersect
\r
400 // vector from center point to intersection point
\r
402 // creating vectors i and j that are lying on the plane and
\r
403 // are perpendicular
\r
404 // vectors are used to calculate polar coordinate for
\r
405 // intersection point
\r
408 System.out.println("I,J " + i + " " + j);
\r
409 double angleI = i2.angle(i);
\r
410 double angleJ = i2.angle(j);
\r
411 prevAngle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));
\r
413 // picking ray and plane defined by gizmo's center point and
\r
414 // rotation axis are parallel,
\r
415 // so we'll use cross product of rotation axis and picking
\r
416 // ray to detect amount of rotation
\r
417 i.cross(ray.dir, axis);
\r
418 MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s);
\r
426 if (inputType != InputType.KEY)
\r
427 axis = getRotationAxis();
\r
428 if (axis == null) {
\r
431 Vector3d taxis = null;
\r
433 taxis = new Vector3d(axis);
\r
434 MathTools.rotate(parentWorldOrientation, axis, axis);
\r
436 System.out.println(inputType);
\r
437 if (inputType == InputType.INTERSECT) {
\r
439 double s[] = new double[2];
\r
440 Vector3d i2 = new Vector3d();
\r
441 MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s);
\r
443 double angleI = i2.angle(i);
\r
444 double angleJ = i2.angle(j);
\r
445 double angle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));
\r
446 System.out.println("Angle " + angle + " i " + angleI + " j " + angleJ + " prev " + prevAngle);
\r
451 //setOrientation(MathTools.getQuat(rotation));
\r
452 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
\r
453 Quat4d qrot = new Quat4d();
\r
454 MathTools.getQuat(rot, qrot);
\r
455 //prevAngle = angle;
\r
456 qrot.mulInverse(worldOrientation);
\r
459 if (stepMethod == 0) {
\r
461 rot.angle = roundAngle(rot.angle);
\r
463 MathTools.getQuat(rot,qrot);
\r
464 setOrientation(qrot);
\r
465 } else if (stepMethod == 1){
\r
467 //Vector3d euler = MathTools.getEuler(qrot);
\r
468 Vector3d euler = EulerTools.getEulerFromQuat(order, qrot);
\r
469 euler.x = roundAngle(euler.x);
\r
470 euler.y = roundAngle(euler.y);
\r
471 euler.z = roundAngle(euler.z);
\r
472 //Quat4d q = MathTools.getQuat(euler);
\r
473 Quat4d q = EulerTools.getQuatFromEuler(order, euler);
\r
475 System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q);
\r
477 setOrientation(qrot);
\r
482 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));
\r
483 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
\r
484 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
\r
485 MathTools.multiplyOrientation(aa, rot);
\r
486 setWorldOrientation(MathTools.getQuat(rot));
\r
488 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
\r
489 AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle);
\r
490 MathTools.multiplyOrientation(aa, rot);
\r
491 setOrientation(MathTools.getQuat(rot));
\r
492 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,angle-prevAngle));
\r
497 } else if (inputType == InputType.NONINTERSECT){
\r
499 double s[] = new double[2];
\r
500 MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s);
\r
504 //setOrientation(MathTools.getQuat(rotation));
\r
505 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
\r
507 Quat4d qrot = new Quat4d();
\r
509 MathTools.getQuat(rot, qrot);
\r
510 //prevAngle = angle;
\r
511 qrot.mulInverse(worldOrientation);
\r
514 if (stepMethod == 0) {
\r
516 rot.angle = roundAngle(rot.angle);
\r
518 MathTools.getQuat(rot,qrot);
\r
519 setOrientation(qrot);
\r
520 } else if (stepMethod == 1){
\r
522 //Vector3d euler = MathTools.getEuler(qrot);
\r
523 Vector3d euler = EulerTools.getEulerFromQuat(order, qrot);
\r
524 euler.x = roundAngle(euler.x);
\r
525 euler.y = roundAngle(euler.y);
\r
526 euler.z = roundAngle(euler.z);
\r
527 //Quat4d q = MathTools.getQuat(euler);
\r
528 Quat4d q = EulerTools.getQuatFromEuler(order, euler);
\r
530 System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q);
\r
532 setOrientation(qrot);
\r
536 // G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));
\r
537 // G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));
\r
538 // AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation());
\r
539 // rotations.put(mo, aa);
\r
540 // Vector3d euler = MathTools.getEuler(aa);
\r
541 // euler.x = roundAngle(euler.x);
\r
542 // euler.y = roundAngle(euler.y);
\r
543 // euler.z = roundAngle(euler.z);
\r
544 // aa = MathTools.getFromEuler2(euler);
\r
546 // G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa);
\r
547 // Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()));
\r
548 // e.scale(180.0/Math.PI);
\r
549 // text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " ";
\r
554 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
\r
555 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
\r
556 MathTools.multiplyOrientation(aa, rot);
\r
557 setWorldOrientation(MathTools.getQuat(rot));
\r
558 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));
\r
560 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
\r
561 AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS);
\r
562 MathTools.multiplyOrientation(aa, rot);
\r
563 setOrientation(MathTools.getQuat(rot));
\r
564 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,s[1] - prevS));
\r
566 //text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";
\r
573 AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation());
\r
574 AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5);
\r
575 MathTools.multiplyOrientation(aa, rot);
\r
576 setWorldOrientation(MathTools.getQuat(rot));
\r
577 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));
\r
579 AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation());
\r
580 AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5);
\r
581 MathTools.multiplyOrientation(aa, rot);
\r
582 setOrientation(MathTools.getQuat(rot));
\r
583 //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));
\r
585 // text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";
\r
588 //setInfoText(text);
\r
592 protected void setOrientation(Quat4d q) {
\r
593 node.setOrientation(q);
\r
596 protected void setWorldOrientation(Quat4d q) {
\r
597 node.setWorldOrientation(q);
\r
601 public void mouseMoved(MouseEvent e) {
\r
602 panel.getDefaultAction().mouseMoved(e);
\r
605 private Vector3d getRotationAxis() {
\r
608 return new Vector3d(1.0, 0.0, 0.0);
\r
610 return new Vector3d(0.0, 1.0, 0.0);
\r
612 return new Vector3d(0.0, 0.0, 1.0);
\r
614 Vector3d axis = new Vector3d(panel.GetRenderer().GetActiveCamera()
\r
615 .GetDirectionOfProjection());
\r
623 private double prevS = 0.0;
\r
625 private Vector3d i = new Vector3d();
\r
626 private Vector3d j = new Vector3d();
\r
627 private double prevAngle = 0;
\r
629 enum InputType{INTERSECT,NONINTERSECT,KEY,NONE};
\r
630 InputType inputType;
\r
631 private boolean useStep = false;
\r
635 private double roundAngle(double angle) {
\r
636 while (angle < - Math.PI)
\r
637 angle += Math.PI*2.0;
\r
638 while (angle > Math.PI)
\r
639 angle -= Math.PI*2.0;
\r
643 while (angle > angles[index])
\r
648 double d = angle - angles[index - 1];
\r
649 double d2 = angles[index] - angle;
\r
651 angle = angles[index - 1];
\r
653 angle = angles[index];
\r