]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/swt/TranslateAction.java
Indicate ongoing translation operations in UI
[simantics/3d.git] / org.simantics.g3d.vtk / src / org / simantics / g3d / vtk / swt / TranslateAction.java
1 /*******************************************************************************
2  * Copyright (c) 2012, 2013 Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g3d.vtk.swt;
13
14 import java.awt.event.KeyEvent;
15 import java.awt.event.MouseEvent;
16 import java.awt.event.MouseWheelEvent;
17 import java.math.BigDecimal;
18
19 import javax.vecmath.AxisAngle4d;
20 import javax.vecmath.Point3d;
21 import javax.vecmath.Quat4d;
22 import javax.vecmath.Vector3d;
23
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.events.SelectionAdapter;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.graphics.Cursor;
28 import org.eclipse.swt.widgets.Button;
29 import org.eclipse.swt.widgets.Combo;
30 import org.eclipse.swt.widgets.Display;
31 import org.eclipse.swt.widgets.Label;
32 import org.simantics.g3d.math.MathTools;
33 import org.simantics.g3d.math.Ray;
34 import org.simantics.g3d.scenegraph.IG3DNode;
35 import org.simantics.g3d.scenegraph.base.INode;
36 import org.simantics.g3d.scenegraph.structural.IStructuralNode;
37 import org.simantics.g3d.toolbar.ToolComposite;
38 import org.simantics.g3d.vtk.Activator;
39 import org.simantics.g3d.vtk.common.VTKNodeMap;
40 import org.simantics.g3d.vtk.gizmo.TranslateAxisGizmo;
41 import org.simantics.g3d.vtk.utils.vtkUtil;
42 import org.simantics.utils.threads.ThreadUtils;
43
44 import vtk.vtkProp;
45
46 public class TranslateAction extends vtkSwtAction {
47
48         public static final int X = 0;
49         public static final int Y = 1;
50         public static final int Z = 2;
51         public static final int XY = 3;
52         public static final int XZ = 4;
53         public static final int YZ = 5;
54         public static final int P = 6;
55
56         private VTKNodeMap<?, ? extends INode> nodeMap;
57         // private TranslateGizmo gizmo = new TranslateGizmo();
58         private TranslateAxisGizmo gizmo = new TranslateAxisGizmo();
59         protected IG3DNode node;
60
61         private Cursor activeCursor;// = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
62         private Cursor dragCursor;// = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
63
64         protected ToolComposite toolComposite;
65         protected Combo axisCombo;
66
67         public void setNode(IG3DNode node) {
68                 this.node = node;
69                 if ((node instanceof IStructuralNode) && ((IStructuralNode) node).isPartOfInstantiatedModel()
70                                 && !((IStructuralNode) node).isInstantiatedModelRoot()) {
71                         setEnabled(false);
72                 } else {
73                         setEnabled(true);
74                 }
75         }
76
77         public IG3DNode getNode() {
78                 return node;
79         }
80
81         public TranslateAction(InteractiveVtkComposite panel, VTKNodeMap<?, ? extends INode> nodeMap,
82                         ToolComposite toolComposite) {
83                 super(panel);
84                 setImageDescriptor(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/arrow_out.png"));
85                 setText("Translate");
86                 this.nodeMap = nodeMap;
87
88                 activeCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_HAND);
89                 dragCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_SIZEALL);
90                 this.toolComposite = toolComposite;
91         }
92
93         protected void createTools(ToolComposite toolComposite) {
94                 createMessage(toolComposite);
95                 createAxisSelection(toolComposite);
96                 createCloseButton(toolComposite);
97
98                 toolComposite.relayout();
99         }
100
101         protected void createMessage(ToolComposite toolComposite) {
102                 new Label(toolComposite, SWT.READ_ONLY).setText("Press ESC to close translation. ");
103         }
104
105         protected void createAxisSelection(ToolComposite toolComposite) {
106                 Label label = new Label(toolComposite, SWT.READ_ONLY);
107                 label.setText("Translate direction:");
108                 axisCombo = new Combo(toolComposite, SWT.READ_ONLY);
109                 axisCombo.add("X");
110                 axisCombo.add("Y");
111                 axisCombo.add("Z");
112                 axisCombo.add("XY");
113                 axisCombo.add("XZ");
114                 axisCombo.add("YZ");
115                 axisCombo.add("Camera");
116                 axisCombo.addSelectionListener(new SelectionAdapter() {
117                         @Override
118                         public void widgetSelected(SelectionEvent e) {
119                                 Combo c = (Combo) e.getSource();
120                                 index = c.getSelectionIndex();
121                                 updateLock();
122                                 panel.getComponent().setFocus();
123                         }
124                 });
125                 axisCombo.select(index);
126         }
127
128         protected void createCloseButton(ToolComposite toolComposite) {
129                 Button close = new Button(toolComposite, SWT.PUSH);
130                 close.setText("Close");
131                 close.addSelectionListener(new SelectionAdapter() {
132                         public void widgetSelected(SelectionEvent e) {
133                                 panel.useDefaultAction();
134                         };
135                 });
136         }
137
138         public void attach() {
139                 if (node == null)
140                         return;
141                 if (toolComposite != null) {
142                         createTools(toolComposite);
143                 }
144                 setDBUndo(false);
145                 super.attach();
146                 ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() {
147                         public void run() {
148                                 attachUI();
149                                 update();
150                         }
151                 });
152         }
153
154         public void deattach() {
155                 if (toolComposite != null) {
156                         toolComposite.clear();
157                         axisCombo = null;
158                 }
159                 setDBUndo(true);
160                 node = null;
161                 nodeMap.commit("Translate");
162                 deattachUI();
163                 super.deattach();
164                 panel.refresh();
165         }
166
167         private void attachUI() {
168                 panel.getComponent().setCursor(activeCursor);
169                 gizmo.attach(panel);
170         }
171
172         private void deattachUI() {
173                 panel.getComponent().setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_ARROW));
174                 gizmo.deattach();
175         }
176
177         @Override
178         public boolean keyPressed(KeyEvent e) {
179                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
180                         panel.useDefaultAction();
181                 if (valid)
182                         return true;
183                 if (e.getKeyCode() == KeyEvent.VK_X) {
184                         if (index != X)
185                                 index = X;
186                         else
187                                 index = P;
188                 }
189                 if (e.getKeyCode() == KeyEvent.VK_Y) {
190                         if (index != Y)
191                                 index = Y;
192                         else
193                                 index = P;
194                 }
195                 if (e.getKeyCode() == KeyEvent.VK_Z) {
196                         if (index != Z)
197                                 index = Z;
198                         else
199                                 index = P;
200                 }
201                 if (e.getKeyCode() == KeyEvent.VK_G) {
202                         worldCoord = !worldCoord;
203                 }
204
205                 updateLock();
206                 update();
207                 // panel.repaint();
208                 return true;
209         }
210
211         private void updateLock() {
212                 gizmo.setType(index);
213                 if (axisCombo != null)
214                         axisCombo.select(index);
215                 panel.refresh();
216         }
217
218         @Override
219         public boolean mouseClicked(MouseEvent e) {
220                 if (e.getClickCount() > 1) {
221                         if (isOverNode(e)) {
222                                 return true;
223                         } else {
224                                 panel.useDefaultAction();
225                         }
226                         // if(!gizmo.isPartOf(actor))
227                         // panel.useDefaultAction();
228                         return true;
229                 }
230                 return false;
231         }
232
233         private boolean isOverNode(MouseEvent e) {
234                 vtkProp picked[] = panel.pick(e.getX(), e.getY());
235                 if (picked != null) {
236                         for (int i = 0; i < picked.length; i++) {
237                                 if (node.equals(nodeMap.getNode(picked[i])))
238                                         return true;
239                         }
240                 }
241                 return false;
242         }
243
244         int index = P;
245         protected boolean valid = false;
246         private boolean worldCoord = true;
247         private AxisAngle4d aa = null;
248         private Quat4d q = null;
249
250         public void setWorldCoord(boolean b) {
251                 if (worldCoord == b)
252                         return;
253                 worldCoord = b;
254                 update();
255
256         }
257
258         protected void update() {
259                 if (node == null)
260                         return;
261                 if (worldCoord) {
262                         gizmo.setRotation(new AxisAngle4d());
263                         aa = null;
264                         q = null;
265                 } else {
266                         aa = new AxisAngle4d();
267                         aa.set(((IG3DNode) node.getParent()).getWorldOrientation());
268                         gizmo.setRotation(aa);
269                         q = new Quat4d();
270                         MathTools.getQuat(aa, q);
271                 }
272
273                 Vector3d nodePos = node.getWorldPosition();
274                 // System.out.println(nodePos);
275                 gizmo.setPosition(nodePos);
276
277                 Point3d camPos = new Point3d(panel.getRenderer().GetActiveCamera().GetPosition());
278                 Vector3d p = new Vector3d(nodePos);
279                 p.sub(camPos);
280
281                 if (q != null) {
282                         Quat4d qi = new Quat4d(q);
283                         qi.inverse();
284                         MathTools.rotate(q, p, p);
285                 }
286                 if (panel.getRenderer().GetActiveCamera().GetParallelProjection() == 0) {
287                         double distance = p.length();
288                         p.negate();
289                         double fov = panel.getRenderer().GetActiveCamera().GetViewAngle();
290                         float s = (float) (Math.sin(fov) * distance * 0.1);
291
292                         Vector3d scale = new Vector3d(1., 1., 1.);
293
294 //            if (p.x > 0.f)
295 //                scale.x = -1.;
296 //            if (p.y > 0.f)
297 //                scale.y = -1.;
298 //            if (p.z > 0.f)
299 //                scale.z = -1.;
300                         scale.scale(s);
301                         gizmo.setScale(scale);
302
303                 } else {
304                         Vector3d scale = new Vector3d(1.f, 1.f, 1.f);
305                         double s = panel.getRenderer().GetActiveCamera().GetParallelScale() / 5.;
306 //            if (p.x > 0.f)
307 //                scale.x = -1.;
308 //            if (p.y > 0.f)
309 //                scale.y = -1.;
310 //            if (p.z > 0.f)
311 //                scale.z = -1.;
312                         scale.scale(s);
313                         gizmo.setScale(scale);
314                 }
315
316                 // panel.Render();
317                 panel.refresh();
318         }
319
320         protected Vector3d prevTranslate = null;
321
322         @Override
323         public boolean mousePressed(MouseEvent e) {
324                 if (e.getButton() == MouseEvent.BUTTON1) {
325
326                         if (isOverNode(e)) {
327                                 prevTranslate = getTranslate(e.getX(), e.getY());
328                                 valid = true;
329                                 panel.getComponent().setCursor(dragCursor);
330                         } else {
331                                 valid = false;
332                                 getDefaultAction().mousePressed(e);
333                                 panel.getComponent().setCursor(activeCursor);
334                         }
335                 } else {
336                         getDefaultAction().mousePressed(e);
337                 }
338                 return true;
339                 // index = gizmo.getTranslateAxis(actor);
340                 // if (index == -1) {
341                 // valid = false;
342                 // panel.getDefaultAction().mousePressed(e);
343                 // return;
344                 // }
345                 // valid = true;
346                 // prevTranslate = getTranslate(e.getX(), e.getY());
347                 // System.out.println("start translate " + prevTranslate);
348         }
349
350         @Override
351         public boolean mouseReleased(MouseEvent e) {
352                 if (e.getButton() == MouseEvent.BUTTON1) {
353                         valid = false;
354                         prevTranslate = null;
355                         panel.getComponent().setCursor(activeCursor);
356                 } else {
357                         getDefaultAction().mouseReleased(e);
358                 }
359                 return true;
360         }
361
362         @Override
363         public boolean mouseDragged(MouseEvent e) {
364                 // if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) > 0 && valid) {
365                 if (e.getButton() == MouseEvent.BUTTON1 && valid) {
366
367                         Vector3d translate = getTranslate(e.getX(), e.getY(), prevTranslate);
368                         // System.out.println("translate " + translate);
369                         if (translate == null)
370                                 return true;
371                         boolean step = ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0);
372                         if (worldCoord) {
373                                 Vector3d pos = new Vector3d(node.getWorldPosition());
374                                 pos.add(translate);
375                                 pos = constaints(pos, step);
376                                 setWorldPos(pos);
377                         } else {
378                                 Vector3d pos = new Vector3d(node.getPosition());
379                                 pos.add(translate);
380                                 pos = constaints(pos, step);
381                                 setPos(pos);
382                         }
383                         // mapping.rangeModified(node);
384
385                         // nodeMap.modified(node);
386                         update();
387                 } else {
388                         getDefaultAction().mouseDragged(e);
389                         update();
390                 }
391                 return true;
392         }
393
394         @Override
395         public boolean mouseWheelMoved(MouseWheelEvent e) {
396                 return getDefaultAction().mouseWheelMoved(e);
397         }
398
399         protected void setPos(Vector3d pos) {
400                 node.setPosition(pos);
401         }
402
403         protected void setWorldPos(Vector3d pos) {
404                 node.setWorldPosition(pos);
405         }
406
407         private double istep = 10.0;
408         private int decimals = 2;
409
410         protected Vector3d constaints(Vector3d p, boolean step) {
411                 if (!step)
412                         return p;
413                 switch (index) {
414                 case X:
415                         p.x = Math.round(istep * p.x) / istep;
416                         BigDecimal bx = new BigDecimal(p.x);
417                         bx.setScale(decimals, BigDecimal.ROUND_HALF_UP);
418                         p.x = bx.doubleValue();
419                         break;
420                 case Y:
421                         p.y = Math.round(istep * p.y) / istep;
422                         BigDecimal by = new BigDecimal(p.y);
423                         by.setScale(decimals, BigDecimal.ROUND_HALF_UP);
424                         p.y = by.doubleValue();
425                         break;
426
427                 case Z:
428                         p.z = Math.round(istep * p.z) / istep;
429                         BigDecimal bz = new BigDecimal(p.z);
430                         bz.setScale(decimals, BigDecimal.ROUND_HALF_UP);
431                         p.z = bz.doubleValue();
432                         break;
433                 }
434                 return p;
435         }
436
437         @Override
438         public boolean mouseMoved(MouseEvent e) {
439                 getDefaultAction().mouseMoved(e);
440                 return true;
441         }
442
443         protected Vector3d getTranslate(double x, double y) {
444                 return getTranslate(x, y, new Vector3d());
445         }
446
447         protected Vector3d getTranslate(double x, double y, Vector3d offset) {
448                 Vector3d translate = new Vector3d();
449
450                 Ray ray = vtkUtil.createMouseRay(panel.getRenderer(), x, y);
451
452                 Vector3d p = node.getWorldPosition();
453                 Vector3d dir = null;
454
455                 switch (index) {
456                 case P:
457                         Vector3d normal = new Vector3d(panel.getRenderer().GetActiveCamera().GetDirectionOfProjection());
458                         if (!worldCoord) {
459                                 MathTools.rotate(q, normal, normal);
460                         }
461                         normal.normalize();
462                         double s[] = new double[1];
463                         Vector3d r = new Vector3d();
464                         if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) {
465                                 r.sub(p);
466                                 translate.x = r.x;
467                                 translate.y = r.y;
468                                 translate.z = r.z;
469                         }
470                         break;
471
472                 case X:
473                         dir = new Vector3d(1.0, 0.0, 0.0);
474                         if (!worldCoord)
475                                 MathTools.rotate(q, dir, dir);
476                         Vector3d i1 = new Vector3d();
477                         Vector3d i2 = new Vector3d();
478                         s = new double[2];
479                         MathTools.intersectStraightStraight(p, dir, ray.pos, ray.dir, i2, i1, s);
480                         translate.x = s[0];
481
482                         break;
483                 case Y:
484                         dir = new Vector3d(0.0, 1.0, 0.0);
485                         if (!worldCoord)
486                                 MathTools.rotate(q, dir, dir);
487                         i1 = new Vector3d();
488                         i2 = new Vector3d();
489                         s = new double[2];
490                         MathTools.intersectStraightStraight(p, dir, ray.pos, ray.dir, i2, i1, s);
491                         translate.y = s[0];
492                         break;
493                 case Z:
494                         dir = new Vector3d(0.0, 0.0, 1.0);
495                         if (!worldCoord)
496                                 MathTools.rotate(q, dir, dir);
497                         i1 = new Vector3d();
498                         i2 = new Vector3d();
499                         s = new double[2];
500                         MathTools.intersectStraightStraight(p, dir, ray.pos, ray.dir, i2, i1, s);
501                         translate.z = s[0];
502                         break;
503                 case XY:
504                         normal = new Vector3d(0.0, 0.0, 1.0);
505                         if (!worldCoord)
506                                 MathTools.rotate(q, normal, normal);
507                         r = new Vector3d();
508                         if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) {
509                                 r.sub(p);
510                                 translate.x = r.x;
511                                 translate.y = r.y;
512                         }
513                         break;
514                 case XZ:
515                         normal = new Vector3d(0.0, 1.0, 0.0);
516                         if (!worldCoord)
517                                 MathTools.rotate(q, normal, normal);
518                         r = new Vector3d();
519                         if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) {
520                                 r.sub(p);
521                                 translate.x = r.x;
522                                 translate.z = r.z;
523                         }
524                         break;
525                 case YZ:
526                         normal = new Vector3d(1.0, 0.0, 0.0);
527                         if (!worldCoord)
528                                 MathTools.rotate(q, normal, normal);
529                         r = new Vector3d();
530                         if (MathTools.intersectStraightPlane(ray.pos, ray.dir, p, normal, r)) {
531                                 r.sub(p);
532                                 translate.y = r.y;
533                                 translate.z = r.z;
534                         }
535                         break;
536                 default:
537
538                         return null;
539                 }
540                 translate.sub(offset);
541                 return translate;
542         }
543
544 }