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