1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
2020-08-15 17:53:12 +02:00

179 lines
8.0 KiB
C#

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Controls
{
public class PropertyTimelineHeader : FrameworkElement
{
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(nameof(Fill), typeof(Brush), typeof(PropertyTimelineHeader),
new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register(nameof(FontFamily), typeof(FontFamily), typeof(PropertyTimelineHeader),
new FrameworkPropertyMetadata(TextBlock.FontFamilyProperty.DefaultMetadata.DefaultValue, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty PixelsPerSecondProperty = DependencyProperty.Register(nameof(PixelsPerSecond), typeof(int), typeof(PropertyTimelineHeader),
new FrameworkPropertyMetadata(default(int), FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.Register(nameof(HorizontalOffset), typeof(double), typeof(PropertyTimelineHeader),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty VisibleWidthProperty = DependencyProperty.Register(nameof(VisibleWidth), typeof(double), typeof(PropertyTimelineHeader),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty OffsetFirstValueProperty = DependencyProperty.Register(nameof(OffsetFirstValue), typeof(bool), typeof(PropertyTimelineHeader),
new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.AffectsRender));
private double _subd1;
private double _subd2;
private double _subd3;
public Brush Fill
{
get => (Brush) GetValue(FillProperty);
set => SetValue(FillProperty, value);
}
public FontFamily FontFamily
{
get => (FontFamily) GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
public int PixelsPerSecond
{
get => (int) GetValue(PixelsPerSecondProperty);
set => SetValue(PixelsPerSecondProperty, value);
}
public double HorizontalOffset
{
get => (double) GetValue(HorizontalOffsetProperty);
set => SetValue(HorizontalOffsetProperty, value);
}
public double VisibleWidth
{
get => (double) GetValue(VisibleWidthProperty);
set => SetValue(VisibleWidthProperty, value);
}
public bool OffsetFirstValue
{
get => (bool) GetValue(OffsetFirstValueProperty);
set => SetValue(OffsetFirstValueProperty, value);
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
UpdateTimeScale();
var linePen = new Pen(Fill, 1);
var width = HorizontalOffset + VisibleWidth;
var frameStart = 0;
var units = PixelsPerSecond / _subd1;
var offsetUnits = frameStart * PixelsPerSecond % units;
// Labels
var count = (width + offsetUnits) / units;
for (var i = 0; i < count; i++)
{
var x = i * units - offsetUnits;
// Add a 100px margin to allow the text to partially render when needed
if (x < HorizontalOffset - 100 || x > HorizontalOffset + width)
continue;
var t = TimeSpan.FromSeconds((i * units - offsetUnits) / PixelsPerSecond + frameStart);
// 0.00 is always formatted as 0.00
if (t == TimeSpan.Zero)
RenderLabel(drawingContext, "0.00", x);
else if (PixelsPerSecond > 200)
RenderLabel(drawingContext, $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}", x);
else if (PixelsPerSecond > 60)
RenderLabel(drawingContext, $"{Math.Floor(t.TotalSeconds):00}.{t.Milliseconds:000}", x);
else
RenderLabel(drawingContext, $"{Math.Floor(t.TotalMinutes):0}:{t.Seconds:00}", x);
}
// Large ticks
units = PixelsPerSecond / _subd2;
count = (width + offsetUnits) / units;
for (var i = 0; i < count; i++)
{
var x = i * units - offsetUnits;
if (x == 0 && OffsetFirstValue)
drawingContext.DrawLine(linePen, new Point(1, 20), new Point(1, 30));
else if (x > HorizontalOffset && x < HorizontalOffset + width)
drawingContext.DrawLine(linePen, new Point(x, 20), new Point(x, 30));
}
// Small ticks
var mul = _subd3 / _subd2;
units = PixelsPerSecond / _subd3;
count = (width + offsetUnits) / units;
for (var i = 0; i < count; i++)
{
if (Math.Abs(i % mul) < 0.001) continue;
var x = i * units - offsetUnits;
if (x > HorizontalOffset && x < HorizontalOffset + width)
drawingContext.DrawLine(linePen, new Point(x, 25), new Point(x, 30));
}
}
private void RenderLabel(DrawingContext drawingContext, string text, double x)
{
var typeFace = new Typeface(FontFamily, new FontStyle(), new FontWeight(), new FontStretch());
var formattedText = new FormattedText(text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeFace, 9, Fill, null, VisualTreeHelper.GetDpi(this).PixelsPerDip);
if (x == 0 && OffsetFirstValue)
drawingContext.DrawText(formattedText, new Point(2, 2));
else
drawingContext.DrawText(formattedText, new Point(x - formattedText.Width / 2, 2));
}
private void UpdateTimeScale()
{
object[] subds;
if (PixelsPerSecond > 350)
subds = new object[] {12d, 12d, 60d};
else if (PixelsPerSecond > 250)
subds = new object[] {6d, 12d, 60d};
else if (PixelsPerSecond > 200)
subds = new object[] {6d, 6d, 30d};
else if (PixelsPerSecond > 150)
subds = new object[] {4d, 4d, 20d};
else if (PixelsPerSecond > 140)
subds = new object[] {4d, 4d, 20d};
else if (PixelsPerSecond > 90)
subds = new object[] {2d, 4d, 20d};
else if (PixelsPerSecond > 60)
subds = new object[] {2d, 4d, 8d};
else if (PixelsPerSecond > 40)
subds = new object[] {1d, 2d, 10d};
else if (PixelsPerSecond > 30)
subds = new object[] {1d, 2d, 10d};
else if (PixelsPerSecond > 10)
subds = new object[] {1d / 2d, 1d / 2d, 1d / 2d};
else if (PixelsPerSecond > 4)
subds = new object[] {1d / 5d, 1d / 5d, 1d / 5d};
else if (PixelsPerSecond > 3)
subds = new object[] {1d / 10d, 1d / 10d, 1d / 5d};
else if (PixelsPerSecond > 1)
subds = new object[] {1d / 20d, 1d / 20d, 1d / 10d};
else if (PixelsPerSecond >= 1)
subds = new object[] {1d / 30d, 1d / 30d, 1d / 15d};
else
{
// 1s per pixel
subds = new object[] {1d / 60d, 1d / 60d, 1d / 15d};
}
_subd1 = (double) subds[0]; // big ticks / labels
_subd2 = (double) subds[1]; // medium ticks
_subd3 = (double) subds[2]; // small ticks
}
}
}