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