/******************************************************************************* * Copyright (c) 2012, 2013 Association for Decentralized Information Management in * Industry THTH ry. * 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.g3d.vtk.awt; import java.awt.Cursor; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.math.BigDecimal; import javax.vecmath.AxisAngle4d; import javax.vecmath.Point3d; import javax.vecmath.Quat4d; import javax.vecmath.Vector3d; import org.simantics.g3d.math.MathTools; import org.simantics.g3d.math.Ray; import org.simantics.g3d.scenegraph.IG3DNode; import org.simantics.g3d.scenegraph.base.INode; import org.simantics.g3d.scenegraph.structural.IStructuralNode; import org.simantics.g3d.vtk.Activator; import org.simantics.g3d.vtk.common.VTKNodeMap; import org.simantics.g3d.vtk.gizmo.TranslateAxisGizmo; import org.simantics.g3d.vtk.utils.vtkUtil; import org.simantics.utils.threads.AWTThread; import org.simantics.utils.threads.ThreadUtils; import vtk.vtkProp; public class TranslateAction extends vtkAwtAction{ public static final int X = 0; public static final int Y = 1; public static final int Z = 2; public static final int XY = 3; public static final int XZ = 4; public static final int YZ = 5; public static final int P = 6; private VTKNodeMap nodeMap; //private TranslateGizmo gizmo = new TranslateGizmo(); private TranslateAxisGizmo gizmo = new TranslateAxisGizmo(); protected IG3DNode node; private Cursor activeCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); private Cursor dragCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); public void setNode(IG3DNode node) { this.node = node; if ((node instanceof IStructuralNode) && ((IStructuralNode)node).isPartOfInstantiatedModel() && !((IStructuralNode)node).isInstantiatedModelRoot()) { setEnabled(false); } else { setEnabled(true); } } public IG3DNode getNode() { return node; } public TranslateAction(InteractiveVtkPanel panel, VTKNodeMap nodeMap) { super(panel); setImageDescriptor(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/arrow_out.png")); setText("Translate"); this.nodeMap = nodeMap; } 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("Translate"); deattachUI(); super.deattach(); panel.repaint(); } private void attachUI() { panel.setCursor(activeCursor); gizmo.attach(panel); } 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); update(); //panel.repaint(); } @Override public void keyReleased(KeyEvent e) { } @Override public void keyTyped(KeyEvent e) { } @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() > 1) { if (isOverNode(e)) { return; } else { panel.useDefaultAction(); } //if(!gizmo.isPartOf(actor)) // panel.useDefaultAction(); } } 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 mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) { } int index = P; protected boolean valid = false; private boolean worldCoord = true; private AxisAngle4d aa = null; private Quat4d q = null; public void setWorldCoord(boolean b) { if (worldCoord == b) return; worldCoord = b; update(); } protected void update() { if (node == null) return; if (worldCoord) { gizmo.setRotation(new AxisAngle4d()); aa = null; q = null; } else { aa = new AxisAngle4d(); aa.set(((IG3DNode)node.getParent()).getWorldOrientation()); gizmo.setRotation(aa); q = new Quat4d(); MathTools.getQuat(aa, q); } Vector3d nodePos = node.getWorldPosition(); //System.out.println(nodePos); gizmo.setPosition(nodePos); Point3d camPos = new Point3d(panel.GetRenderer().GetActiveCamera().GetPosition()); Vector3d p = new Vector3d(nodePos); p.sub(camPos); if (q != null) { Quat4d qi = new Quat4d(q); qi.inverse(); MathTools.rotate(q, 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(); panel.repaint(); } protected Vector3d prevTranslate = null; @Override public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { if (isOverNode(e)) { prevTranslate = getTranslate(e.getX(), e.getY()); valid = true; panel.setCursor(dragCursor); } else { valid = false; getDefaultAction().mousePressed(e); panel.setCursor(activeCursor); } } else { getDefaultAction().mousePressed(e); } //index = gizmo.getTranslateAxis(actor); //if (index == -1) { // valid = false; // panel.getDefaultAction().mousePressed(e); // return; //} //valid = true; //prevTranslate = getTranslate(e.getX(), e.getY()); //System.out.println("start translate " + prevTranslate); } @Override public void mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { valid = false; prevTranslate = null; panel.setCursor(activeCursor); } else { getDefaultAction().mouseReleased(e); } } @Override public void mouseDragged(MouseEvent e) { if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) > 0 && valid) { Vector3d translate = getTranslate(e.getX(), e.getY(), prevTranslate); //System.out.println("translate " + translate); if (translate == null) return; boolean step = ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0); if (worldCoord) { Vector3d pos = new Vector3d(node.getWorldPosition()); pos.add(translate); pos = constaints(pos, step); setWorldPos(pos); } else { Vector3d pos = new Vector3d(node.getPosition()); pos.add(translate); pos = constaints(pos, step); setPos(pos); } //mapping.rangeModified(node); //nodeMap.modified(node); update(); } else { getDefaultAction().mouseDragged(e); update(); } } protected void setPos(Vector3d pos) { node.setPosition(pos); } protected void setWorldPos(Vector3d pos) { node.setWorldPosition(pos); } private double istep = 10.0; private int decimals = 2; protected Vector3d constaints(Vector3d p, boolean step) { if(!step) return p; switch (index) { case X: p.x = Math.round(istep * p.x) / istep; BigDecimal bx = new BigDecimal(p.x); bx.setScale(decimals, BigDecimal.ROUND_HALF_UP); p.x = bx.doubleValue(); break; case Y: p.y = Math.round(istep * p.y) / istep; BigDecimal by = new BigDecimal(p.y); by.setScale(decimals, BigDecimal.ROUND_HALF_UP); p.y = by.doubleValue(); break; case Z: p.z = Math.round(istep * p.z) / istep; BigDecimal bz = new BigDecimal(p.z); bz.setScale(decimals, BigDecimal.ROUND_HALF_UP); p.z = bz.doubleValue(); break; } return p; } @Override public void mouseMoved(MouseEvent e) { getDefaultAction().mouseMoved(e); } protected Vector3d getTranslate(double x, double y) { return getTranslate(x, y, new Vector3d()); } protected Vector3d getTranslate(double x, double y, Vector3d offset) { Vector3d translate = new Vector3d(); Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(),x, y); Vector3d p = node.getWorldPosition(); Vector3d dir = null; switch (index) { case P: Vector3d normal = new Vector3d(panel.GetRenderer().GetActiveCamera().GetDirectionOfProjection()); if (!worldCoord) { MathTools.rotate(q, normal, normal); } normal.normalize(); double s[] = new double[1]; Vector3d r = new Vector3d(); if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) { r.sub(p); translate.x = r.x; translate.y = r.y; translate.z = r.z; } break; case X : dir = new Vector3d(1.0,0.0,0.0); if(!worldCoord) MathTools.rotate(q, dir, dir); Vector3d i1 = new Vector3d(); Vector3d i2 = new Vector3d(); s = new double[2]; MathTools.intersectStraightStraight( p, dir,ray.pos, ray.dir, i2, i1,s); translate.x = s[0]; break; case Y : dir = new Vector3d(0.0,1.0,0.0); if(!worldCoord) MathTools.rotate(q, dir, dir); i1 = new Vector3d(); i2 = new Vector3d(); s = new double[2]; MathTools.intersectStraightStraight( p, dir,ray.pos, ray.dir, i2, i1,s); translate.y = s[0]; break; case Z : dir = new Vector3d(0.0,0.0,1.0); if(!worldCoord) MathTools.rotate(q, dir, dir); i1 = new Vector3d(); i2 = new Vector3d(); s = new double[2]; MathTools.intersectStraightStraight( p, dir,ray.pos, ray.dir, i2, i1,s); translate.z = s[0]; break; case XY : normal = new Vector3d(0.0,0.0,1.0); if(!worldCoord) MathTools.rotate(q, normal, normal); r = new Vector3d(); if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) { r.sub(p); translate.x = r.x; translate.y = r.y; } break; case XZ : normal = new Vector3d(0.0,1.0,0.0); if(!worldCoord) MathTools.rotate(q, normal, normal); r = new Vector3d(); if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) { r.sub(p); translate.x = r.x; translate.z = r.z; } break; case YZ : normal = new Vector3d(1.0,0.0,0.0); if(!worldCoord) MathTools.rotate(q, normal, normal); r = new Vector3d(); if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) { r.sub(p); translate.y = r.y; translate.z = r.z; } break; default : return null; } translate.sub(offset); return translate; } }