/******************************************************************************* * 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.swt; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.math.BigDecimal; import javax.vecmath.AxisAngle4d; import javax.vecmath.Point3d; import javax.vecmath.Quat4d; import javax.vecmath.Vector3d; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; 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.toolbar.ToolComposite; 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.ThreadUtils; import vtk.vtkProp; public class TranslateAction extends vtkSwtAction { 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); protected ToolComposite toolComposite; protected Combo axisCombo; 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(InteractiveVtkComposite panel, VTKNodeMap nodeMap, ToolComposite toolComposite) { super(panel); setImageDescriptor(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/arrow_out.png")); setText("Translate"); this.nodeMap = nodeMap; activeCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_HAND); dragCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_SIZEALL); this.toolComposite = toolComposite; } protected void createTools(ToolComposite toolComposite) { createMessage(toolComposite); createAxisSelection(toolComposite); createCloseButton(toolComposite); toolComposite.relayout(); } protected void createMessage(ToolComposite toolComposite) { new Label(toolComposite, SWT.READ_ONLY).setText("Press ESC to close translation. "); } protected void createAxisSelection(ToolComposite toolComposite) { Label label = new Label(toolComposite, SWT.READ_ONLY); label.setText("Translate direction:"); axisCombo = new Combo(toolComposite, SWT.READ_ONLY); axisCombo.add("X"); axisCombo.add("Y"); axisCombo.add("Z"); axisCombo.add("XY"); axisCombo.add("XZ"); axisCombo.add("YZ"); axisCombo.add("Camera"); axisCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Combo c = (Combo) e.getSource(); index = c.getSelectionIndex(); updateLock(); panel.getComponent().setFocus(); } }); axisCombo.select(index); } protected void createCloseButton(ToolComposite toolComposite) { Button close = new Button(toolComposite, SWT.PUSH); close.setText("Close"); close.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { panel.useDefaultAction(); }; }); } public void attach() { if (node == null) return; if (toolComposite != null) { createTools(toolComposite); } setDBUndo(false); super.attach(); ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() { public void run() { attachUI(); update(); } }); } public void deattach() { if (toolComposite != null) { toolComposite.clear(); axisCombo = null; } setDBUndo(true); node = null; nodeMap.commit("Translate"); deattachUI(); super.deattach(); panel.refresh(); } private void attachUI() { panel.getComponent().setCursor(activeCursor); gizmo.attach(panel); } private void deattachUI() { panel.getComponent().setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_ARROW)); gizmo.deattach(); } @Override public boolean keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) panel.useDefaultAction(); if (valid) return true; 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; } updateLock(); update(); // panel.repaint(); return true; } private void updateLock() { gizmo.setType(index); if (axisCombo != null) axisCombo.select(index); panel.refresh(); } @Override public boolean mouseClicked(MouseEvent e) { if (e.getClickCount() > 1) { if (isOverNode(e)) { return true; } else { panel.useDefaultAction(); } // if(!gizmo.isPartOf(actor)) // panel.useDefaultAction(); return true; } return false; } 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; } 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.refresh(); } protected Vector3d prevTranslate = null; @Override public boolean mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { if (isOverNode(e)) { prevTranslate = getTranslate(e.getX(), e.getY()); valid = true; panel.getComponent().setCursor(dragCursor); } else { valid = false; getDefaultAction().mousePressed(e); panel.getComponent().setCursor(activeCursor); } } else { getDefaultAction().mousePressed(e); } return true; // 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 boolean mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { valid = false; prevTranslate = null; panel.getComponent().setCursor(activeCursor); } else { getDefaultAction().mouseReleased(e); } return true; } @Override public boolean mouseDragged(MouseEvent e) { // if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) > 0 && valid) { if (e.getButton() == MouseEvent.BUTTON1 && valid) { Vector3d translate = getTranslate(e.getX(), e.getY(), prevTranslate); // System.out.println("translate " + translate); if (translate == null) return true; 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(); } return true; } @Override public boolean mouseWheelMoved(MouseWheelEvent e) { return getDefaultAction().mouseWheelMoved(e); } 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 boolean mouseMoved(MouseEvent e) { getDefaultAction().mouseMoved(e); return true; } 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; } }