X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.g3d.vtk%2Fsrc%2Forg%2Fsimantics%2Fg3d%2Fvtk%2Faction%2FRotateAction.java;fp=org.simantics.g3d.vtk%2Fsrc%2Forg%2Fsimantics%2Fg3d%2Fvtk%2Faction%2FRotateAction.java;h=b2752c4c674faa3bb296a509ba3b225c5850d3ee;hb=87b3241ec277ba3d8e414b26186a032c9cdcaeed;hp=0000000000000000000000000000000000000000;hpb=1f0bcd66274375f2278d2e6c486cb28257a5f7b2;p=simantics%2F3d.git diff --git a/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/action/RotateAction.java b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/action/RotateAction.java new file mode 100644 index 00000000..b2752c4c --- /dev/null +++ b/org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/action/RotateAction.java @@ -0,0 +1,647 @@ +package org.simantics.g3d.vtk.action; + +import java.awt.Cursor; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.Point3d; +import javax.vecmath.Quat4d; +import javax.vecmath.Vector3d; + +import org.simantics.g3d.math.EulerTools; +import org.simantics.g3d.math.MathTools; +import org.simantics.g3d.math.Ray; +import org.simantics.g3d.math.EulerTools.Order; +import org.simantics.g3d.preferences.PreferenceConstants; +import org.simantics.g3d.scenegraph.IG3DNode; +import org.simantics.g3d.scenegraph.structural.IStructuralNode; +import org.simantics.g3d.vtk.Activator; +import org.simantics.g3d.vtk.common.InteractiveVtkPanel; +import org.simantics.g3d.vtk.common.VTKNodeMap; +import org.simantics.g3d.vtk.gizmo.RotateAxisGizmo; +import org.simantics.g3d.vtk.utils.vtkUtil; +import org.simantics.utils.threads.AWTThread; +import org.simantics.utils.threads.ThreadUtils; + +import vtk.vtkProp; +/** + * FIXME: complete rewrite. + * + * @author Marko Luukkainen + * + */ +public class RotateAction extends vtkAction{ + + public static final int X = 0; + public static final int Y = 1; + public static final int Z = 2; + public static final int P = 3; + + private VTKNodeMap nodeMap; + //private TranslateGizmo gizmo = new TranslateGizmo(); + private RotateAxisGizmo gizmo = new RotateAxisGizmo(); + private IG3DNode node; + + + + private Cursor activeCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); + private Cursor dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); + + + int stepMethod = 1; + Order order = Order.YXZ; + + private int steps; + private double angles[]; + + int index = P; + boolean valid = false; + private boolean worldCoord = true; + //private AxisAngle4d aa = null; + private Quat4d parentWorldOrientation = null; + + //AxisAngle4d rotation = new AxisAngle4d(); + Quat4d worldOrientation = new Quat4d(); + + public void setNode(IG3DNode node) { + this.node = node; + if ((node instanceof IStructuralNode) && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()) { + setEnabled(false); + } else { + setEnabled(true); + } + + String set = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.ORIENTATION_PRESENTATION); + if (set.equals("aa")) { + stepMethod = 0; + } else if (set.equals("euler")){ + stepMethod = 1; + String eulerOrder = org.simantics.g3d.Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.EULER_ANGLE_ORDER); + try { + order = Order.valueOf(eulerOrder); + } catch (Exception e) { + order = Order.YXZ; + } + } else { + stepMethod = 2; + } + } + + public IG3DNode getNode() { + return node; + } + + public RotateAction(InteractiveVtkPanel panel, VTKNodeMap nodeMap) { + super(panel); + setImageDescriptor(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/arrow_rotate_clockwise.png")); + setText("Rotate"); + this.nodeMap = nodeMap; + + + steps = 36; + angles = new double[steps+1]; + for (int i = 0; i < angles.length; i++) { + angles[i] = - Math.PI + (Math.PI * i * 2.0 / steps); + } + } + + public void attach() { + if (node == null) + return; + + super.attach(); + ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { + public void run() { + attachUI(); + update(); + } + }); + + + + } + + public void deattach() { + + node = null; + nodeMap.commit(); + deattachUI(); + super.deattach(); + panel.repaint(); + } + + private void attachUI() { + panel.setCursor(activeCursor); + gizmo.attach(panel.GetRenderer()); + } + + private void deattachUI() { + panel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + gizmo.deattach(); + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) + panel.useDefaultAction(); + if (valid) + return; + if (e.getKeyCode() == KeyEvent.VK_X) { + if (index != X) + index = X; + else + index = P; + } + if (e.getKeyCode() == KeyEvent.VK_Y) { + if (index != Y) + index = Y; + else + index = P; + } + if (e.getKeyCode() == KeyEvent.VK_Z) { + if (index != Z) + index = Z; + else + index = P; + } + if (e.getKeyCode() == KeyEvent.VK_G) { + worldCoord = !worldCoord; + } + gizmo.setType(index); + panel.repaint(); + } + + @Override + public void keyReleased(KeyEvent e) { + + } + + + + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() > 1) { + if (isOverNode(e)) { + return; + } + panel.useDefaultAction(); + //if(!gizmo.isPartOf(actor)) + // panel.useDefaultAction(); + + } + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + + + + + + public void setWorldCoord(boolean b) { + if (worldCoord == b) + return; + worldCoord = b; + update(); + + } + + + private void update() { + Vector3d nodePos = node.getWorldPosition(); + System.out.println(nodePos); + gizmo.setPosition(nodePos); + if (worldCoord) { + gizmo.setRotation(new AxisAngle4d()); + parentWorldOrientation = null; + } else { + AxisAngle4d aa = new AxisAngle4d(); + parentWorldOrientation = ((IG3DNode)node.getParent()).getWorldOrientation(); + aa.set(parentWorldOrientation); + gizmo.setRotation(aa); + } + + Point3d camPos = new Point3d(panel.GetRenderer().GetActiveCamera().GetPosition()); + Vector3d p = new Vector3d(nodePos); + p.sub(camPos); + + if (parentWorldOrientation != null) { + Quat4d qi = new Quat4d(parentWorldOrientation); + qi.inverse(); + MathTools.rotate(parentWorldOrientation, p, p); + } + if (panel.GetRenderer().GetActiveCamera().GetParallelProjection() == 0) { + double distance = p.length(); + p.negate(); + double fov = panel.GetRenderer().GetActiveCamera().GetViewAngle(); + float s = (float) (Math.sin(fov) * distance * 0.1); + + Vector3d scale = new Vector3d(1., 1., 1.); + +// if (p.x > 0.f) +// scale.x = -1.; +// if (p.y > 0.f) +// scale.y = -1.; +// if (p.z > 0.f) +// scale.z = -1.; + scale.scale(s); + gizmo.setScale(scale); + + } else { + Vector3d scale = new Vector3d(1.f, 1.f, 1.f); + double s = panel.GetRenderer().GetActiveCamera().GetParallelScale() / 5.; +// if (p.x > 0.f) +// scale.x = -1.; +// if (p.y > 0.f) +// scale.y = -1.; +// if (p.z > 0.f) +// scale.z = -1.; + scale.scale(s); + gizmo.setScale(scale); + } + + panel.Render(); + } + + private boolean isOverNode(MouseEvent e) { + vtkProp picked[] = panel.pick(e.getX(), e.getY()); + if (picked !=null) { + for (int i = 0; i < picked.length; i++) { + if (node.equals(nodeMap.getNode(picked[i]))) + return true; + } + } + return false; + } + + + + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + + + if (isOverNode(e)) { + valid = true; + if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) { + useStep = true; + } else { + useStep = false; + } + worldOrientation = node.getWorldOrientation(); + doChanges(true, e.getX(), e.getY()); + + panel.setCursor(dragCursor); + } else { + valid = false; + panel.getDefaultAction().mousePressed(e); + panel.setCursor(activeCursor); + } + } else { + panel.getDefaultAction().mousePressed(e); + } + } + + + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + valid = false; + worldOrientation = null; + panel.setCursor(activeCursor); + } else { + panel.getDefaultAction().mouseReleased(e); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) > 0 && valid) { + if ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0) { + useStep = true; + } else { + useStep = false; + } + doChanges(false, e.getX(), e.getY()); + + //nodeMap.modified(node); + update(); + } else { + panel.getDefaultAction().mouseDragged(e); + update(); + } + } + + Vector3d axis = null; + + @Override + public void keyTyped(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_LEFT) { + inputType = InputType.KEY; + axis = new Vector3d(0.0,1.0,0.0); + } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) { + inputType = InputType.KEY; + axis = new Vector3d(0.0,-1.0,0.0); + } else if (e.getKeyCode() ==KeyEvent.VK_UP) { + inputType = InputType.KEY; + axis = new Vector3d(1.0,0.0,0.0); + } else if (e.getKeyCode() == KeyEvent.VK_DOWN) { + inputType = InputType.KEY; + axis = new Vector3d(-1.0,0.0,0.0); + } + } + + public void doChanges(boolean pressed, int x, int y) { + Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(),x, y); + Vector3d p = node.getWorldPosition(); + + if (pressed) { + Vector3d axis = getRotationAxis(); + if (axis != null) { + if (!worldCoord) { + MathTools.rotate(parentWorldOrientation, axis, axis); + } + + + double s[] = new double[2]; + Vector3d i2 = new Vector3d(); + + boolean intersect = MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s); + double dot = Math.abs(ray.dir.dot(axis)); + if (intersect && dot > 0.4) + inputType = InputType.INTERSECT; + else + inputType = InputType.NONINTERSECT; + + + if (inputType == InputType.INTERSECT) { + // picking ray and plane defined by gizmo's center point and + // rotation axis can intersect + // vector from center point to intersection point + i2.sub(p); + // creating vectors i and j that are lying on the plane and + // are perpendicular + // vectors are used to calculate polar coordinate for + // intersection point + j.set(i2); + i.cross(j, axis); + System.out.println("I,J " + i + " " + j); + double angleI = i2.angle(i); + double angleJ = i2.angle(j); + prevAngle = Math.atan2(Math.cos(angleJ), Math.cos(angleI)); + } else { + // picking ray and plane defined by gizmo's center point and + // rotation axis are parallel, + // so we'll use cross product of rotation axis and picking + // ray to detect amount of rotation + i.cross(ray.dir, axis); + MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s); + prevS = s[1]; + } + } + + + } + + if (inputType != InputType.KEY) + axis = getRotationAxis(); + if (axis == null) { + return; + } + Vector3d taxis = null; + if (!worldCoord) { + taxis = new Vector3d(axis); + MathTools.rotate(parentWorldOrientation, axis, axis); + } + System.out.println(inputType); + if (inputType == InputType.INTERSECT) { + + double s[] = new double[2]; + Vector3d i2 = new Vector3d(); + MathTools.intersectStraightPlane(ray.pos, ray.dir, p, axis, i2, s); + i2.sub(p); + double angleI = i2.angle(i); + double angleJ = i2.angle(j); + double angle = Math.atan2(Math.cos(angleJ), Math.cos(angleI)); + System.out.println("Angle " + angle + " i " + angleI + " j " + angleJ + " prev " + prevAngle); + if(!worldCoord) + axis = taxis; + if (useStep) { + + //setOrientation(MathTools.getQuat(rotation)); + AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle); + Quat4d qrot = new Quat4d(); + MathTools.getQuat(rot, qrot); + //prevAngle = angle; + qrot.mulInverse(worldOrientation); + + + if (stepMethod == 0) { + rot.set(qrot); + rot.angle = roundAngle(rot.angle); + //qrot.set(rot); + MathTools.getQuat(rot,qrot); + setOrientation(qrot); + } else if (stepMethod == 1){ + + //Vector3d euler = MathTools.getEuler(qrot); + Vector3d euler = EulerTools.getEulerFromQuat(order, qrot); + euler.x = roundAngle(euler.x); + euler.y = roundAngle(euler.y); + euler.z = roundAngle(euler.z); + //Quat4d q = MathTools.getQuat(euler); + Quat4d q = EulerTools.getQuatFromEuler(order, euler); + setOrientation(q); + System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q); + } else { + setOrientation(qrot); + } + + } else { + if (worldCoord) { + //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle)); + AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation()); + AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle); + MathTools.multiplyOrientation(aa, rot); + setWorldOrientation(MathTools.getQuat(rot)); + } else { + AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation()); + AxisAngle4d rot = new AxisAngle4d(axis,angle-prevAngle); + MathTools.multiplyOrientation(aa, rot); + setOrientation(MathTools.getQuat(rot)); + //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,angle-prevAngle)); + } + prevAngle = angle; + } + + } else if (inputType == InputType.NONINTERSECT){ + + double s[] = new double[2]; + MathTools.intersectStraightStraight(ray.pos, ray.dir, p, i, new Vector3d(), new Vector3d(), s); + if(!worldCoord) + axis = taxis; + if (useStep) { + //setOrientation(MathTools.getQuat(rotation)); + AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS); + + Quat4d qrot = new Quat4d(); + //qrot.set(rot); + MathTools.getQuat(rot, qrot); + //prevAngle = angle; + qrot.mulInverse(worldOrientation); + + + if (stepMethod == 0) { + rot.set(qrot); + rot.angle = roundAngle(rot.angle); + //qrot.set(rot); + MathTools.getQuat(rot,qrot); + setOrientation(qrot); + } else if (stepMethod == 1){ + + //Vector3d euler = MathTools.getEuler(qrot); + Vector3d euler = EulerTools.getEulerFromQuat(order, qrot); + euler.x = roundAngle(euler.x); + euler.y = roundAngle(euler.y); + euler.z = roundAngle(euler.z); + //Quat4d q = MathTools.getQuat(euler); + Quat4d q = EulerTools.getQuatFromEuler(order, euler); + setOrientation(q); + System.out.println(" (" + MathTools.radToDeg(euler.x) + " " + MathTools.radToDeg(euler.y) + " " + MathTools.radToDeg(euler.z) + ") " + qrot + " "+ q); + } else { + setOrientation(qrot); + } + prevS = s[1]; + +// G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo)); +// G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS)); +// AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()); +// rotations.put(mo, aa); +// Vector3d euler = MathTools.getEuler(aa); +// euler.x = roundAngle(euler.x); +// euler.y = roundAngle(euler.y); +// euler.z = roundAngle(euler.z); +// aa = MathTools.getFromEuler2(euler); +// prevS = s[1]; +// G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa); +// Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())); +// e.scale(180.0/Math.PI); +// text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " "; + + + } else { + if (worldCoord) { + AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation()); + AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS); + MathTools.multiplyOrientation(aa, rot); + setWorldOrientation(MathTools.getQuat(rot)); + //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS)); + } else { + AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation()); + AxisAngle4d rot = new AxisAngle4d(axis,s[1] - prevS); + MathTools.multiplyOrientation(aa, rot); + setOrientation(MathTools.getQuat(rot)); + //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,s[1] - prevS)); + } + //text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " "; + prevS = s[1]; + + } + + } else { + if (worldCoord) { + AxisAngle4d aa = MathTools.getAxisAngle(node.getWorldOrientation()); + AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5); + MathTools.multiplyOrientation(aa, rot); + setWorldOrientation(MathTools.getQuat(rot)); + //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,Math.PI * 0.5)); + } else { + AxisAngle4d aa = MathTools.getAxisAngle(node.getOrientation()); + AxisAngle4d rot = new AxisAngle4d(axis,Math.PI * 0.5); + MathTools.multiplyOrientation(aa, rot); + setOrientation(MathTools.getQuat(rot)); + //G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,Math.PI * 0.5)); + } + // text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " "; + + } + //setInfoText(text); + + } + + protected void setOrientation(Quat4d q) { + node.setOrientation(q); + } + + protected void setWorldOrientation(Quat4d q) { + node.setWorldOrientation(q); + } + + @Override + public void mouseMoved(MouseEvent e) { + panel.getDefaultAction().mouseMoved(e); + } + + private Vector3d getRotationAxis() { + switch (index) { + case X: + return new Vector3d(1.0, 0.0, 0.0); + case Y: + return new Vector3d(0.0, 1.0, 0.0); + case Z: + return new Vector3d(0.0, 0.0, 1.0); + case P: + Vector3d axis = new Vector3d(panel.GetRenderer().GetActiveCamera() + .GetDirectionOfProjection()); + axis.normalize(); + return axis; + default: + return null; + } + } + + private double prevS = 0.0; + + private Vector3d i = new Vector3d(); + private Vector3d j = new Vector3d(); + private double prevAngle = 0; + + enum InputType{INTERSECT,NONINTERSECT,KEY,NONE}; + InputType inputType; + private boolean useStep = false; + + + + private double roundAngle(double angle) { + while (angle < - Math.PI) + angle += Math.PI*2.0; + while (angle > Math.PI) + angle -= Math.PI*2.0; + + + int index = 0; + while (angle > angles[index]) + index++; + if (index == 0) { + angle = angles[0]; + } else { + double d = angle - angles[index - 1]; + double d2 = angles[index] - angle; + if (d < d2) + angle = angles[index - 1]; + else + angle = angles[index]; + } + return angle; + } + +}