A quick layout fix.
This commit is contained in:
parent
c23db4b5e8
commit
628427443f
8 changed files with 441 additions and 6 deletions
|
@ -30,6 +30,9 @@
|
|||
<Border Name="VideoBorder" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Grid.RowSpan="3" BorderBrush="Black" BorderThickness="1" Background="LightGray" Margin="5,5,0,0">
|
||||
<of:OverlayImage x:Name="video" />
|
||||
</Border>
|
||||
|
||||
|
||||
<local:AxesBorder NumVertGrid="7" MinVal="-1" MaxVal="1" MinHeight="180" Grid.Row="4" Grid.Column="0" Padding="60 20 30 40" RangeLabel="Head pose" Orientation="Horizontal">
|
||||
<local:TimeSeriesPlot ShowLegend="True" x:Name="headPosePlot" Orientation="Horizontal"/>
|
||||
</local:AxesBorder>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
|
|
@ -22,7 +22,6 @@ using CppInterop.LandmarkDetector;
|
|||
using CameraInterop;
|
||||
using FaceAnalyser_Interop;
|
||||
using System.Windows.Threading;
|
||||
using OpenFaceDemo.UI_items;
|
||||
|
||||
namespace OpenFaceDemo
|
||||
{
|
||||
|
@ -31,7 +30,7 @@ namespace OpenFaceDemo
|
|||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Members
|
||||
// -----------------------------------------------------------------
|
||||
|
|
|
@ -54,6 +54,9 @@
|
|||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>logo1.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
|
@ -75,9 +78,13 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="UI_items\AxesBorder.cs" />
|
||||
<Compile Include="UI_items\CameraSelection.xaml.cs">
|
||||
<DependentUpon>CameraSelection.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI_items\TimeSeriesPlot.xaml.cs">
|
||||
<DependentUpon>TimeSeriesPlot.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
@ -94,6 +101,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="UI_items\TimeSeriesPlot.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
|
@ -132,6 +143,9 @@
|
|||
<Name>CppInerop</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="logo1.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
|
|
180
gui/OpenFaceDemo/UI_items/AxesBorder.cs
Normal file
180
gui/OpenFaceDemo/UI_items/AxesBorder.cs
Normal file
|
@ -0,0 +1,180 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace OpenFaceDemo
|
||||
{
|
||||
class AxesBorder : Border
|
||||
{
|
||||
|
||||
public double MinVal { get; set; }
|
||||
public double MaxVal { get; set; }
|
||||
public int NumVertGrid { get; set; }
|
||||
|
||||
public bool ShowXLabel { get; set; }
|
||||
public bool ShowYLabel { get; set; }
|
||||
|
||||
public string RangeLabel { get; set; }
|
||||
|
||||
public bool XTicks { get; set; }
|
||||
|
||||
public Orientation Orientation { get; set; }
|
||||
|
||||
public AxesBorder()
|
||||
{
|
||||
MinVal = -1;
|
||||
MaxVal = 1;
|
||||
NumVertGrid = 5;
|
||||
ShowXLabel = true;
|
||||
ShowYLabel = true;
|
||||
XTicks = true;
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc)
|
||||
{
|
||||
base.OnRender(dc);
|
||||
|
||||
if (Orientation == System.Windows.Controls.Orientation.Horizontal)
|
||||
RenderHorizontal(dc);
|
||||
else
|
||||
RenderVertical(dc);
|
||||
}
|
||||
|
||||
private void RenderHorizontal(DrawingContext dc)
|
||||
{
|
||||
Pen p = new Pen(Brushes.Black, 1);
|
||||
Pen q = new Pen(Brushes.LightGray, 1);
|
||||
|
||||
double padLeft = Padding.Left;
|
||||
double padBottom = Padding.Bottom - 2 + 10;
|
||||
double padTop = Padding.Top;
|
||||
double padRight = Padding.Right;
|
||||
|
||||
// Draw horizontal gridlines
|
||||
|
||||
double step_size = (MaxVal - MinVal)/(NumVertGrid - 1.0);
|
||||
|
||||
for (int i = 0; i < NumVertGrid; i++)
|
||||
{
|
||||
double y = (int)(padTop + ((NumVertGrid - 1.0) - i) * ((ActualHeight - padBottom - padTop) / (NumVertGrid - 1.0))) - 0.5;
|
||||
|
||||
double y_val = MinVal + i * step_size;
|
||||
|
||||
if (y_val != 0)
|
||||
dc.DrawLine(q, new Point(padLeft, y), new Point(ActualWidth - padRight, y));
|
||||
else
|
||||
dc.DrawLine(p, new Point(padLeft, y), new Point(ActualWidth - padRight, y));
|
||||
|
||||
dc.DrawLine(p, new Point(padLeft - 10, y), new Point(padLeft, y));
|
||||
|
||||
//var t = FT((i / 2.0 - 1).ToString("0.0"), 10);
|
||||
var t = FT((MinVal + i * step_size).ToString("0.0"), 10);
|
||||
dc.DrawText(t, new Point(padLeft - t.Width - 12, y - t.Height / 2));
|
||||
}
|
||||
|
||||
// Draw vertical gridlines
|
||||
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
double x = (int)(padLeft + (10 - i) * ((ActualWidth - padLeft - padRight) / 10.0)) - 0.5;
|
||||
if (i < 10)
|
||||
dc.DrawLine(q, new Point(x, ActualHeight - padBottom), new Point(x, padTop));
|
||||
dc.DrawLine(p, new Point(x, ActualHeight - padBottom + 10), new Point(x, ActualHeight - padBottom));
|
||||
|
||||
if(XTicks)
|
||||
{
|
||||
var t = FT(i.ToString(), 10);
|
||||
dc.DrawText(t, new Point(x - t.Width / 2, ActualHeight - padBottom + t.Height));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw y axis
|
||||
|
||||
dc.DrawLine(p, new Point(((int)padLeft) - 0.5, padTop), new Point(((int)padLeft) - 0.5, ActualHeight - padBottom));
|
||||
|
||||
// Draw x axis
|
||||
//dc.DrawLine(p, new Point(padLeft, ((int)((ActualHeight - padBottom - padTop) / 2 + padTop)) - 0.5), new Point(ActualWidth - padRight, ((int)((ActualHeight - padBottom - padTop) / 2 + padTop)) - 0.5));
|
||||
|
||||
// Draw x axis label
|
||||
if(ShowXLabel)
|
||||
{
|
||||
FormattedText ft = FT("History (seconds)", 20);
|
||||
dc.DrawText(ft, new Point(padLeft + (ActualWidth - padLeft - padRight) / 2 - ft.Width / 2, ActualHeight - ft.Height));
|
||||
}
|
||||
|
||||
// Draw y axis label
|
||||
if(ShowYLabel)
|
||||
{
|
||||
FormattedText ft = FT(RangeLabel, 20);
|
||||
dc.PushTransform(new RotateTransform(-90));
|
||||
dc.DrawText(ft, new Point(-ft.Width - ActualHeight / 2 + ft.Width / 2, 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderVertical(DrawingContext dc)
|
||||
{
|
||||
Pen p = new Pen(Brushes.Black, 1);
|
||||
Pen q = new Pen(Brushes.LightGray, 1);
|
||||
|
||||
double padLeft = Padding.Left;
|
||||
double padBottom = Padding.Bottom - 2 + 10;
|
||||
double padTop = Padding.Top;
|
||||
double padRight = Padding.Right;
|
||||
|
||||
// Draw horizontal gridlines
|
||||
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
double y = (int)(padTop + (10 - i) * ((ActualHeight - padBottom - padTop) / 10.0)) - 0.5;
|
||||
if (i > 0)
|
||||
dc.DrawLine(q, new Point(padLeft, y), new Point(ActualWidth - padRight, y));
|
||||
dc.DrawLine(p, new Point(padLeft - 10, y), new Point(padLeft, y));
|
||||
var t = FT(i.ToString(), 10);
|
||||
dc.DrawText(t, new Point(padLeft - t.Width - 12, y - t.Height / 2));
|
||||
}
|
||||
|
||||
// Draw vertical gridlines
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
double x = (int)(padLeft + (4 - i) * ((ActualWidth - padLeft - padRight) / 4.0)) - 0.5;
|
||||
if (i < 10)
|
||||
dc.DrawLine(q, new Point(x, ActualHeight - padBottom), new Point(x, padTop));
|
||||
dc.DrawLine(p, new Point(x, ActualHeight - padBottom + 10), new Point(x, ActualHeight - padBottom));
|
||||
|
||||
var t = FT(((4-i) / 2.0 - 1).ToString("0.0"), 10);
|
||||
dc.DrawText(t, new Point(x - t.Width / 2, ActualHeight - padBottom + t.Height));
|
||||
}
|
||||
|
||||
// Draw y axis
|
||||
|
||||
dc.DrawLine(p, new Point(((int)((ActualWidth - padRight - padLeft) / 2 + padLeft)) - 0.5, padTop), new Point(((int)((ActualWidth - padRight - padLeft) / 2 + padLeft)) - 0.5, ActualHeight - padBottom));
|
||||
|
||||
// Draw x axis
|
||||
dc.DrawLine(p, new Point(padLeft, ((int)((ActualHeight - padBottom))) - 0.5), new Point(ActualWidth - padRight, ((int)((ActualHeight - padBottom))) - 0.5));
|
||||
|
||||
// Draw x axis label
|
||||
|
||||
FormattedText ft = FT(RangeLabel, 20);
|
||||
dc.DrawText(ft, new Point(padLeft + (ActualWidth - padLeft - padRight) / 2 - ft.Width / 2, ActualHeight - ft.Height));
|
||||
|
||||
// Draw y axis label
|
||||
|
||||
ft = FT("History (seconds)", 20);
|
||||
dc.PushTransform(new RotateTransform(-90));
|
||||
dc.DrawText(ft, new Point(-ft.Width - ActualHeight / 2 + ft.Width / 2, 0));
|
||||
}
|
||||
|
||||
private FormattedText FT(string text, int size)
|
||||
{
|
||||
return new FormattedText(text, CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Verdana"), size, Brushes.Black);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
<Window x:Class="OpenFaceDemo.UI_items.CameraSelection"
|
||||
<Window x:Class="OpenFaceDemo.CameraSelection"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:OpenFaceDemo.UI_items"
|
||||
xmlns:local="clr-namespace:OpenFaceDemo"
|
||||
mc:Ignorable="d"
|
||||
Title="CameraSelection" Height="460" Width="600" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Closing="Window_Closing">
|
||||
<Grid>
|
||||
|
|
|
@ -16,7 +16,7 @@ using CppInterop;
|
|||
using System.Windows.Threading;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenFaceDemo.UI_items
|
||||
namespace OpenFaceDemo
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for CameraSelection.xaml
|
||||
|
|
8
gui/OpenFaceDemo/UI_items/TimeSeriesPlot.xaml
Normal file
8
gui/OpenFaceDemo/UI_items/TimeSeriesPlot.xaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<UserControl x:Class="OpenFaceDemo.TimeSeriesPlot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="200" d:DesignWidth="900">
|
||||
</UserControl>
|
231
gui/OpenFaceDemo/UI_items/TimeSeriesPlot.xaml.cs
Normal file
231
gui/OpenFaceDemo/UI_items/TimeSeriesPlot.xaml.cs
Normal file
|
@ -0,0 +1,231 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace OpenFaceDemo
|
||||
{
|
||||
public class DataPoint
|
||||
{
|
||||
public DataPoint()
|
||||
{
|
||||
Time = TimeSeriesPlot.CurrentTime;
|
||||
|
||||
}
|
||||
public DateTime Time { get; set; }
|
||||
|
||||
public Dictionary<int, double> values = new Dictionary<int, double>();
|
||||
|
||||
public double Confidence { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Interaction logic for TimeSeriesPlot.xaml
|
||||
/// </summary>
|
||||
public partial class TimeSeriesPlot : UserControl
|
||||
{
|
||||
|
||||
#region High-Resolution Timing
|
||||
static DateTime startTime;
|
||||
static Stopwatch sw = new Stopwatch();
|
||||
static TimeSeriesPlot()
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
sw.Start();
|
||||
}
|
||||
public static DateTime CurrentTime
|
||||
{
|
||||
get { return startTime + sw.Elapsed; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public Orientation Orientation { get; set; }
|
||||
|
||||
public bool ShowLegend { get; set; }
|
||||
|
||||
Queue<DataPoint> dataPoints = new Queue<DataPoint>();
|
||||
TimeSpan historyLength = TimeSpan.FromSeconds(10);
|
||||
Dictionary<int, Brush> brushes = new Dictionary<int, Brush>();
|
||||
Dictionary<int, int> brush_thicknesses = new Dictionary<int, int>();
|
||||
Dictionary<int, String> line_names = new Dictionary<int, String>();
|
||||
Dictionary<int, Color> brush_colors = new Dictionary<int, Color>();
|
||||
|
||||
public TimeSeriesPlot()
|
||||
{
|
||||
InitializeComponent();
|
||||
ShowLegend = false;
|
||||
ClipToBounds = true;
|
||||
DispatcherTimer dt = new DispatcherTimer(TimeSpan.FromMilliseconds(20), DispatcherPriority.Background, Timer_Tick, Dispatcher);
|
||||
}
|
||||
|
||||
private void PruneData()
|
||||
{
|
||||
lock (dataPoints)
|
||||
{
|
||||
while (dataPoints.Count > 0 && dataPoints.Peek().Time < CurrentTime - historyLength - TimeSpan.FromSeconds(2))
|
||||
dataPoints.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDataPoint(DataPoint dp)
|
||||
{
|
||||
lock (dataPoints)
|
||||
dataPoints.Enqueue(dp);
|
||||
}
|
||||
|
||||
private void Timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
PruneData();
|
||||
|
||||
if (this.IsVisible)
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private FormattedText FT(string text, int size)
|
||||
{
|
||||
return new FormattedText(text, System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Verdana"), size, Brushes.Black);
|
||||
}
|
||||
|
||||
public void AssocColor(int seriesId, Color b)
|
||||
{
|
||||
Color bTransparent = b;
|
||||
bTransparent.A = 0;
|
||||
|
||||
GradientStopCollection gs = new GradientStopCollection();
|
||||
gs.Add(new GradientStop(bTransparent, 0));
|
||||
gs.Add(new GradientStop(b, 0.2));
|
||||
LinearGradientBrush g = new LinearGradientBrush(gs, new Point(0, 0), Orientation == System.Windows.Controls.Orientation.Horizontal ? new Point(ActualWidth, 0) : new Point(0, ActualHeight));
|
||||
g.MappingMode = BrushMappingMode.Absolute;
|
||||
g.Freeze();
|
||||
brushes[seriesId] = g;
|
||||
|
||||
brush_colors[seriesId] = b;
|
||||
}
|
||||
|
||||
public void AssocThickness(int seriesId, int thickness)
|
||||
{
|
||||
brush_thicknesses[seriesId] = thickness;
|
||||
}
|
||||
|
||||
public void AssocName(int seriesId, String name)
|
||||
{
|
||||
line_names[seriesId] = name;
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc)
|
||||
{
|
||||
base.OnRender(dc);
|
||||
|
||||
|
||||
|
||||
DataPoint[] localPoints;
|
||||
lock (dataPoints)
|
||||
localPoints = dataPoints.ToArray();
|
||||
|
||||
var pfs = new Dictionary<int, PathFigure>();
|
||||
|
||||
for (int i = 0; i < localPoints.Length; i++)
|
||||
{
|
||||
var ptTime = localPoints[i].Time;
|
||||
var ptAge = (DateTime.Now - ptTime).TotalSeconds;
|
||||
foreach (var kvp in localPoints[i].values)
|
||||
{
|
||||
var seriesId = kvp.Key;
|
||||
|
||||
double v = Math.Min(Math.Max(kvp.Value, 0), 1);
|
||||
|
||||
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
|
||||
if (Orientation == System.Windows.Controls.Orientation.Horizontal)
|
||||
{
|
||||
x = ActualWidth - (CurrentTime - localPoints[i].Time).TotalMilliseconds * (ActualWidth / historyLength.TotalMilliseconds);
|
||||
y = ActualHeight - ActualHeight * v;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = ActualHeight - (CurrentTime - localPoints[i].Time).TotalMilliseconds * (ActualHeight / historyLength.TotalMilliseconds);
|
||||
x = ActualWidth * v;
|
||||
}
|
||||
|
||||
if (!pfs.ContainsKey(seriesId))
|
||||
{
|
||||
pfs[seriesId] = new PathFigure();
|
||||
pfs[seriesId].IsClosed = false;
|
||||
pfs[seriesId].StartPoint = new Point(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
pfs[seriesId].Segments.Add(new LineSegment(new Point(x, y), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var kvp in pfs)
|
||||
{
|
||||
var seriesId = kvp.Key;
|
||||
var pf = kvp.Value;
|
||||
|
||||
Brush b = brushes.ContainsKey(seriesId) ? brushes[seriesId] : Brushes.Black;
|
||||
|
||||
int thickness = brush_thicknesses.ContainsKey(seriesId) ? brush_thicknesses[seriesId] : 2;
|
||||
|
||||
PathGeometry pg = new PathGeometry(new PathFigure[] { pf });
|
||||
|
||||
|
||||
Pen p = new Pen(b, thickness);
|
||||
|
||||
if (line_names.ContainsKey(seriesId) && line_names[seriesId].CompareTo("State rapport") == 0)
|
||||
{
|
||||
double[] dashValues = { 5.0, 5.0, 5.0 };
|
||||
p.DashStyle = new System.Windows.Media.DashStyle(dashValues, 0);
|
||||
}
|
||||
dc.DrawGeometry(null, p, pg);
|
||||
}
|
||||
|
||||
if (ShowLegend && line_names.Count > 0)
|
||||
{
|
||||
int height_one = 18;
|
||||
int height = height_one * line_names.Count;
|
||||
|
||||
Pen p = new Pen(Brushes.Black, 1);
|
||||
Brush legend_b = new SolidColorBrush(Color.FromRgb(255, 255, 255));
|
||||
|
||||
dc.DrawRectangle(legend_b, p, new Rect(0, 1, 100, height));
|
||||
|
||||
int i = 0;
|
||||
foreach (var key_name_pair in line_names)
|
||||
{
|
||||
var line_name = key_name_pair.Value;
|
||||
FormattedText ft = FT(line_name, 11);
|
||||
|
||||
// Draw the text
|
||||
dc.DrawText(ft, new Point(15, 1 + height_one * i));
|
||||
// Draw example lines
|
||||
|
||||
Brush legend_c = new SolidColorBrush(brush_colors[key_name_pair.Key]);
|
||||
Pen p_line = new Pen(legend_c, brush_thicknesses[key_name_pair.Key]);
|
||||
dc.DrawLine(p_line, new Point(0, 1 + height_one * i + height_one / 2), new Point(14, 1 + height_one * i + height_one / 2));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue