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