]> gerrit.simantics Code Review - simantics/3d.git/blob
1b413c0a8de25bd544b1b88bddbb09b03addc79c
[simantics/3d.git] /
1 /*******************************************************************************\r
2  * Copyright (c) 2007- VTT Technical Research Centre of Finland.\r
3  * All rights reserved. This program and the accompanying materials\r
4  * are made available under the terms of the Eclipse Public License v1.0\r
5  * which accompanies this distribution, and is available at\r
6  * http://www.eclipse.org/legal/epl-v10.html\r
7  *\r
8  * Contributors:\r
9  *     VTT Technical Research Centre of Finland - initial API and implementation\r
10  *******************************************************************************/\r
11 package org.simantics.proconf.g3d.shapeeditor.tools;\r
12 \r
13 import java.util.ArrayList;\r
14 import java.util.Collection;\r
15 import java.util.List;\r
16 \r
17 import javax.vecmath.AxisAngle4d;\r
18 import javax.vecmath.Quat4d;\r
19 \r
20 import org.eclipse.jface.action.IMenuManager;\r
21 import org.eclipse.jface.action.IToolBarManager;\r
22 import org.eclipse.jface.dialogs.Dialog;\r
23 import org.eclipse.jface.dialogs.InputDialog;\r
24 import org.eclipse.jface.dialogs.MessageDialog;\r
25 import org.eclipse.jface.viewers.ISelectionChangedListener;\r
26 import org.eclipse.jface.viewers.SelectionChangedEvent;\r
27 import org.eclipse.swt.SWT;\r
28 import org.eclipse.swt.custom.CCombo;\r
29 import org.eclipse.swt.events.FocusAdapter;\r
30 import org.eclipse.swt.events.FocusEvent;\r
31 import org.eclipse.swt.events.KeyAdapter;\r
32 import org.eclipse.swt.events.KeyEvent;\r
33 import org.eclipse.swt.events.SelectionAdapter;\r
34 import org.eclipse.swt.events.SelectionEvent;\r
35 import org.eclipse.swt.layout.FillLayout;\r
36 import org.eclipse.swt.layout.FormAttachment;\r
37 import org.eclipse.swt.layout.FormData;\r
38 import org.eclipse.swt.layout.FormLayout;\r
39 import org.eclipse.swt.layout.GridData;\r
40 import org.eclipse.swt.layout.GridLayout;\r
41 import org.eclipse.swt.widgets.Button;\r
42 import org.eclipse.swt.widgets.Composite;\r
43 import org.eclipse.swt.widgets.Slider;\r
44 import org.eclipse.swt.widgets.Text;\r
45 import org.simantics.db.ContextGraph;\r
46 import org.simantics.db.Graph;\r
47 import org.simantics.db.GraphRequestAdapter;\r
48 import org.simantics.db.GraphRequestStatus;\r
49 import org.simantics.db.GraphRequestWithResult;\r
50 import org.simantics.db.Resource;\r
51 import org.simantics.animation.stubs.Animation;\r
52 import org.simantics.animation.stubs.Interpolator;\r
53 import org.simantics.animation.stubs.ScalarInterpolator;\r
54 import org.simantics.animation.stubs.SlerpInterpolator;\r
55 import org.simantics.layer0.utils.EntityFactory;\r
56 import org.simantics.layer0.utils.IEntity;\r
57 import org.simantics.layer0.utils.Property;\r
58 import org.simantics.animation.curve.SlerpCurve;\r
59 import org.simantics.animation.curve.TCBCurve;\r
60 import org.simantics.proconf.g3d.actions.ContextAction;\r
61 import org.simantics.proconf.g3d.actions.RotateAction;\r
62 import org.simantics.proconf.g3d.actions.TranslateAction;\r
63 import org.simantics.proconf.g3d.animation.Animatable;\r
64 import org.simantics.proconf.g3d.base.EditorContribution;\r
65 import org.simantics.proconf.g3d.base.G3DTools;\r
66 import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase;\r
67 import org.simantics.proconf.g3d.base.VisualizationScheduler;\r
68 import org.simantics.proconf.g3d.common.StructuredResourceSelection;\r
69 import org.simantics.proconf.g3d.scenegraph.IGraphicsNode;\r
70 import org.simantics.proconf.g3d.scenegraph.ISelectableNode;\r
71 import org.simantics.proconf.g3d.shapeeditor.ShapeEditorResources;\r
72 import org.simantics.proconf.g3d.shapeeditor.dialogs.PropertySelectionDialog;\r
73 import org.simantics.proconf.g3d.shapeeditor.views.ShapeEditorBase;\r
74 import org.simantics.proconf.g3d.stubs.G3DModel;\r
75 import org.simantics.proconf.g3d.stubs.Orientation;\r
76 import org.simantics.utils.ui.ErrorLogger;\r
77 \r
78 public class AnimationContribution implements EditorContribution {\r
79         List<ContextAction> actions = new ArrayList<ContextAction>();\r
80         \r
81         private ShapeEditorBase parent;\r
82         \r
83         private final String NO_ANIMATION = "None";\r
84 \r
85         private Slider timeSlider;\r
86         private double key = 0;\r
87         private Composite buttonComposite;\r
88         private Button insertKeyFrameButton;\r
89         private Button clearKeyFrameButton;\r
90         private CCombo animationCombo;\r
91         private Button addAnimationButton;\r
92         private Button removeAnimationButton;\r
93         private Button clearAnimationButton;\r
94         private Button animateButton;\r
95         private Button usePrecalculation;\r
96         private Text timeText;\r
97         private Composite infoComposite;\r
98         private Text infoText;\r
99 \r
100         private Resource animationResource;\r
101         \r
102         private ContextAction translateAction;\r
103         private ContextAction rotateAction;\r
104         \r
105         public AnimationContribution(ThreeDimensionalEditorBase parent) {\r
106                 this.parent = (ShapeEditorBase)parent;\r
107         }\r
108         \r
109         @Override\r
110         public String getName() {\r
111                 return "Animator";\r
112         }\r
113         \r
114         @Override\r
115         public void createControl(Composite parent) {\r
116                 FormLayout flayout = new FormLayout();\r
117                 parent.setLayout(flayout);\r
118                 infoComposite = new Composite(parent, SWT.BORDER);\r
119                 FormData data = new FormData();\r
120                 data.top = new FormAttachment(0, 0);\r
121                 data.left = new FormAttachment(0, 0);\r
122                 data.right = new FormAttachment(100, 0);\r
123                 data.bottom = new FormAttachment(infoComposite, 0, SWT.TOP);\r
124                 this.parent.getRenderingComposite().setLayoutData(data);\r
125 \r
126                 infoComposite.setLayout(new FillLayout(SWT.VERTICAL));\r
127                 infoText = new Text(infoComposite, SWT.NONE);\r
128                 GridLayout layout = new GridLayout(2, false);\r
129                 layout.horizontalSpacing = 2;\r
130                 layout.verticalSpacing = 2;\r
131                 layout.marginWidth = 1;\r
132                 layout.marginHeight = 1;\r
133                 infoComposite.setLayout(layout);\r
134                 GridData gdata = new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 2, 1);\r
135                 // FIXME : allows text widget to take all available space (horizontal / width)\r
136                 gdata.widthHint = 2000;\r
137                 infoText.setLayoutData(gdata);\r
138                 timeSlider = new Slider(infoComposite, SWT.NONE);\r
139                 timeSlider.setValues(0, 0, 100, 1, 1, 1);\r
140                 timeSlider.addSelectionListener(new SelectionAdapter() {\r
141                         @Override\r
142                         public void widgetSelected(SelectionEvent e) {\r
143                                 key = ((double) timeSlider.getSelection()) / 100.0;\r
144                                 updateTime();\r
145                         }\r
146                 });\r
147                 timeSlider.setLayoutData(new GridData(SWT.FILL, 1, true, false));\r
148                 timeText = new Text(infoComposite, SWT.SINGLE);\r
149                 timeText.addFocusListener(new FocusAdapter() {\r
150                         @Override\r
151                         public void focusLost(FocusEvent e) {\r
152                                 updateTime();\r
153                         }\r
154                 });\r
155                 timeText.addKeyListener(new KeyAdapter() {\r
156                         @Override\r
157                         public void keyReleased(KeyEvent e) {\r
158                                 if (e.keyCode == SWT.CR)\r
159                                         updateTime();\r
160                         }\r
161                 });\r
162 \r
163                 buttonComposite = new Composite(infoComposite, SWT.NONE);\r
164                 buttonComposite.setLayout(new FillLayout(SWT.HORIZONTAL));\r
165                 buttonComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 2, 1));\r
166                 insertKeyFrameButton = new Button(buttonComposite, SWT.PUSH);\r
167                 insertKeyFrameButton.setText("Insert Keyframe");\r
168                 insertKeyFrameButton.addSelectionListener(new SelectionAdapter() {\r
169                         public void widgetSelected(SelectionEvent e) {\r
170                                 insertKeyFrame();\r
171                         }\r
172                 });\r
173 \r
174                 clearKeyFrameButton = new Button(buttonComposite, SWT.PUSH);\r
175                 clearKeyFrameButton.setText("Clear Keyframe");\r
176 \r
177                 animationCombo = new CCombo(buttonComposite, SWT.NONE);\r
178                 animationCombo.add(NO_ANIMATION);\r
179                 animationCombo.select(0);\r
180                 animationCombo.setEditable(false);\r
181                 animationCombo.addSelectionListener(new SelectionAdapter() {\r
182                         public void widgetSelected(SelectionEvent e) {\r
183                                 selectAnimation();\r
184                         }\r
185                 });\r
186 \r
187                 addAnimationButton = new Button(buttonComposite, SWT.PUSH);\r
188                 addAnimationButton.setText("New Animation");\r
189                 addAnimationButton.addSelectionListener(new SelectionAdapter() {\r
190                         public void widgetSelected(SelectionEvent e) {\r
191                                 addAnimation();\r
192                         }\r
193                 });\r
194 \r
195                 clearAnimationButton = new Button(buttonComposite, SWT.PUSH);\r
196                 clearAnimationButton.setText("Clear Animation");\r
197                 clearAnimationButton.addSelectionListener(new SelectionAdapter() {\r
198                         public void widgetSelected(SelectionEvent e) {\r
199                                 clearAnimation();\r
200                         }\r
201                 });\r
202 \r
203                 removeAnimationButton = new Button(buttonComposite, SWT.PUSH);\r
204                 removeAnimationButton.setText("Remove Animation");\r
205                 removeAnimationButton.addSelectionListener(new SelectionAdapter() {\r
206                         public void widgetSelected(SelectionEvent e) {\r
207                                 removeAnimation();\r
208                         }\r
209                 });\r
210 \r
211                 animateButton = new Button(buttonComposite, SWT.TOGGLE);\r
212                 animateButton.setText("Animate");\r
213                 animateButton.addSelectionListener(new SelectionAdapter() {\r
214                         public void widgetSelected(SelectionEvent e) {\r
215                                 animate();\r
216                         }\r
217                 });\r
218 \r
219                 usePrecalculation = new Button(buttonComposite, SWT.CHECK);\r
220                 usePrecalculation.setText("Precalc");\r
221 \r
222                 data = new FormData();\r
223                 data.left = new FormAttachment(0, 0);\r
224                 data.right = new FormAttachment(100, 0);\r
225                 data.bottom = new FormAttachment(100, 0);\r
226                 // FIXME : take account font size\r
227                 data.height = 20 * 3;\r
228                 infoComposite.setLayoutData(data);\r
229 \r
230                 this.parent.getSelectionAdapter().addSelectionChangedListener(new ISelectionChangedListener() {\r
231                         public void selectionChanged(SelectionChangedEvent event) {\r
232                                 updateUI();\r
233                         }\r
234                 });\r
235                 updateUI();\r
236                 \r
237                 this.parent.getSession().asyncRead(new GraphRequestAdapter() {\r
238                         @Override\r
239                         public GraphRequestStatus perform(Graph g) throws Exception {\r
240                                 G3DModel m = new G3DModel(g,AnimationContribution.this.parent.getModelResource());\r
241                         Collection<Animation> animations = m.getAnimation();\r
242 \r
243                         final List<String> animationNames = new ArrayList<String>();\r
244                         for (Animation a : animations) {\r
245                                 animationNames.add(a.getName());\r
246                         }\r
247                         AnimationContribution.this.parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
248                                 public void run() {\r
249                                         for (String s : animationNames)\r
250                                                 animationCombo.add(s);\r
251                                 }\r
252                         });\r
253                         return GraphRequestStatus.transactionComplete();\r
254                         }\r
255                         \r
256                 });\r
257         }\r
258         \r
259         @Override\r
260         public void disposeControl() {\r
261                 if (animateRunnable != null)\r
262                 VisualizationScheduler.getInstance().removeVisualization(animateRunnable);\r
263         animateRunnable = null;\r
264         \r
265         infoComposite.dispose();\r
266 \r
267         }\r
268         \r
269         @Override\r
270         public void fillContextMenu(Graph graph, IMenuManager manager, StructuredResourceSelection selection) {\r
271 \r
272         }\r
273         \r
274         \r
275         \r
276         @Override\r
277         public Collection<ContextAction> getActions() {\r
278                 return actions;\r
279         }\r
280         \r
281         @Override\r
282         public void initialize(Graph graph) {\r
283                 actions.add(translateAction = new TranslateAction(parent) {\r
284                         @Override\r
285                         public void setInfoText(String text) {\r
286                                 infoText.setText(text);\r
287                         }\r
288                 });\r
289                 actions.add(rotateAction = new RotateAction(parent){\r
290                         @Override\r
291                         public void setInfoText(String text) {\r
292                                 infoText.setText(text);\r
293                         }\r
294                 });\r
295         }\r
296         \r
297         private double getCurrentKey() {\r
298         return key;\r
299     }\r
300     \r
301     private void updateTime() {\r
302         final double t = getCurrentKey();\r
303         timeText.setText(Double.toString(t));\r
304         if (animationResource == null)\r
305             return;\r
306         if (usePrecalculation.getSelection()) {\r
307             for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) {\r
308                 if (n instanceof Animatable) {\r
309                         // TODO : frame-rate dependent animations\r
310                     ((Animatable)n).animate(t,0.0);\r
311                 }\r
312                 parent.setViewChanged(true);\r
313             }    \r
314         } else {\r
315                 parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
316                         @Override\r
317                         public GraphRequestStatus perform(Graph g) throws Exception {\r
318                                 Collection<IEntity> interpolators = getAnimation(g).getRelatedObjects(ShapeEditorResources.animationResource.HasInterpolator);\r
319                     for (IEntity i : interpolators) {\r
320                         if (i.isInstanceOf(ShapeEditorResources.animationResource.ScalarInterpolator)) {\r
321                             // TODO : creating curve each time when time is set is\r
322                             // slow. Curve should be cached\r
323                             TCBCurve c = (TCBCurve) ShapeEditorResources.curveBuilder.loadInterpolator(i);\r
324                             double out = c.evaluate(t);\r
325                             g.setScalarDouble(i.getSingleRelatedObject(ShapeEditorResources.animationResource.HasTarget).getResource(), out);\r
326                         } else if (i.isInstanceOf(ShapeEditorResources.animationResource.SlerpInterpolator)) {\r
327                             // TODO : creating curve each time when time is set is slow.\r
328                             // Curve should be cached\r
329                             SlerpCurve c = (SlerpCurve) ShapeEditorResources.curveBuilder.loadInterpolator(i);\r
330                             Quat4d out = c.evaluate(t);\r
331                             Orientation r = new Orientation(i.getSingleRelatedObject(ShapeEditorResources.animationResource.HasTarget));\r
332                             AxisAngle4d aa = new AxisAngle4d();\r
333                             aa.set(out);\r
334                             G3DTools.setOrientation(r, aa);\r
335                         }\r
336                     }\r
337                                 return GraphRequestStatus.transactionComplete();\r
338                         }\r
339                         \r
340                         @Override\r
341                         public void requestCompleted(GraphRequestStatus status) {\r
342                                 parent.getScenegraphAdapter().setChanged(true);\r
343                         }\r
344                 });   \r
345         }\r
346     }\r
347     \r
348     private IEntity findPropertyInterpolator(Graph g, Resource property) {\r
349         Collection<IEntity> interpolators = getAnimation(g).getRelatedObjects(ShapeEditorResources.animationResource.HasInterpolator);\r
350         for (IEntity i : interpolators) {\r
351             IEntity e = i.getAtMostOneRelatedObject(ShapeEditorResources.animationResource.HasTarget);\r
352             if (e == null)\r
353                 continue;\r
354             if (e.getResource().equals(property)) {\r
355                 return i;\r
356             }\r
357         }\r
358         return null;\r
359     }\r
360     \r
361     private void insertKeyFrame() {\r
362         ArrayList<Resource> instances = new ArrayList<Resource>();\r
363         for (Resource rs : parent.getSelectionAdapter().getCurrentSelection().getSelectionList()) {\r
364                 instances.add(rs);\r
365         }\r
366         PropertySelectionDialog dialog = new PropertySelectionDialog(parent.getRenderingComposite().getShell(),"Select property","Select animated property",parent.getSession(),instances);\r
367         if (dialog.open() == Dialog.CANCEL) {\r
368             return;\r
369         }\r
370         final List<Resource> properties = dialog.getSelectedPropertyInstances(); \r
371         parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
372                 @Override\r
373                 public GraphRequestStatus perform(Graph g) throws Exception {\r
374                         for (Resource r : properties) {\r
375                                 IEntity t = EntityFactory.create(g,r);\r
376                     IEntity current = findPropertyInterpolator(g, r);\r
377                     if (t.isInstanceOf(ShapeEditorResources.g3dResource.Position)) {\r
378                         \r
379                         for (int i = 0; i < 3; i++) {\r
380                                 IEntity d = null;\r
381                             switch (i) {\r
382                             case 0:\r
383                                 d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasX);\r
384                                 break;\r
385                             case 1:\r
386                                 d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasY);\r
387                                 break;\r
388                             case 2:\r
389                                 d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasZ);\r
390                                 break;\r
391                             }\r
392                             current = findPropertyInterpolator(g, d.getResource());\r
393                             addScalarKey(current,d);\r
394                         }\r
395                         \r
396                     } else if (t.isInstanceOf(ShapeEditorResources.g3dResource.Orientation)) {\r
397                         Orientation rot = new Orientation(t);\r
398                         addSlerpKey(current, rot);\r
399                     } else if (t.isInstanceOf(ShapeEditorResources.g3dResource.Color)) {\r
400                         for (int i = 0; i < 3; i++) {\r
401                             IEntity d = null;\r
402                                 switch (i) {\r
403                             case 0:\r
404                                 d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasRed);\r
405                                 break;\r
406                             case 1:\r
407                                 break;\r
408                             case 2:\r
409                                 break;\r
410                             }\r
411                             current = findPropertyInterpolator(g, d.getResource());\r
412                                 addScalarKey(current,d);\r
413                         }\r
414                     } else if (t.isInstanceOf(g.getBuiltins().Double)) {\r
415                         addScalarKey(current,t);\r
416                     } else {\r
417                         // TODO: basic cases are handled, only way to support\r
418                         // is to find all doubles from property structure and\r
419                         // interpolators attached to them, if there's any\r
420                         ErrorLogger.getDefault().logWarning("Default keyframe adding has not been implemented", null);\r
421                     }\r
422                 }\r
423                         return GraphRequestStatus.transactionComplete();\r
424                 }\r
425         });\r
426     }\r
427     \r
428     private void addScalarKey(IEntity current, IEntity d) {\r
429         \r
430         Graph graph = d.getGraph();\r
431         if (current == null) {\r
432             current = ScalarInterpolator.createDefault(graph).toInterpolator(); // FIXME : stubcast\r
433             getAnimation(graph).addStatement(ShapeEditorResources.animationResource.HasInterpolator, current);\r
434             current.addStatement(ShapeEditorResources.animationResource.HasTarget, d);\r
435         }\r
436         ShapeEditorResources.curveBuilder.addKey(current,getCurrentKey(),new double[]{d.getGraph().getScalarDouble(d.getResource()),0.0,0.0,0.0});\r
437     }\r
438     \r
439     private void addSlerpKey(IEntity current, Orientation r) {\r
440         Graph graph = r.getGraph();\r
441         if (current == null) {\r
442             current = SlerpInterpolator.createDefault(graph).toInterpolator(); // FIXME : stubcast\r
443             getAnimation(graph).addStatement(ShapeEditorResources.animationResource.HasInterpolator, current);\r
444             current.addStatement(ShapeEditorResources.animationResource.HasTarget, r);\r
445             \r
446         }\r
447         AxisAngle4d aa = G3DTools.getOrientation(r);\r
448         Quat4d q = new Quat4d();\r
449         q.set(aa);\r
450         ShapeEditorResources.curveBuilder.addKey(current,getCurrentKey(),new double[]{q.w,q.x,q.y,q.z});  \r
451     }\r
452     \r
453     private void selectAnimation() {\r
454         if (animationCombo.getSelectionIndex() == 0) {\r
455             animationResource = null;\r
456             updateUI();\r
457         } else {\r
458                 final String name = animationCombo.getItem(animationCombo.getSelectionIndex());\r
459                 parent.getSession().asyncRead(new GraphRequestAdapter() {\r
460                         @Override\r
461                         public GraphRequestStatus perform(Graph g) throws Exception {\r
462                                 \r
463                     Resource modelResource = parent.getModelResource();\r
464                     G3DModel m = new G3DModel(g, modelResource);\r
465                     Collection<Animation> animations = m.getAnimation();\r
466                     boolean found = false;\r
467                     for (Animation a : animations) {\r
468                         if (a.getName().startsWith(name) && a.getName().length() == name.length()) {\r
469                             animationResource = a.getResource();\r
470                             found = true;\r
471                             break;\r
472                         }\r
473                     }\r
474                     if (!found) {\r
475                         ErrorLogger.defaultLogError("Could find animation " + name + " for model " + m.getResource(), null);\r
476                         animationResource = null;\r
477                     }\r
478                                 return GraphRequestStatus.transactionComplete();\r
479                         }\r
480                         @Override\r
481                         public void requestCompleted(GraphRequestStatus status) {\r
482                                 parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
483                                         @Override\r
484                                         public void run() {\r
485                                                  updateUI();    \r
486                                         }\r
487                                 });\r
488                         }\r
489                 });\r
490         }       \r
491     }\r
492     \r
493     private Animation getAnimation(Graph graph) {\r
494         return new Animation(graph, animationResource);\r
495     }\r
496 \r
497     private void addAnimation() {\r
498         InputDialog d = new InputDialog(parent.getRenderingComposite().getShell(),"Animation name","Animation name","",null);\r
499         if (d.open() == InputDialog.CANCEL) {\r
500             return;\r
501         }\r
502         final String name = d.getValue();\r
503         if (name == null || name.length() == 0) {\r
504             return;\r
505         }\r
506         parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
507                 @Override\r
508                 public GraphRequestStatus perform(Graph g) throws Exception {\r
509                         G3DModel m = parent.getModel(g);\r
510                         Collection<IEntity> animations = m.getRelatedObjects(ShapeEditorResources.animationResource.HasAnimation);\r
511                         for (IEntity a : animations) {\r
512                         if (a.getName().startsWith(name) && a.getName().length() == name.length()) {\r
513                         ErrorLogger.getDefault().logWarning("Cannot add animation with the same name " + name, null);\r
514                         return GraphRequestStatus.transactionCancel();\r
515                     }\r
516                 }\r
517 \r
518                 Animation newAnimation = Animation.createDefault(g);\r
519                 newAnimation.setName(name);\r
520                 m.addStatement(ShapeEditorResources.animationResource.HasAnimation, newAnimation);\r
521                 \r
522                         return GraphRequestStatus.transactionComplete();\r
523                 }\r
524 \r
525                 @Override\r
526                 public void requestCompleted(GraphRequestStatus status) {\r
527                         parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
528                                 public void run() {\r
529                                         animationCombo.add(name);\r
530                         animationCombo.select(animationCombo.indexOf(name));\r
531                         selectAnimation();\r
532                                 }\r
533                         });\r
534                 }\r
535         });    \r
536     }\r
537     \r
538     private void removeAnimation() {\r
539         assert(animationResource != null);\r
540         GraphRequestWithResult<String> r = new GraphRequestWithResult<String>() {\r
541                 public String performWithResult(Graph g) throws Exception {\r
542                         return getAnimation(g).getName();\r
543                 };\r
544         };\r
545         parent.getSession().syncRead(r);\r
546         MessageDialog dialog = new MessageDialog(parent.getRenderingComposite().getShell(),"Confirm",null,"Do you want to remove animation " + r.getResult() ,MessageDialog.QUESTION,new String[]{"Yes","No"},1);\r
547         if (dialog.open() == 1)\r
548                 return;\r
549         parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
550                 String name;\r
551                 @Override\r
552                 public GraphRequestStatus perform(Graph g) throws Exception {\r
553                         \r
554                         Animation currentAnimation = getAnimation(g);\r
555                         name = currentAnimation.getName();\r
556                 parent.getModel(g).removeStatement(ShapeEditorResources.animationResource.HasAnimation, currentAnimation);\r
557                 \r
558                         return GraphRequestStatus.transactionComplete();\r
559                 }\r
560                 \r
561                 @Override\r
562                 public void requestCompleted(GraphRequestStatus status) {\r
563                         parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
564                                 public void run() {\r
565                                         animationCombo.remove(animationCombo.indexOf(name));\r
566                                 }\r
567                         });     \r
568                 }\r
569         });\r
570     }\r
571     \r
572     private void clearAnimation() {\r
573         assert(animationResource != null);  \r
574         \r
575         parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
576                 \r
577                 boolean proceed;\r
578                 @Override\r
579                 public GraphRequestStatus perform(Graph g) throws Exception {\r
580                         int size = getAnimation(g).getInterpolator().size();\r
581                         if (size == 0)\r
582                                 return GraphRequestStatus.transactionCancel();\r
583                         final String name = getAnimation(g).getName();\r
584                         parent.getRenderingComposite().getDisplay().syncExec(new Runnable() {\r
585                                 public void run() {\r
586                                         MessageDialog dialog = new MessageDialog(parent.getRenderingComposite().getShell(),"Confirm",null,"Do you want to clear animation " + name ,MessageDialog.QUESTION,new String[]{"Yes","No"},1);\r
587                             proceed = (dialog.open() != MessageDialog.CANCEL);\r
588                                 }\r
589                         });\r
590                         if (proceed) {\r
591                                 //getAnimation(g).getInterpolator().clear();\r
592                                 getAnimation(g).removeRelatedStatements(ShapeEditorResources.animationResource.HasInterpolator);\r
593                                 return GraphRequestStatus.transactionComplete();\r
594                         } else {\r
595                                 return GraphRequestStatus.transactionCancel();\r
596                         }\r
597                 }\r
598         });\r
599     }\r
600  \r
601     public Graph createAnimationParameterization(Graph g) {\r
602                 if (g.getObjects(parent.getModelResource(), ShapeEditorResources.g3dResource.HasSizingParameter).size() > 0) {\r
603                         ContextGraph graph;\r
604                         if (!(g instanceof ContextGraph)) {\r
605                                 graph = new ContextGraph(g);\r
606                                 graph.setContext(parent.getModelResource());\r
607                         } else {\r
608                                 graph = (ContextGraph)g;\r
609                         }\r
610                         Animation animation = getAnimation(graph);\r
611                         Collection<Interpolator> interpolators = animation.getInterpolator();\r
612                         for (org.simantics.animation.stubs.Interpolator interpolator : interpolators) {\r
613                         IEntity target = interpolator.getTarget();\r
614                         // check all model properties\r
615                         G3DModel model = parent.getModel(graph);\r
616                         Collection<Property> modelProperties = model.getRelatedProperties(ShapeEditorResources.g3dResource.HasSizingParameter);\r
617                         for (Property p : modelProperties) {\r
618                                 IEntity t = EntityFactory.create(graph,p.getResource());\r
619                                 // get parameterization equations\r
620                                 Collection<IEntity> equations = t.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget);\r
621                                 // get parameterized values\r
622                                 Collection<IEntity> parameterTargets = new ArrayList<IEntity>();\r
623                                 for (IEntity eq : equations) {\r
624                                         Collection<IEntity> tgts = eq.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget);\r
625                                         assert(tgts.size() == 1);\r
626                                         parameterTargets.add(tgts.iterator().next());\r
627                                 }\r
628                                 // do matching between interpolator targets and parameterized values\r
629                                 // TODO : old system did not have inverse relations but current system does.\r
630                                 //                it is possible to take interpolation target and find if it is connected to an equation\r
631                                 //                this would make code much faster (no more stupid loops over everything)\r
632                                 for (IEntity d : parameterTargets) {\r
633                                         if (d.getResource().equals(target.getResource())) {\r
634                                                 // get default value for sizing property\r
635                                                 Collection<IEntity> prop = t.getRelatedObjects(ShapeEditorResources.g3dResource.HasDefaultDoubleValue);\r
636                                                 if (prop.size() == 1) {\r
637                                                         ShapeEditorResources.curveBuilder.parameterize(interpolator, prop.iterator().next().toProperty().getDoubleArray(), p.getDoubleArray());\r
638                                         } else {\r
639                                                 ErrorLogger.defaultLogError("Cannot parameterize interpolator " + interpolator.getResource() + " of animation " + animation.getResource() + " since parameter " + p.getResource() + " has no default value", null);\r
640                                         }\r
641                                         }\r
642                                 }\r
643                         }\r
644                 }\r
645                         \r
646                         return graph;\r
647                 } else {\r
648                         return g;\r
649                 }\r
650         }\r
651     \r
652     private AnimateRunnable animateRunnable = null;\r
653     \r
654     private void animate() {\r
655         updateUI();\r
656         if (animateButton.getSelection()) {\r
657             if (animateRunnable != null)\r
658                 return;\r
659             if (usePrecalculation.getSelection()) {\r
660                 parent.getSession().asyncRead(new GraphRequestAdapter() {\r
661                         @Override\r
662                         public GraphRequestStatus perform(Graph g) throws Exception {\r
663                                 Graph graph = parent.createParameterization(g);\r
664                                 createAnimationParameterization(graph);\r
665                                 for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) {\r
666                                         if (n instanceof ISelectableNode) {\r
667                                                 if (!((ISelectableNode)n).isVisible())\r
668                                                         continue;\r
669                                         }\r
670                             if (n instanceof Animatable) {\r
671                                 ((Animatable)n).setAnimation(graph,animationResource);\r
672                             }\r
673                         }\r
674                                 return GraphRequestStatus.transactionComplete();\r
675                         }\r
676                         \r
677                         @Override\r
678                         public void requestCompleted(GraphRequestStatus status) {\r
679                                 animateRunnable = new AnimateRunnable();\r
680                         VisualizationScheduler.getInstance().addVisualization(animateRunnable);\r
681                         }\r
682                 });\r
683             } else {\r
684                 animateRunnable = new AnimateRunnable();\r
685                 VisualizationScheduler.getInstance().addVisualization(animateRunnable);\r
686             }\r
687             \r
688         } else {\r
689             if (animateRunnable == null)\r
690                 return;       \r
691             VisualizationScheduler.getInstance().removeVisualization(animateRunnable);\r
692             animateRunnable = null;\r
693             if (usePrecalculation.getSelection()) {\r
694                 // updateTime updates values to graph if precalculation is not used.\r
695                 // we must store current values from animation to synchronize view and graph\r
696                 // information so that use can modify animation properly.\r
697                 usePrecalculation.setSelection(false);\r
698                 updateTime();\r
699                 usePrecalculation.setSelection(true);\r
700                 for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) {\r
701                   if (n instanceof Animatable) {\r
702                       ((Animatable)n).setAnimation(null,null);\r
703                   }\r
704               }\r
705             }\r
706         }\r
707     }\r
708     \r
709     private void updateUI() {\r
710         if (animationResource != null) {\r
711             animateButton.setEnabled(true);\r
712             timeSlider.setEnabled(true);\r
713             if (animateButton.getSelection()) {\r
714                 addAnimationButton.setEnabled(false);\r
715                 insertKeyFrameButton.setEnabled(false);\r
716                 removeAnimationButton.setEnabled(false);\r
717                 clearKeyFrameButton.setEnabled(false);\r
718                 animationCombo.setEnabled(false);\r
719                 usePrecalculation.setEnabled(false);\r
720                 clearAnimationButton.setEnabled(false);\r
721             } else {\r
722                 addAnimationButton.setEnabled(true);\r
723                 insertKeyFrameButton.setEnabled(!parent.getSelectionAdapter().getCurrentSelection().isEmpty());\r
724                 removeAnimationButton.setEnabled(true);\r
725                 clearKeyFrameButton.setEnabled(false); //FIXME : detect keyframes\r
726                 animationCombo.setEnabled(true);\r
727                 usePrecalculation.setEnabled(true);\r
728                 clearAnimationButton.setEnabled(true);\r
729             }    \r
730         } else {\r
731             timeSlider.setEnabled(false);\r
732             addAnimationButton.setEnabled(true);\r
733             insertKeyFrameButton.setEnabled(false);\r
734             removeAnimationButton.setEnabled(false);\r
735             clearKeyFrameButton.setEnabled(false);\r
736             animateButton.setEnabled(false);\r
737             animateButton.setSelection(false);\r
738             animationCombo.setEnabled(true);\r
739             usePrecalculation.setEnabled(false);\r
740             clearAnimationButton.setEnabled(false);\r
741         }\r
742     }\r
743     \r
744     private void updateKey(double k) {\r
745         key = k;\r
746         if (key >= 1.0)\r
747                 key = 0.0;\r
748         else if (key < 0.0)\r
749                 key = 1.0;\r
750         timeSlider.setSelection((int)(key*100.0));\r
751     }\r
752     \r
753     private class AnimateRunnable implements Runnable {\r
754         public void run() {\r
755                 try {\r
756                         updateKey(key + 0.01);\r
757                         updateTime();\r
758                 } catch (Exception e) {\r
759                          VisualizationScheduler.getInstance().removeVisualization(animateRunnable);\r
760                 }\r
761         }\r
762     }\r
763     \r
764     @Override\r
765     public void fillLocalToolBar(IToolBarManager manager) {\r
766 \r
767     }\r
768     \r
769     @Override\r
770     public void fillLocalPullDown(IMenuManager manager) {\r
771         \r
772     }\r
773     \r
774     @Override\r
775     public void dispose() {\r
776         \r
777     }\r
778     \r
779     @Override\r
780     public void run() {\r
781 \r
782     }\r
783 \r
784 }\r