--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007- VTT Technical Research Centre of Finland.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.proconf.g3d.actions;\r
+\r
+import java.awt.event.KeyEvent;\r
+import java.awt.event.MouseEvent;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import javax.vecmath.AxisAngle4d;\r
+import javax.vecmath.AxisAngle4f;\r
+import javax.vecmath.Quat4d;\r
+import javax.vecmath.Vector3d;\r
+\r
+import org.eclipse.jface.action.Action;\r
+import org.eclipse.jface.action.IToolBarManager;\r
+import org.simantics.db.Graph;\r
+import org.simantics.db.GraphRequest;\r
+import org.simantics.db.GraphRequestAdapter;\r
+import org.simantics.db.GraphRequestStatus;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.Resource;\r
+import org.simantics.layer0.utils.IEntity;\r
+import org.simantics.layer0.utils.EntityFactory;\r
+import org.simantics.proconf.g3d.Activator;\r
+import org.simantics.proconf.g3d.Resources;\r
+import org.simantics.proconf.g3d.base.G3DTools;\r
+import org.simantics.proconf.g3d.base.JmeRenderingComponent;\r
+import org.simantics.proconf.g3d.base.MathTools;\r
+import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase;\r
+import org.simantics.proconf.g3d.base.VecmathJmeTools;\r
+import org.simantics.proconf.g3d.common.OrbitalCamera;\r
+import org.simantics.proconf.g3d.gizmo.RotateGizmo;\r
+import org.simantics.proconf.g3d.scenegraph.IGraphicsNode;\r
+import org.simantics.proconf.g3d.stubs.G3DNode;\r
+\r
+public class RotateAction extends WriteInteractiveAction {\r
+ \r
+ private JmeRenderingComponent component;\r
+ \r
+ private RotateGizmo gizmo;\r
+\r
+ private OrbitalCamera camera;\r
+ \r
+ private Map<IGraphicsNode, AxisAngle4d > rotations = new HashMap<IGraphicsNode, AxisAngle4d>();\r
+ \r
+ private int steps; \r
+ private double angles[];\r
+ \r
+ private Action csAction;\r
+ private boolean worldCoord = true;\r
+ private IToolBarManager manager;\r
+ \r
+ private List<IGraphicsNode> mos;\r
+ AxisAngle4d aa;\r
+ Quat4d q;\r
+ \r
+ public RotateAction(ThreeDimensionalEditorBase parent) {\r
+ super(parent,true);\r
+ component = parent.getRenderingComponent();\r
+ camera = parent.getCamera();\r
+ gizmo = new RotateGizmo(component.getDisplaySystem().getRenderer());\r
+ csAction = new Action("World",Action.AS_CHECK_BOX) {\r
+ public void run() {\r
+ GraphRequest r = new GraphRequestAdapter() {\r
+ @Override\r
+ public GraphRequestStatus perform(Graph g) throws Exception {\r
+ setWorldCoord(g,!isChecked());\r
+ return GraphRequestStatus.transactionComplete();\r
+ }\r
+ };\r
+ RotateAction.this.parent.getSession().asyncRead(r);\r
+ \r
+ }\r
+ };\r
+ }\r
+ \r
+ public void init() {\r
+ this.setText("Rotate");\r
+ this.setToolTipText("Rotate the object");\r
+ this.setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/rotate.png"));\r
+ steps = 36;\r
+ angles = new double[steps+1];\r
+ for (int i = 0; i < angles.length; i++) {\r
+ angles[i] = - Math.PI + (Math.PI * i * 2.0 / steps);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public boolean usable(Graph graph, List<Resource> resources) {\r
+ if (resources.size() == 0)\r
+ return false;\r
+ for (Resource r : resources) {\r
+ IEntity t = EntityFactory.create(graph,r);\r
+ if (t.isInstanceOf(Resources.g3dResource.G3DNode)) {\r
+ Collection<IEntity> p = t.getRelatedObjects(Resources.g3dResource.HasLocalOrientation);\r
+ if (p == null || p.size() != 1)\r
+ return false;\r
+ }\r
+ \r
+ }\r
+ return true;\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void deactivate() {\r
+ inputType = InputType.NONE;\r
+ parent.setGizmo(null);\r
+ mos = null;\r
+\r
+ }\r
+ \r
+ public void fillToolBar(IToolBarManager manager) {\r
+ super.fillToolBar(manager);\r
+ this.manager = manager;\r
+ csAction.setChecked(!worldCoord);\r
+ manager.add(csAction);\r
+ \r
+ }\r
+ \r
+ private void setWorldCoord(Graph graph,boolean b) {\r
+ if (worldCoord == b)\r
+ return;\r
+ worldCoord = b;\r
+ updateWorldCoord(graph);\r
+ }\r
+ \r
+ private void updateWorldCoord(Graph graph) {\r
+ if (worldCoord) {\r
+ csAction.setText("World");\r
+ gizmo.setRotation(new AxisAngle4f());\r
+ aa = null;\r
+ q = null;\r
+ } else {\r
+ csAction.setText("Local");\r
+ aa = G3DTools.getOrientation(mos.get(0).getParent().getG3DNode(graph).getWorldOrientation());\r
+ if (aa == null)\r
+ aa = new AxisAngle4d();\r
+ gizmo.setRotation(new AxisAngle4f(aa));\r
+ q = new Quat4d();\r
+ q.set(aa);\r
+ }\r
+ if (manager != null)\r
+ manager.update(true);\r
+ this.parent.setViewChanged(true);\r
+ }\r
+ \r
+ @Override\r
+ public void activate() {\r
+ \r
+ Session session = parent.getSession();\r
+ \r
+ GraphRequest r = new GraphRequestAdapter() {\r
+ @Override\r
+ public GraphRequestStatus perform(Graph g) throws Exception {\r
+ parent.setGizmo(gizmo);\r
+ \r
+ component.getNoShadowRoot().attachChild(gizmo.getNode());\r
+ updateGizmo();\r
+\r
+ String text = "";\r
+ mos = parent.getSelectionAdapter().getSelectedObjects();\r
+ rotations = new HashMap<IGraphicsNode, AxisAngle4d>();\r
+ for (IGraphicsNode mo : mos) {\r
+ G3DNode n = mo.getG3DNode(g);\r
+ rotations.put(mo,G3DTools.getOrientation(n.getLocalOrientation()));\r
+ text += G3DTools.getOrientation(n.getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(g).getLocalOrientation())) + " ";\r
+ }\r
+ setInfoText(text);\r
+ parent.setViewChanged(true);\r
+ inputType = InputType.NONE;\r
+ return GraphRequestStatus.transactionComplete();\r
+ }\r
+ };\r
+ \r
+ session.syncRead(r);\r
+ \r
+ \r
+ }\r
+ \r
+ \r
+ private Vector3d getRotationAxis() {\r
+ switch (gizmo.getSelected()) {\r
+ case RotateGizmo.X:\r
+ return new Vector3d(1.0,0.0,0.0);\r
+ case RotateGizmo.Y:\r
+ return new Vector3d(0.0,1.0,0.0);\r
+ case RotateGizmo.Z:\r
+ return new Vector3d(0.0,0.0,1.0);\r
+ case RotateGizmo.XYZ:\r
+ Vector3d axis = camera.getUnNormalizedHeading();\r
+ axis.normalize();\r
+ return axis;\r
+ default:\r
+ return null;\r
+ }\r
+ }\r
+ private double prevS = 0.0;\r
+ \r
+ private Vector3d i = new Vector3d();\r
+ private Vector3d j = new Vector3d();\r
+ private double prevAngle = 0;\r
+\r
+ enum InputType{INTERSECT,NONINTERSECT,KEY,NONE};\r
+ InputType inputType;\r
+ private boolean useStep = false;\r
+ \r
+ @Override\r
+ public void doChanges(Graph graph) throws Exception {\r
+ \r
+ if (input.mousePressed()) {\r
+ Vector3d axis = getRotationAxis();\r
+ if (axis != null) {\r
+ if (!worldCoord) {\r
+ MathTools.rotate(q, axis, axis);\r
+ }\r
+ Vector3d o = new Vector3d();\r
+ Vector3d d = new Vector3d();\r
+ parent.createPickRay(o, d);\r
+ Vector3d p = gizmo.getPosition();\r
+ double s[] = new double[2];\r
+ Vector3d i2 = new Vector3d();\r
+ if ((input.pressModifiers() & MouseEvent.CTRL_MASK) > 0) {\r
+ useStep = true;\r
+ } else {\r
+ useStep = false;\r
+ }\r
+ if (MathTools.intersectStraightPlane(o, d, p, axis, i2, s) && Math.abs(d.dot(axis)) > 0.2)\r
+ inputType = InputType.INTERSECT;\r
+ else\r
+ inputType = InputType.NONINTERSECT;\r
+ \r
+ \r
+ if (inputType == InputType.INTERSECT) {\r
+ // picking ray and plane defined by gizmo's center point and\r
+ // rotation axis can intersect\r
+ // vector from center point to intersection point\r
+ i2.sub(p);\r
+ // creating vectors i and j that are lying on the plane and\r
+ // are perpendicular\r
+ // vectors are used to calculate polar coordinate for\r
+ // intersection point\r
+ j.set(i2);\r
+ i.cross(j, axis);\r
+ double angleI = i2.angle(i);\r
+ double angleJ = i2.angle(j);\r
+ prevAngle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));\r
+ } else {\r
+ // picking ray and plane defined by gizmo's center point and\r
+ // rotation axis are parallel,\r
+ // so we'll use cross product of rotation axis and picking\r
+ // ray to detect amount of rotation\r
+ i.cross(d, axis);\r
+ MathTools.intersectStraightStraight(o, d, p, i, new Vector3d(), new Vector3d(), s);\r
+ prevS = s[1];\r
+ }\r
+ }\r
+ \r
+ \r
+ }\r
+ if (input.mouseClicked()) {\r
+ end();\r
+ return;\r
+ }\r
+ Vector3d axis = null;\r
+ if (input.keyPressed(KeyEvent.VK_LEFT)) {\r
+ inputType = InputType.KEY;\r
+ axis = new Vector3d(0.0,1.0,0.0);\r
+ } else if (input.keyPressed(KeyEvent.VK_RIGHT)) {\r
+ inputType = InputType.KEY;\r
+ axis = new Vector3d(0.0,-1.0,0.0);\r
+ } else if (input.keyPressed(KeyEvent.VK_UP)) {\r
+ inputType = InputType.KEY;\r
+ axis = new Vector3d(1.0,0.0,0.0);\r
+ } else if (input.keyPressed(KeyEvent.VK_DOWN)) {\r
+ inputType = InputType.KEY;\r
+ axis = new Vector3d(-1.0,0.0,0.0);\r
+ } else if (!input.mouseDragged()) {\r
+ parent.getDefaultAction().update();\r
+ return;\r
+ }\r
+ parent.setViewChanged(true);\r
+ \r
+ \r
+ updateGizmo();\r
+ List<IGraphicsNode> mos = parent.getSelectionAdapter().getSelectedObjects();\r
+ if (inputType != InputType.KEY)\r
+ axis = getRotationAxis();\r
+ if (axis == null) {\r
+ parent.getDefaultAction().update();\r
+ return; \r
+ }\r
+ Vector3d taxis = null;\r
+ if (!worldCoord) {\r
+ taxis = new Vector3d(axis);\r
+ MathTools.rotate(q, axis, axis);\r
+ }\r
+ String text = "";\r
+ if (inputType == InputType.INTERSECT) {\r
+ Vector3d o = new Vector3d();\r
+ Vector3d d = new Vector3d();\r
+ parent.createPickRay(o, d);\r
+ Vector3d p = gizmo.getPosition();\r
+ double s[] = new double[2];\r
+ Vector3d i2 = new Vector3d();\r
+ MathTools.intersectStraightPlane(o, d, p, axis, i2, s);\r
+ i2.sub(p);\r
+ double angleI = i2.angle(i);\r
+ double angleJ = i2.angle(j);\r
+ double angle = Math.atan2(Math.cos(angleJ), Math.cos(angleI));\r
+ if(!worldCoord)\r
+ axis = taxis;\r
+ if (false && useStep) {\r
+ for (IGraphicsNode mo : mos) {\r
+ G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));\r
+ // FIXME : commit\r
+ G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));\r
+ //mo.setRotation(rotations.get(mo));\r
+ //mo.modifyWorldRotation(axis, angle - prevAngle);\r
+ AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation());\r
+ rotations.put(mo, aa);\r
+ prevAngle = angle;\r
+ Vector3d euler = MathTools.getEuler(aa);\r
+ euler.x = roundAngle(euler.x);\r
+ euler.y = roundAngle(euler.y);\r
+ euler.z = roundAngle(euler.z);\r
+ aa = MathTools.getFromEuler2(euler);\r
+ G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa);\r
+ //mo.setRotation(aa);\r
+ Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()));\r
+ e.scale(180.0/Math.PI);\r
+ text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " ";\r
+ }\r
+ } else {\r
+ for (IGraphicsNode mo : mos) {\r
+ if (worldCoord)\r
+ G3DTools.multiplyOrientation\r
+ (mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,angle-prevAngle));\r
+ else\r
+ G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,angle-prevAngle));\r
+ text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";\r
+ }\r
+ prevAngle = angle;\r
+ }\r
+ \r
+ } else if (inputType == InputType.NONINTERSECT){\r
+ Vector3d o = new Vector3d();\r
+ Vector3d d = new Vector3d();\r
+ parent.createPickRay(o, d);\r
+ Vector3d p = gizmo.getPosition();\r
+ double s[] = new double[2];\r
+ MathTools.intersectStraightStraight(o, d, p, i, new Vector3d(), new Vector3d(), s);\r
+ if(!worldCoord)\r
+ axis = taxis;\r
+ if (false && useStep) {\r
+ for (IGraphicsNode mo : mos) {\r
+ G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), rotations.get(mo));\r
+ G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));\r
+ //mo.setRotation(rotations.get(mo));\r
+ //mo.modifyWorldRotation(axis, s[1] - prevS);\r
+ AxisAngle4d aa = G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation());\r
+ rotations.put(mo, aa);\r
+ Vector3d euler = MathTools.getEuler(aa);\r
+ euler.x = roundAngle(euler.x);\r
+ euler.y = roundAngle(euler.y);\r
+ euler.z = roundAngle(euler.z);\r
+ aa = MathTools.getFromEuler2(euler);\r
+ prevS = s[1];\r
+ G3DTools.setOrientation(mo.getG3DNode(graph).getLocalOrientation(), aa);\r
+ //mo.setRotation(aa);\r
+ Vector3d e = MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()));\r
+ e.scale(180.0/Math.PI);\r
+ text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + e + " ";\r
+ }\r
+ } else {\r
+ for (IGraphicsNode mo : mos) {\r
+ if (worldCoord)\r
+ G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,s[1] - prevS));\r
+ else\r
+ G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,s[1] - prevS));\r
+ text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";\r
+ }\r
+ prevS = s[1];\r
+ \r
+ }\r
+ \r
+ } else {\r
+ for (IGraphicsNode mo : mos) {\r
+ if (worldCoord)\r
+ G3DTools.multiplyOrientation(mo.getG3DNode(graph).getWorldOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));\r
+ else\r
+ G3DTools.multiplyOrientation(mo.getG3DNode(graph).getLocalOrientation(), new AxisAngle4d(axis,Math.PI * 0.5));\r
+ text += G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation()) + " " + MathTools.getEuler(G3DTools.getOrientation(mo.getG3DNode(graph).getLocalOrientation())) + " ";\r
+ }\r
+ }\r
+ setInfoText(text);\r
+ \r
+ }\r
+ /*\r
+ private double roundAngle(double current, double modify) {\r
+ double angle = roundAngle(current+modify);\r
+ if (Double.isNaN(angle)) {\r
+ angle = current+modify;\r
+ }\r
+ //System.out.println(angle + " " + (current+modify));\r
+ return angle;\r
+ }\r
+ */\r
+ \r
+ private double roundAngle(double angle) {\r
+ while (angle < - Math.PI)\r
+ angle += Math.PI*2.0;\r
+ while (angle > Math.PI)\r
+ angle -= Math.PI*2.0;\r
+ \r
+ \r
+ int index = 0;\r
+ while (angle > angles[index])\r
+ index++;\r
+ if (index == 0) {\r
+ angle = angles[0];\r
+ } else {\r
+ double d = angle - angles[index - 1];\r
+ double d2 = angles[index] - angle;\r
+ if (d < d2)\r
+ angle = angles[index - 1];\r
+ else\r
+ angle = angles[index];\r
+ }\r
+ return angle;\r
+ }\r
+ \r
+ private void updateGizmo() {\r
+ List<IGraphicsNode> mos = parent.getSelectionAdapter().getSelectedObjects();\r
+ //gizmo.update(XithTools.getPosition(mos.get(0).getGroup()),camera.getCameraPos(),component);\r
+ gizmo.update(VecmathJmeTools.getD(mos.get(0).getGroup().getWorldTranslation()),camera.getCameraPos(),component);\r
+ }\r
+ \r
+ public void setInfoText(String text) {\r
+ \r
+ }\r
+}
\ No newline at end of file