]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.proconf.g3d.shapeeditor/src/org/simantics/proconf/g3d/shapeeditor/tools/AnimationContribution.java
Moved 3D plug-ins to /3d/branches/dev.
[simantics/3d.git] / org.simantics.proconf.g3d.shapeeditor / src / org / simantics / proconf / g3d / shapeeditor / tools / AnimationContribution.java
diff --git a/org.simantics.proconf.g3d.shapeeditor/src/org/simantics/proconf/g3d/shapeeditor/tools/AnimationContribution.java b/org.simantics.proconf.g3d.shapeeditor/src/org/simantics/proconf/g3d/shapeeditor/tools/AnimationContribution.java
new file mode 100644 (file)
index 0000000..b81aa30
--- /dev/null
@@ -0,0 +1,774 @@
+package org.simantics.proconf.g3d.shapeeditor.tools;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.List;\r
+\r
+import javax.vecmath.AxisAngle4d;\r
+import javax.vecmath.Quat4d;\r
+\r
+import org.eclipse.jface.action.IMenuManager;\r
+import org.eclipse.jface.action.IToolBarManager;\r
+import org.eclipse.jface.dialogs.Dialog;\r
+import org.eclipse.jface.dialogs.InputDialog;\r
+import org.eclipse.jface.dialogs.MessageDialog;\r
+import org.eclipse.jface.viewers.ISelectionChangedListener;\r
+import org.eclipse.jface.viewers.SelectionChangedEvent;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.custom.CCombo;\r
+import org.eclipse.swt.events.FocusAdapter;\r
+import org.eclipse.swt.events.FocusEvent;\r
+import org.eclipse.swt.events.KeyAdapter;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.events.SelectionAdapter;\r
+import org.eclipse.swt.events.SelectionEvent;\r
+import org.eclipse.swt.layout.FillLayout;\r
+import org.eclipse.swt.layout.FormAttachment;\r
+import org.eclipse.swt.layout.FormData;\r
+import org.eclipse.swt.layout.FormLayout;\r
+import org.eclipse.swt.layout.GridData;\r
+import org.eclipse.swt.layout.GridLayout;\r
+import org.eclipse.swt.widgets.Button;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Slider;\r
+import org.eclipse.swt.widgets.Text;\r
+import org.simantics.db.ContextGraph;\r
+import org.simantics.db.Graph;\r
+import org.simantics.db.GraphRequestAdapter;\r
+import org.simantics.db.GraphRequestStatus;\r
+import org.simantics.db.GraphRequestWithResult;\r
+import org.simantics.db.Resource;\r
+import org.simantics.g2d.stubs.anim.Animation;\r
+import org.simantics.g2d.stubs.anim.Interpolator;\r
+import org.simantics.g2d.stubs.anim.ScalarInterpolator;\r
+import org.simantics.g2d.stubs.anim.SlerpInterpolator;\r
+import org.simantics.layer0.utils.EntityFactory;\r
+import org.simantics.layer0.utils.IEntity;\r
+import org.simantics.layer0.utils.Property;\r
+import org.simantics.proconf.animation.curve.SlerpCurve;\r
+import org.simantics.proconf.animation.curve.TCBCurve;\r
+import org.simantics.proconf.g3d.actions.ContextAction;\r
+import org.simantics.proconf.g3d.actions.RotateAction;\r
+import org.simantics.proconf.g3d.actions.TranslateAction;\r
+import org.simantics.proconf.g3d.animation.Animatable;\r
+import org.simantics.proconf.g3d.base.EditorContribution;\r
+import org.simantics.proconf.g3d.base.G3DTools;\r
+import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase;\r
+import org.simantics.proconf.g3d.base.VisualizationScheduler;\r
+import org.simantics.proconf.g3d.common.StructuredResourceSelection;\r
+import org.simantics.proconf.g3d.scenegraph.IGraphicsNode;\r
+import org.simantics.proconf.g3d.scenegraph.ISelectableNode;\r
+import org.simantics.proconf.g3d.shapeeditor.ShapeEditorResources;\r
+import org.simantics.proconf.g3d.shapeeditor.dialogs.PropertySelectionDialog;\r
+import org.simantics.proconf.g3d.shapeeditor.views.ShapeEditorBase;\r
+import org.simantics.proconf.g3d.stubs.G3DModel;\r
+import org.simantics.proconf.g3d.stubs.Orientation;\r
+import org.simantics.utils.ErrorLogger;\r
+\r
+public class AnimationContribution implements EditorContribution {\r
+       List<ContextAction> actions = new ArrayList<ContextAction>();\r
+       \r
+       private ShapeEditorBase parent;\r
+       \r
+       private final String NO_ANIMATION = "None";\r
+\r
+       private Slider timeSlider;\r
+       private double key = 0;\r
+       private Composite buttonComposite;\r
+       private Button insertKeyFrameButton;\r
+       private Button clearKeyFrameButton;\r
+       private CCombo animationCombo;\r
+       private Button addAnimationButton;\r
+       private Button removeAnimationButton;\r
+       private Button clearAnimationButton;\r
+       private Button animateButton;\r
+       private Button usePrecalculation;\r
+       private Text timeText;\r
+       private Composite infoComposite;\r
+       private Text infoText;\r
+\r
+       private Resource animationResource;\r
+       \r
+       private ContextAction translateAction;\r
+       private ContextAction rotateAction;\r
+       \r
+       public AnimationContribution(ThreeDimensionalEditorBase parent) {\r
+               this.parent = (ShapeEditorBase)parent;\r
+       }\r
+       \r
+       @Override\r
+       public String getName() {\r
+               return "Animator";\r
+       }\r
+       \r
+       @Override\r
+       public void createControl(Composite parent) {\r
+               FormLayout flayout = new FormLayout();\r
+               parent.setLayout(flayout);\r
+               infoComposite = new Composite(parent, SWT.BORDER);\r
+               FormData data = new FormData();\r
+               data.top = new FormAttachment(0, 0);\r
+               data.left = new FormAttachment(0, 0);\r
+               data.right = new FormAttachment(100, 0);\r
+               data.bottom = new FormAttachment(infoComposite, 0, SWT.TOP);\r
+               this.parent.getRenderingComposite().setLayoutData(data);\r
+\r
+               infoComposite.setLayout(new FillLayout(SWT.VERTICAL));\r
+               infoText = new Text(infoComposite, SWT.NONE);\r
+               GridLayout layout = new GridLayout(2, false);\r
+               layout.horizontalSpacing = 2;\r
+               layout.verticalSpacing = 2;\r
+               layout.marginWidth = 1;\r
+               layout.marginHeight = 1;\r
+               infoComposite.setLayout(layout);\r
+               GridData gdata = new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 2, 1);\r
+               // FIXME : allows text widget to take all available space (horizontal / width)\r
+               gdata.widthHint = 2000;\r
+               infoText.setLayoutData(gdata);\r
+               timeSlider = new Slider(infoComposite, SWT.NONE);\r
+               timeSlider.setValues(0, 0, 100, 1, 1, 1);\r
+               timeSlider.addSelectionListener(new SelectionAdapter() {\r
+                       @Override\r
+                       public void widgetSelected(SelectionEvent e) {\r
+                               key = ((double) timeSlider.getSelection()) / 100.0;\r
+                               updateTime();\r
+                       }\r
+               });\r
+               timeSlider.setLayoutData(new GridData(SWT.FILL, 1, true, false));\r
+               timeText = new Text(infoComposite, SWT.SINGLE);\r
+               timeText.addFocusListener(new FocusAdapter() {\r
+                       @Override\r
+                       public void focusLost(FocusEvent e) {\r
+                               updateTime();\r
+                       }\r
+               });\r
+               timeText.addKeyListener(new KeyAdapter() {\r
+                       @Override\r
+                       public void keyReleased(KeyEvent e) {\r
+                               if (e.keyCode == SWT.CR)\r
+                                       updateTime();\r
+                       }\r
+               });\r
+\r
+               buttonComposite = new Composite(infoComposite, SWT.NONE);\r
+               buttonComposite.setLayout(new FillLayout(SWT.HORIZONTAL));\r
+               buttonComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 2, 1));\r
+               insertKeyFrameButton = new Button(buttonComposite, SWT.PUSH);\r
+               insertKeyFrameButton.setText("Insert Keyframe");\r
+               insertKeyFrameButton.addSelectionListener(new SelectionAdapter() {\r
+                       public void widgetSelected(SelectionEvent e) {\r
+                               insertKeyFrame();\r
+                       }\r
+               });\r
+\r
+               clearKeyFrameButton = new Button(buttonComposite, SWT.PUSH);\r
+               clearKeyFrameButton.setText("Clear Keyframe");\r
+\r
+               animationCombo = new CCombo(buttonComposite, SWT.NONE);\r
+               animationCombo.add(NO_ANIMATION);\r
+               animationCombo.select(0);\r
+               animationCombo.setEditable(false);\r
+               animationCombo.addSelectionListener(new SelectionAdapter() {\r
+                       public void widgetSelected(SelectionEvent e) {\r
+                               selectAnimation();\r
+                       }\r
+               });\r
+\r
+               addAnimationButton = new Button(buttonComposite, SWT.PUSH);\r
+               addAnimationButton.setText("New Animation");\r
+               addAnimationButton.addSelectionListener(new SelectionAdapter() {\r
+                       public void widgetSelected(SelectionEvent e) {\r
+                               addAnimation();\r
+                       }\r
+               });\r
+\r
+               clearAnimationButton = new Button(buttonComposite, SWT.PUSH);\r
+               clearAnimationButton.setText("Clear Animation");\r
+               clearAnimationButton.addSelectionListener(new SelectionAdapter() {\r
+                       public void widgetSelected(SelectionEvent e) {\r
+                               clearAnimation();\r
+                       }\r
+               });\r
+\r
+               removeAnimationButton = new Button(buttonComposite, SWT.PUSH);\r
+               removeAnimationButton.setText("Remove Animation");\r
+               removeAnimationButton.addSelectionListener(new SelectionAdapter() {\r
+                       public void widgetSelected(SelectionEvent e) {\r
+                               removeAnimation();\r
+                       }\r
+               });\r
+\r
+               animateButton = new Button(buttonComposite, SWT.TOGGLE);\r
+               animateButton.setText("Animate");\r
+               animateButton.addSelectionListener(new SelectionAdapter() {\r
+                       public void widgetSelected(SelectionEvent e) {\r
+                               animate();\r
+                       }\r
+               });\r
+\r
+               usePrecalculation = new Button(buttonComposite, SWT.CHECK);\r
+               usePrecalculation.setText("Precalc");\r
+\r
+               data = new FormData();\r
+               data.left = new FormAttachment(0, 0);\r
+               data.right = new FormAttachment(100, 0);\r
+               data.bottom = new FormAttachment(100, 0);\r
+               // FIXME : take account font size\r
+               data.height = 20 * 3;\r
+               infoComposite.setLayoutData(data);\r
+\r
+               this.parent.getSelectionAdapter().addSelectionChangedListener(new ISelectionChangedListener() {\r
+                       public void selectionChanged(SelectionChangedEvent event) {\r
+                               updateUI();\r
+                       }\r
+               });\r
+               updateUI();\r
+               \r
+               this.parent.getSession().asyncRead(new GraphRequestAdapter() {\r
+                       @Override\r
+                       public GraphRequestStatus perform(Graph g) throws Exception {\r
+                               G3DModel m = new G3DModel(g,AnimationContribution.this.parent.getModelResource());\r
+                       Collection<Animation> animations = m.getAnimation();\r
+\r
+                       final List<String> animationNames = new ArrayList<String>();\r
+                       for (Animation a : animations) {\r
+                               animationNames.add(a.getName());\r
+                       }\r
+                       AnimationContribution.this.parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
+                               public void run() {\r
+                                       for (String s : animationNames)\r
+                                               animationCombo.add(s);\r
+                               }\r
+                       });\r
+                       return GraphRequestStatus.transactionComplete();\r
+                       }\r
+                       \r
+               });\r
+       }\r
+       \r
+       @Override\r
+       public void disposeControl() {\r
+               if (animateRunnable != null)\r
+               VisualizationScheduler.getInstance().removeVisualization(animateRunnable);\r
+       animateRunnable = null;\r
+       \r
+       infoComposite.dispose();\r
+\r
+       }\r
+       \r
+       @Override\r
+       public void fillContextMenu(Graph graph, IMenuManager manager, StructuredResourceSelection selection) {\r
+\r
+       }\r
+       \r
+       \r
+       \r
+       @Override\r
+       public Collection<ContextAction> getActions() {\r
+               return actions;\r
+       }\r
+       \r
+       @Override\r
+       public void initialize(Graph graph) {\r
+               actions.add(translateAction = new TranslateAction(parent) {\r
+                       @Override\r
+                       public void setInfoText(String text) {\r
+                               infoText.setText(text);\r
+                       }\r
+               });\r
+               actions.add(rotateAction = new RotateAction(parent){\r
+                       @Override\r
+                       public void setInfoText(String text) {\r
+                               infoText.setText(text);\r
+                       }\r
+               });\r
+       }\r
+       \r
+       private double getCurrentKey() {\r
+       return key;\r
+    }\r
+    \r
+    private void updateTime() {\r
+        final double t = getCurrentKey();\r
+        timeText.setText(Double.toString(t));\r
+        if (animationResource == null)\r
+            return;\r
+        if (usePrecalculation.getSelection()) {\r
+            for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) {\r
+                if (n instanceof Animatable) {\r
+                       // TODO : frame-rate dependent animations\r
+                    ((Animatable)n).animate(t,0.0);\r
+                }\r
+                parent.setViewChanged(true);\r
+            }    \r
+        } else {\r
+               parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
+                       @Override\r
+                       public GraphRequestStatus perform(Graph g) throws Exception {\r
+                               Collection<IEntity> interpolators = getAnimation(g).getRelatedObjects(ShapeEditorResources.animationResource.HasInterpolator);\r
+                    for (IEntity i : interpolators) {\r
+                        if (i.isInstanceOf(ShapeEditorResources.animationResource.ScalarInterpolator)) {\r
+                            // TODO : creating curve each time when time is set is\r
+                            // slow. Curve should be cached\r
+                            TCBCurve c = (TCBCurve) ShapeEditorResources.curveBuilder.loadInterpolator(i);\r
+                            double out = c.evaluate(t);\r
+                            g.setScalarDouble(i.getSingleRelatedObject(ShapeEditorResources.animationResource.HasTarget).getResource(), out);\r
+                        } else if (i.isInstanceOf(ShapeEditorResources.animationResource.SlerpInterpolator)) {\r
+                            // TODO : creating curve each time when time is set is slow.\r
+                            // Curve should be cached\r
+                            SlerpCurve c = (SlerpCurve) ShapeEditorResources.curveBuilder.loadInterpolator(i);\r
+                            Quat4d out = c.evaluate(t);\r
+                            Orientation r = new Orientation(i.getSingleRelatedObject(ShapeEditorResources.animationResource.HasTarget));\r
+                            AxisAngle4d aa = new AxisAngle4d();\r
+                            aa.set(out);\r
+                            G3DTools.setOrientation(r, aa);\r
+                        }\r
+                    }\r
+                               return GraphRequestStatus.transactionComplete();\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void requestCompleted(GraphRequestStatus status) {\r
+                               parent.getScenegraphAdapter().setChanged(true);\r
+                       }\r
+               });   \r
+        }\r
+    }\r
+    \r
+    private IEntity findPropertyInterpolator(Graph g, Resource property) {\r
+       Collection<IEntity> interpolators = getAnimation(g).getRelatedObjects(ShapeEditorResources.animationResource.HasInterpolator);\r
+        for (IEntity i : interpolators) {\r
+            IEntity e = i.getAtMostOneRelatedObject(ShapeEditorResources.animationResource.HasTarget);\r
+            if (e == null)\r
+                continue;\r
+            if (e.getResource().equals(property)) {\r
+                return i;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+    \r
+    private void insertKeyFrame() {\r
+       ArrayList<Resource> instances = new ArrayList<Resource>();\r
+       for (Resource rs : parent.getSelectionAdapter().getCurrentSelection().getSelectionList()) {\r
+               instances.add(rs);\r
+       }\r
+        PropertySelectionDialog dialog = new PropertySelectionDialog(parent.getRenderingComposite().getShell(),"Select property","Select animated property",parent.getSession(),instances);\r
+        if (dialog.open() == Dialog.CANCEL) {\r
+            return;\r
+        }\r
+        final List<Resource> properties = dialog.getSelectedPropertyInstances(); \r
+        parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
+               @Override\r
+               public GraphRequestStatus perform(Graph g) throws Exception {\r
+                       for (Resource r : properties) {\r
+                               IEntity t = EntityFactory.create(g,r);\r
+                    IEntity current = findPropertyInterpolator(g, r);\r
+                    if (t.isInstanceOf(ShapeEditorResources.g3dResource.Position)) {\r
+                        \r
+                        for (int i = 0; i < 3; i++) {\r
+                               IEntity d = null;\r
+                            switch (i) {\r
+                            case 0:\r
+                                d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasX);\r
+                                break;\r
+                            case 1:\r
+                               d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasY);\r
+                               break;\r
+                            case 2:\r
+                               d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasZ);\r
+                               break;\r
+                            }\r
+                            current = findPropertyInterpolator(g, d.getResource());\r
+                            addScalarKey(current,d);\r
+                        }\r
+                        \r
+                    } else if (t.isInstanceOf(ShapeEditorResources.g3dResource.Orientation)) {\r
+                        Orientation rot = new Orientation(t);\r
+                        addSlerpKey(current, rot);\r
+                    } else if (t.isInstanceOf(ShapeEditorResources.g3dResource.Color)) {\r
+                        for (int i = 0; i < 3; i++) {\r
+                            IEntity d = null;\r
+                               switch (i) {\r
+                            case 0:\r
+                               d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasRed);\r
+                                break;\r
+                            case 1:\r
+                                break;\r
+                            case 2:\r
+                                break;\r
+                            }\r
+                            current = findPropertyInterpolator(g, d.getResource());\r
+                               addScalarKey(current,d);\r
+                        }\r
+                    } else if (t.isInstanceOf(g.getBuiltins().Double)) {\r
+                       addScalarKey(current,t);\r
+                    } else {\r
+                        // TODO: basic cases are handled, only way to support\r
+                        // is to find all doubles from property structure and\r
+                        // interpolators attached to them, if there's any\r
+                        ErrorLogger.getDefault().logWarning("Default keyframe adding has not been implemented", null);\r
+                    }\r
+                }\r
+                       return GraphRequestStatus.transactionComplete();\r
+               }\r
+        });\r
+    }\r
+    \r
+    private void addScalarKey(IEntity current, IEntity d) {\r
+        \r
+       Graph graph = d.getGraph();\r
+       if (current == null) {\r
+            current = ScalarInterpolator.createDefault(graph).toInterpolator(); // FIXME : stubcast\r
+            getAnimation(graph).addStatement(ShapeEditorResources.animationResource.HasInterpolator, current);\r
+            current.addStatement(ShapeEditorResources.animationResource.HasTarget, d);\r
+        }\r
+        ShapeEditorResources.curveBuilder.addKey(current,getCurrentKey(),new double[]{d.getGraph().getScalarDouble(d.getResource()),0.0,0.0,0.0});\r
+    }\r
+    \r
+    private void addSlerpKey(IEntity current, Orientation r) {\r
+        Graph graph = r.getGraph();\r
+       if (current == null) {\r
+            current = SlerpInterpolator.createDefault(graph).toInterpolator(); // FIXME : stubcast\r
+            getAnimation(graph).addStatement(ShapeEditorResources.animationResource.HasInterpolator, current);\r
+            current.addStatement(ShapeEditorResources.animationResource.HasTarget, r);\r
+            \r
+        }\r
+        AxisAngle4d aa = G3DTools.getOrientation(r);\r
+        Quat4d q = new Quat4d();\r
+        q.set(aa);\r
+        ShapeEditorResources.curveBuilder.addKey(current,getCurrentKey(),new double[]{q.w,q.x,q.y,q.z});  \r
+    }\r
+    \r
+    private void selectAnimation() {\r
+        if (animationCombo.getSelectionIndex() == 0) {\r
+            animationResource = null;\r
+            updateUI();\r
+        } else {\r
+               final String name = animationCombo.getItem(animationCombo.getSelectionIndex());\r
+               parent.getSession().asyncRead(new GraphRequestAdapter() {\r
+                       @Override\r
+                       public GraphRequestStatus perform(Graph g) throws Exception {\r
+                               \r
+                    Resource modelResource = parent.getModelResource();\r
+                    G3DModel m = new G3DModel(g, modelResource);\r
+                    Collection<Animation> animations = m.getAnimation();\r
+                    boolean found = false;\r
+                    for (Animation a : animations) {\r
+                        if (a.getName().startsWith(name) && a.getName().length() == name.length()) {\r
+                            animationResource = a.getResource();\r
+                            found = true;\r
+                            break;\r
+                        }\r
+                    }\r
+                    if (!found) {\r
+                        ErrorLogger.defaultLogError("Could find animation " + name + " for model " + m.getResource(), null);\r
+                        animationResource = null;\r
+                    }\r
+                               return GraphRequestStatus.transactionComplete();\r
+                       }\r
+                       @Override\r
+                       public void requestCompleted(GraphRequestStatus status) {\r
+                               parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
+                                       @Override\r
+                                       public void run() {\r
+                                                updateUI();    \r
+                                       }\r
+                               });\r
+                       }\r
+               });\r
+        }       \r
+    }\r
+    \r
+    private Animation getAnimation(Graph graph) {\r
+       return new Animation(graph, animationResource);\r
+    }\r
+\r
+    private void addAnimation() {\r
+        InputDialog d = new InputDialog(parent.getRenderingComposite().getShell(),"Animation name","Animation name","",null);\r
+        if (d.open() == InputDialog.CANCEL) {\r
+            return;\r
+        }\r
+        final String name = d.getValue();\r
+        if (name == null || name.length() == 0) {\r
+            return;\r
+        }\r
+        parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
+               @Override\r
+               public GraphRequestStatus perform(Graph g) throws Exception {\r
+                       G3DModel m = parent.getModel(g);\r
+                       Collection<IEntity> animations = m.getRelatedObjects(ShapeEditorResources.animationResource.HasAnimation);\r
+                       for (IEntity a : animations) {\r
+                        if (a.getName().startsWith(name) && a.getName().length() == name.length()) {\r
+                        ErrorLogger.getDefault().logWarning("Cannot add animation with the same name " + name, null);\r
+                        return GraphRequestStatus.transactionCancel();\r
+                    }\r
+                }\r
+\r
+                Animation newAnimation = Animation.createDefault(g);\r
+                newAnimation.setName(name);\r
+                m.addStatement(ShapeEditorResources.animationResource.HasAnimation, newAnimation);\r
+                \r
+                       return GraphRequestStatus.transactionComplete();\r
+               }\r
+\r
+               @Override\r
+               public void requestCompleted(GraphRequestStatus status) {\r
+                       parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
+                               public void run() {\r
+                                       animationCombo.add(name);\r
+                        animationCombo.select(animationCombo.indexOf(name));\r
+                        selectAnimation();\r
+                               }\r
+                       });\r
+               }\r
+        });    \r
+    }\r
+    \r
+    private void removeAnimation() {\r
+        assert(animationResource != null);\r
+        GraphRequestWithResult<String> r = new GraphRequestWithResult<String>() {\r
+               public String performWithResult(Graph g) throws Exception {\r
+                       return getAnimation(g).getName();\r
+               };\r
+        };\r
+        parent.getSession().syncRead(r);\r
+        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
+        if (dialog.open() == 1)\r
+               return;\r
+        parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
+               String name;\r
+               @Override\r
+               public GraphRequestStatus perform(Graph g) throws Exception {\r
+                       \r
+                       Animation currentAnimation = getAnimation(g);\r
+                       name = currentAnimation.getName();\r
+                parent.getModel(g).removeStatement(ShapeEditorResources.animationResource.HasAnimation, currentAnimation);\r
+                \r
+                       return GraphRequestStatus.transactionComplete();\r
+               }\r
+               \r
+               @Override\r
+               public void requestCompleted(GraphRequestStatus status) {\r
+                       parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() {\r
+                               public void run() {\r
+                                       animationCombo.remove(animationCombo.indexOf(name));\r
+                               }\r
+                       });     \r
+               }\r
+        });\r
+    }\r
+    \r
+    private void clearAnimation() {\r
+        assert(animationResource != null);  \r
+        \r
+        parent.getSession().asyncWrite(new GraphRequestAdapter() {\r
+               \r
+               boolean proceed;\r
+               @Override\r
+               public GraphRequestStatus perform(Graph g) throws Exception {\r
+                       int size = getAnimation(g).getInterpolator().size();\r
+                       if (size == 0)\r
+                               return GraphRequestStatus.transactionCancel();\r
+                       final String name = getAnimation(g).getName();\r
+                       parent.getRenderingComposite().getDisplay().syncExec(new Runnable() {\r
+                               public void run() {\r
+                                       MessageDialog dialog = new MessageDialog(parent.getRenderingComposite().getShell(),"Confirm",null,"Do you want to clear animation " + name ,MessageDialog.QUESTION,new String[]{"Yes","No"},1);\r
+                           proceed = (dialog.open() != MessageDialog.CANCEL);\r
+                               }\r
+                       });\r
+                       if (proceed) {\r
+                               //getAnimation(g).getInterpolator().clear();\r
+                               getAnimation(g).removeRelatedStatements(ShapeEditorResources.animationResource.HasInterpolator);\r
+                               return GraphRequestStatus.transactionComplete();\r
+                       } else {\r
+                               return GraphRequestStatus.transactionCancel();\r
+                       }\r
+               }\r
+        });\r
+    }\r
\r
+    public Graph createAnimationParameterization(Graph g) {\r
+               if (g.getObjects(parent.getModelResource(), ShapeEditorResources.g3dResource.HasSizingParameter).size() > 0) {\r
+                       ContextGraph graph;\r
+                       if (!(g instanceof ContextGraph)) {\r
+                               graph = new ContextGraph(g);\r
+                               graph.setContext(parent.getModelResource());\r
+                       } else {\r
+                               graph = (ContextGraph)g;\r
+                       }\r
+                       Animation animation = getAnimation(graph);\r
+                       Collection<Interpolator> interpolators = animation.getInterpolator();\r
+                       for (org.simantics.g2d.stubs.anim.Interpolator interpolator : interpolators) {\r
+                       IEntity target = interpolator.getTarget();\r
+                       // check all model properties\r
+                       G3DModel model = parent.getModel(graph);\r
+                       Collection<Property> modelProperties = model.getRelatedProperties(ShapeEditorResources.g3dResource.HasSizingParameter);\r
+                       for (Property p : modelProperties) {\r
+                               IEntity t = EntityFactory.create(graph,p.getResource());\r
+                               // get parameterization equations\r
+                               Collection<IEntity> equations = t.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget);\r
+                               // get parameterized values\r
+                               Collection<IEntity> parameterTargets = new ArrayList<IEntity>();\r
+                               for (IEntity eq : equations) {\r
+                                       Collection<IEntity> tgts = eq.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget);\r
+                                       assert(tgts.size() == 1);\r
+                                       parameterTargets.add(tgts.iterator().next());\r
+                               }\r
+                               // do matching between interpolator targets and parameterized values\r
+                               // TODO : old system did not have inverse relations but current system does.\r
+                               //                it is possible to take interpolation target and find if it is connected to an equation\r
+                               //                this would make code much faster (no more stupid loops over everything)\r
+                               for (IEntity d : parameterTargets) {\r
+                                       if (d.getResource().equals(target.getResource())) {\r
+                                               // get default value for sizing property\r
+                                               Collection<IEntity> prop = t.getRelatedObjects(ShapeEditorResources.g3dResource.HasDefaultDoubleValue);\r
+                                               if (prop.size() == 1) {\r
+                                                       ShapeEditorResources.curveBuilder.parameterize(interpolator, prop.iterator().next().toProperty().getDoubleArray(), p.getDoubleArray());\r
+                                       } else {\r
+                                               ErrorLogger.defaultLogError("Cannot parameterize interpolator " + interpolator.getResource() + " of animation " + animation.getResource() + " since parameter " + p.getResource() + " has no default value", null);\r
+                                       }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+                       \r
+                       return graph;\r
+               } else {\r
+                       return g;\r
+               }\r
+       }\r
+    \r
+    private AnimateRunnable animateRunnable = null;\r
+    \r
+    private void animate() {\r
+        updateUI();\r
+        if (animateButton.getSelection()) {\r
+            if (animateRunnable != null)\r
+                return;\r
+            if (usePrecalculation.getSelection()) {\r
+               parent.getSession().asyncRead(new GraphRequestAdapter() {\r
+                       @Override\r
+                       public GraphRequestStatus perform(Graph g) throws Exception {\r
+                               Graph graph = parent.createParameterization(g);\r
+                               createAnimationParameterization(graph);\r
+                               for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) {\r
+                                       if (n instanceof ISelectableNode) {\r
+                                               if (!((ISelectableNode)n).isVisible())\r
+                                                       continue;\r
+                                       }\r
+                            if (n instanceof Animatable) {\r
+                                ((Animatable)n).setAnimation(graph,animationResource);\r
+                            }\r
+                        }\r
+                               return GraphRequestStatus.transactionComplete();\r
+                       }\r
+                       \r
+                       @Override\r
+                       public void requestCompleted(GraphRequestStatus status) {\r
+                               animateRunnable = new AnimateRunnable();\r
+                        VisualizationScheduler.getInstance().addVisualization(animateRunnable);\r
+                       }\r
+               });\r
+            } else {\r
+               animateRunnable = new AnimateRunnable();\r
+                VisualizationScheduler.getInstance().addVisualization(animateRunnable);\r
+            }\r
+            \r
+        } else {\r
+            if (animateRunnable == null)\r
+                return;       \r
+            VisualizationScheduler.getInstance().removeVisualization(animateRunnable);\r
+            animateRunnable = null;\r
+            if (usePrecalculation.getSelection()) {\r
+               // updateTime updates values to graph if precalculation is not used.\r
+               // we must store current values from animation to synchronize view and graph\r
+               // information so that use can modify animation properly.\r
+               usePrecalculation.setSelection(false);\r
+               updateTime();\r
+               usePrecalculation.setSelection(true);\r
+               for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) {\r
+                  if (n instanceof Animatable) {\r
+                      ((Animatable)n).setAnimation(null,null);\r
+                  }\r
+              }\r
+            }\r
+        }\r
+    }\r
+    \r
+    private void updateUI() {\r
+        if (animationResource != null) {\r
+            animateButton.setEnabled(true);\r
+            timeSlider.setEnabled(true);\r
+            if (animateButton.getSelection()) {\r
+                addAnimationButton.setEnabled(false);\r
+                insertKeyFrameButton.setEnabled(false);\r
+                removeAnimationButton.setEnabled(false);\r
+                clearKeyFrameButton.setEnabled(false);\r
+                animationCombo.setEnabled(false);\r
+                usePrecalculation.setEnabled(false);\r
+                clearAnimationButton.setEnabled(false);\r
+            } else {\r
+                addAnimationButton.setEnabled(true);\r
+                insertKeyFrameButton.setEnabled(!parent.getSelectionAdapter().getCurrentSelection().isEmpty());\r
+                removeAnimationButton.setEnabled(true);\r
+                clearKeyFrameButton.setEnabled(false); //FIXME : detect keyframes\r
+                animationCombo.setEnabled(true);\r
+                usePrecalculation.setEnabled(true);\r
+                clearAnimationButton.setEnabled(true);\r
+            }    \r
+        } else {\r
+            timeSlider.setEnabled(false);\r
+            addAnimationButton.setEnabled(true);\r
+            insertKeyFrameButton.setEnabled(false);\r
+            removeAnimationButton.setEnabled(false);\r
+            clearKeyFrameButton.setEnabled(false);\r
+            animateButton.setEnabled(false);\r
+            animateButton.setSelection(false);\r
+            animationCombo.setEnabled(true);\r
+            usePrecalculation.setEnabled(false);\r
+            clearAnimationButton.setEnabled(false);\r
+        }\r
+    }\r
+    \r
+    private void updateKey(double k) {\r
+       key = k;\r
+       if (key >= 1.0)\r
+               key = 0.0;\r
+       else if (key < 0.0)\r
+               key = 1.0;\r
+       timeSlider.setSelection((int)(key*100.0));\r
+    }\r
+    \r
+    private class AnimateRunnable implements Runnable {\r
+        public void run() {\r
+               try {\r
+                       updateKey(key + 0.01);\r
+                       updateTime();\r
+               } catch (Exception e) {\r
+                        VisualizationScheduler.getInstance().removeVisualization(animateRunnable);\r
+               }\r
+        }\r
+    }\r
+    \r
+    @Override\r
+    public void fillLocalToolBar(IToolBarManager manager) {\r
+\r
+    }\r
+    \r
+    @Override\r
+    public void fillLocalPullDown(IMenuManager manager) {\r
+       \r
+    }\r
+    \r
+    @Override\r
+    public void dispose() {\r
+       \r
+    }\r
+    \r
+    @Override\r
+    public void run() {\r
+\r
+    }\r
+\r
+}\r