Skip to content

Commit

Permalink
Add ability to disable interpolation on the timeline.
Browse files Browse the repository at this point in the history
Make redraws from property changes happen on a timer to stop spamming the invalidate visual.
  • Loading branch information
Lyeeedar committed Sep 9, 2016
1 parent 0342508 commit 141e3a9
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 115 deletions.
1 change: 1 addition & 0 deletions StructuredXmlEditor/Core.xmldef
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
<Attributes>
<String Name="Name" Default="Struct" ToolTip="The name to be written for this element." />
<String Name="TimeChild" Default="Time" SkipIfDefault="false" ToolTip="The name of the child containing the keyframe time." />
<Boolean Name="Interpolate" Default="true" ToolTip="Whether the timeline should show interpolated values" />
<Number Name="MinCount" Min="0" ToolTip="The minimum number of keyframesthis can have." />
<Number Name="MaxCount" Min="1" Default="99999999" ToolTip="The maximum number of keyframes this can have." />
<Boolean Name="SkipIfDefault" ToolTip="Don't write out this element if it is at the default value." Default="true" />
Expand Down
2 changes: 2 additions & 0 deletions StructuredXmlEditor/Definition/TimelineDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class TimelineDefinition : ComplexDataDefinition
public StructDefinition KeyframeDefinition { get; set; }
public int MinCount { get; set; }
public int MaxCount { get; set; }
public bool Interpolate { get; set; }

public NumberDefinition TimeDefinition { get; set; }

Expand Down Expand Up @@ -74,6 +75,7 @@ public override void Parse(XElement definition)
TimeChild = definition.Attribute("TimeChild").Value;
MinCount = TryParseInt(definition, "MinCount", 0);
MaxCount = TryParseInt(definition, "MaxCount", int.MaxValue);
Interpolate = TryParseBool(definition, "Interpolate", true);

var elements = definition.Elements();
KeyframeDefinition = LoadDefinition(elements.First()) as StructDefinition;
Expand Down
232 changes: 117 additions & 115 deletions StructuredXmlEditor/View/CustomControls/Timeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
using System.Globalization;
using System.Windows.Media.Imaging;
using System.Windows.Controls.Primitives;
using StructuredXmlEditor.Definition;
using System.Timers;

namespace StructuredXmlEditor.View
{
Expand Down Expand Up @@ -65,12 +67,30 @@ public Timeline()

InvalidateVisual();
};

redrawTimer = new Timer();
redrawTimer.Interval = 1.0 / 15.0;
redrawTimer.Elapsed += (e, args) =>
{
if (dirty)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() => { InvalidateVisual(); }));
dirty = false;
}
};
redrawTimer.Start();
}

//-----------------------------------------------------------------------
~Timeline()
{
redrawTimer.Stop();
}

//-----------------------------------------------------------------------
private void OnPropertyChange(object sender, EventArgs args)
{
InvalidateVisual();
dirty = true;
}

//-----------------------------------------------------------------------
Expand Down Expand Up @@ -107,68 +127,71 @@ protected override void OnRender(DrawingContext drawingContext)

var sortedKeyframes = TimelineItem.Children.OrderBy((e) => TimelineItem.GetKeyframeTime(e)).ToList();

// Draw the colour keyframes interpolated
var numColours = TimelineItem.NumColourData();
var linePad = 2;
var lineHeight = 5;
var bottomPad = (ActualHeight - (lineHeight * numColours + (numColours - 1) * linePad)) / 2;
for (int i = 0; i < numColours; i++)
if ((TimelineItem.Definition as TimelineDefinition).Interpolate)
{
var drawPos = bottomPad + (lineHeight + linePad) * i;

for (int ii = 0; ii < sortedKeyframes.Count - 1; ii++)
// Draw the colour keyframes interpolated
var numColours = TimelineItem.NumColourData();
var linePad = 2;
var lineHeight = 5;
var bottomPad = (ActualHeight - (lineHeight * numColours + (numColours - 1) * linePad)) / 2;
for (int i = 0; i < numColours; i++)
{
var thisKeyframe = sortedKeyframes[ii];
var nextKeyframe = sortedKeyframes[ii + 1];
var drawPos = bottomPad + (lineHeight + linePad) * i;

var thisCol = TimelineItem.GetColourData(thisKeyframe, i);
var nextCol = TimelineItem.GetColourData(nextKeyframe, i);
for (int ii = 0; ii < sortedKeyframes.Count - 1; ii++)
{
var thisKeyframe = sortedKeyframes[ii];
var nextKeyframe = sortedKeyframes[ii + 1];

var thisCol = TimelineItem.GetColourData(thisKeyframe, i);
var nextCol = TimelineItem.GetColourData(nextKeyframe, i);

var brush = new LinearGradientBrush(thisCol, nextCol, new Point(0, 0), new Point(1, 0));
var brush = new LinearGradientBrush(thisCol, nextCol, new Point(0, 0), new Point(1, 0));

var thisDrawPos = TimelineItem.GetKeyframeTime(thisKeyframe) * pixelsASecond + TimelineItem.LeftPad;
var nextDrawPos = TimelineItem.GetKeyframeTime(nextKeyframe) * pixelsASecond + TimelineItem.LeftPad;
var thisDrawPos = TimelineItem.GetKeyframeTime(thisKeyframe) * pixelsASecond + TimelineItem.LeftPad;
var nextDrawPos = TimelineItem.GetKeyframeTime(nextKeyframe) * pixelsASecond + TimelineItem.LeftPad;

drawingContext.DrawRectangle(brush, null, new Rect(thisDrawPos, drawPos, nextDrawPos - thisDrawPos, lineHeight));
drawingContext.DrawRectangle(brush, null, new Rect(thisDrawPos, drawPos, nextDrawPos - thisDrawPos, lineHeight));
}
}
}

// Draw the number keyframes interpolated
var numNumbers = TimelineItem.NumNumberData();
var min = float.MaxValue;
var max = -float.MaxValue;
for (int i = 0; i < numNumbers; i++)
{
foreach (var keyframe in sortedKeyframes)
// Draw the number keyframes interpolated
var numNumbers = TimelineItem.NumNumberData();
var min = float.MaxValue;
var max = -float.MaxValue;
for (int i = 0; i < numNumbers; i++)
{
var val = TimelineItem.GetNumberData(keyframe, i);
if (val < min) min = val;
if (val > max) max = val;
foreach (var keyframe in sortedKeyframes)
{
var val = TimelineItem.GetNumberData(keyframe, i);
if (val < min) min = val;
if (val > max) max = val;
}
}
}

for (int i = 0; i < numNumbers; i++)
{
var pen = NumberTrackColours[i];

for (int ii = 0; ii < sortedKeyframes.Count - 1; ii++)
for (int i = 0; i < numNumbers; i++)
{
var thisKeyframe = sortedKeyframes[ii];
var nextKeyframe = sortedKeyframes[ii + 1];
var pen = NumberTrackColours[i];

var thisNum = TimelineItem.GetNumberData(thisKeyframe, i);
var nextNum = TimelineItem.GetNumberData(nextKeyframe, i);
for (int ii = 0; ii < sortedKeyframes.Count - 1; ii++)
{
var thisKeyframe = sortedKeyframes[ii];
var nextKeyframe = sortedKeyframes[ii + 1];

var thisNum = TimelineItem.GetNumberData(thisKeyframe, i);
var nextNum = TimelineItem.GetNumberData(nextKeyframe, i);

var thisAlpha = (thisNum - min) / (max - min);
var nextAlpha = (nextNum - min) / (max - min);
var thisAlpha = (thisNum - min) / (max - min);
var nextAlpha = (nextNum - min) / (max - min);

var thisH = (ActualHeight - 20) - (ActualHeight - 25) * thisAlpha;
var nextH = (ActualHeight - 20) - (ActualHeight - 25) * nextAlpha;
var thisH = (ActualHeight - 20) - (ActualHeight - 25) * thisAlpha;
var nextH = (ActualHeight - 20) - (ActualHeight - 25) * nextAlpha;

var thisDrawPos = TimelineItem.GetKeyframeTime(thisKeyframe) * pixelsASecond + TimelineItem.LeftPad;
var nextDrawPos = TimelineItem.GetKeyframeTime(nextKeyframe) * pixelsASecond + TimelineItem.LeftPad;
var thisDrawPos = TimelineItem.GetKeyframeTime(thisKeyframe) * pixelsASecond + TimelineItem.LeftPad;
var nextDrawPos = TimelineItem.GetKeyframeTime(nextKeyframe) * pixelsASecond + TimelineItem.LeftPad;

drawingContext.DrawLine(pen, new Point(thisDrawPos, thisH), new Point(nextDrawPos, nextH));
drawingContext.DrawLine(pen, new Point(thisDrawPos, thisH), new Point(nextDrawPos, nextH));
}
}
}

Expand Down Expand Up @@ -308,6 +331,7 @@ protected override void OnPreviewMouseRightButtonDown(MouseButtonEventArgs e)
}

InvalidateVisual();
e.Handled = true;
}

//-----------------------------------------------------------------------
Expand Down Expand Up @@ -396,7 +420,6 @@ protected override void OnPreviewMouseUp(MouseButtonEventArgs args)
MenuItem delete = new MenuItem();
delete.Header = "Delete";
delete.Click += delegate { TimelineItem.Remove(selected); InvalidateVisual(); };
delete.InputGestureText = "Delete";
delete.IsEnabled = !TimelineItem.IsAtMin;
menu.Items.Add(delete);
}
Expand All @@ -420,63 +443,66 @@ protected override void OnPreviewMouseUp(MouseButtonEventArgs args)

TimelineItem.Children.Sort((e) => TimelineItem.GetKeyframeTime(e));

var index = TimelineItem.Children.IndexOf(item);
var prev = TimelineItem.Children.ElementAtOrDefault(index - 1);
var next = TimelineItem.Children.ElementAtOrDefault(index + 1);

if (prev == null && next == null)
if ((TimelineItem.Definition as TimelineDefinition).Interpolate)
{
var index = TimelineItem.Children.IndexOf(item);
var prev = TimelineItem.Children.ElementAtOrDefault(index - 1);
var next = TimelineItem.Children.ElementAtOrDefault(index + 1);

}
else if (prev == null)
{
for (int i = 0; i < TimelineItem.NumColourData(); i++)
if (prev == null && next == null)
{
TimelineItem.SetColourData(item, i, TimelineItem.GetColourData(next, i));

}
for (int i = 0; i < TimelineItem.NumNumberData(); i++)
else if (prev == null)
{
TimelineItem.SetNumberData(item, i, TimelineItem.GetNumberData(next, i));
for (int i = 0; i < TimelineItem.NumColourData(); i++)
{
TimelineItem.SetColourData(item, i, TimelineItem.GetColourData(next, i));
}
for (int i = 0; i < TimelineItem.NumNumberData(); i++)
{
TimelineItem.SetNumberData(item, i, TimelineItem.GetNumberData(next, i));
}
}
}
else if (next == null)
{
for (int i = 0; i < TimelineItem.NumColourData(); i++)
else if (next == null)
{
TimelineItem.SetColourData(item, i, TimelineItem.GetColourData(prev, i));
for (int i = 0; i < TimelineItem.NumColourData(); i++)
{
TimelineItem.SetColourData(item, i, TimelineItem.GetColourData(prev, i));
}
for (int i = 0; i < TimelineItem.NumNumberData(); i++)
{
TimelineItem.SetNumberData(item, i, TimelineItem.GetNumberData(prev, i));
}
}
for (int i = 0; i < TimelineItem.NumNumberData(); i++)
else
{
TimelineItem.SetNumberData(item, i, TimelineItem.GetNumberData(prev, i));
}
}
else
{
for (int i = 0; i < TimelineItem.NumColourData(); i++)
{
var prevVal = TimelineItem.GetColourData(prev, i);
var nextVal = TimelineItem.GetColourData(next, i);
for (int i = 0; i < TimelineItem.NumColourData(); i++)
{
var prevVal = TimelineItem.GetColourData(prev, i);
var nextVal = TimelineItem.GetColourData(next, i);

var prevTime = TimelineItem.GetKeyframeTime(prev);
var nextTime = TimelineItem.GetKeyframeTime(next);
var alpha = (TimelineItem.GetKeyframeTime(item) - prevTime) / (nextTime - prevTime);
var prevTime = TimelineItem.GetKeyframeTime(prev);
var nextTime = TimelineItem.GetKeyframeTime(next);
var alpha = (TimelineItem.GetKeyframeTime(item) - prevTime) / (nextTime - prevTime);

var col = prevVal.Lerp(nextVal, alpha);
var col = prevVal.Lerp(nextVal, alpha);

TimelineItem.SetColourData(item, i, col);
}
for (int i = 0; i < TimelineItem.NumNumberData(); i++)
{
var prevVal = TimelineItem.GetNumberData(prev, i);
var nextVal = TimelineItem.GetNumberData(next, i);
TimelineItem.SetColourData(item, i, col);
}
for (int i = 0; i < TimelineItem.NumNumberData(); i++)
{
var prevVal = TimelineItem.GetNumberData(prev, i);
var nextVal = TimelineItem.GetNumberData(next, i);

var prevTime = TimelineItem.GetKeyframeTime(prev);
var nextTime = TimelineItem.GetKeyframeTime(next);
var alpha = (TimelineItem.GetKeyframeTime(item) - prevTime) / (nextTime - prevTime);
var prevTime = TimelineItem.GetKeyframeTime(prev);
var nextTime = TimelineItem.GetKeyframeTime(next);
var alpha = (TimelineItem.GetKeyframeTime(item) - prevTime) / (nextTime - prevTime);

var val = prevVal + (nextVal - prevVal) * alpha;
var val = prevVal + (nextVal - prevVal) * alpha;

TimelineItem.SetNumberData(item, i, val);
TimelineItem.SetNumberData(item, i, val);
}
}
}
}
Expand All @@ -492,7 +518,6 @@ protected override void OnPreviewMouseUp(MouseButtonEventArgs args)
MenuItem zoom = new MenuItem();
zoom.Header = "Zoom To Best Fit";
zoom.Click += delegate { ZoomToBestFit(); };
zoom.InputGestureText = "=";
menu.Items.Add(zoom);

this.ContextMenu = menu;
Expand Down Expand Up @@ -530,8 +555,6 @@ protected override void OnPreviewMouseUp(MouseButtonEventArgs args)
}

EndDrag();

base.OnPreviewMouseUp(args);
}

//-----------------------------------------------------------------------
Expand Down Expand Up @@ -572,35 +595,14 @@ public void ZoomToBestFit()
InvalidateVisual();
}

//-----------------------------------------------------------------------
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);

if (e.Key == Key.Delete)
{
foreach (var item in TimelineItem.Children.ToList())
{
if (item.IsSelected)
{
TimelineItem.Remove(item);
}
}
}
else if (e.Key == Key.OemPlus)
{
ZoomToBestFit();
}

e.Handled = true;
}

//-----------------------------------------------------------------------
double startPos = 0;
double panPos = 0;
bool isDragging = false;
bool isPanning = false;
DataItem draggedItem;
DataItem mouseOverItem;
Timer redrawTimer;
bool dirty = false;
}
}

0 comments on commit 141e3a9

Please sign in to comment.