Skip to content

Commit

Permalink
Merge pull request #76 from glopesdev/timeline-visualizer
Browse files Browse the repository at this point in the history
Add timeline visualizer for Harp message streams
  • Loading branch information
glopesdev authored Nov 5, 2023
2 parents 8f79cbd + 45256ad commit affa52f
Show file tree
Hide file tree
Showing 14 changed files with 1,072 additions and 2 deletions.
21 changes: 21 additions & 0 deletions Bonsai.Harp.Visualizers/Bonsai.Harp.Visualizers.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

<PropertyGroup>
<Title>Bonsai - Harp Visualizers Library</Title>
<Description>Bonsai Design Library containing operators for visualizing Harp message streams.</Description>
<PackageTags>Bonsai Rx Harp Visualizers</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<UseWindowsForms>true</UseWindowsForms>
<TargetFramework>net472</TargetFramework>
<Version>0.1.0</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Bonsai.Design.Visualizers" Version="2.8.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Bonsai.Harp.Design\Bonsai.Harp.Design.csproj" />
</ItemGroup>

</Project>
85 changes: 85 additions & 0 deletions Bonsai.Harp.Visualizers/BoundedPointPairList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using ZedGraph;

namespace Bonsai.Harp.Visualizers
{
internal class BoundedPointPairList : IPointListEdit
{
double minValue = double.MinValue;
double maxValue = double.MaxValue;
readonly QueueList<PointPair> points = new();

public BoundedPointPairList()
{
}

public BoundedPointPairList(IPointList points)
{
for (int i = 0; i < points.Count; i++)
{
Add(points[i]);
}
}

public void SetBounds(double min, double max)
{
if (max < min)
{
throw new ArgumentOutOfRangeException(nameof(max));
}

minValue = min;
maxValue = max;
while (points.Count > 0 && points[0].X < min)
{
points.TryDequeue(out _);
}

while (points.Count > 0 && points[points.Count - 1].X > max)
{
points.TryDequeueLast(out _);
}
}

public PointPair this[int index]
{
get => points[index];
set => points[index] = value;
}

PointPair IPointList.this[int index] => this[index];

public int Count => points.Count;

public void Add(PointPair point)
{
if (point.X >= minValue && point.X <= maxValue)
{
points.Enqueue(point);
}
}

public void Add(double x, double y)
{
if (x >= minValue && x <= maxValue)
{
points.Enqueue(new PointPair(x, y));
}
}

public void Clear()
{
points.Clear();
}

public object Clone()
{
return new BoundedPointPairList(this);
}

public void RemoveAt(int index)
{
points.RemoveAt(index);
}
}
}
21 changes: 21 additions & 0 deletions Bonsai.Harp.Visualizers/GraphHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using ZedGraph;

namespace Bonsai.Harp.Visualizers
{
static class GraphHelper
{
internal static void SetAxisLabel(Axis axis, string label)
{
axis.Title.Text = label;
axis.Title.IsVisible = !string.IsNullOrEmpty(label);
}

internal static void FormatTimeAxis(Axis axis)
{
axis.Type = AxisType.Linear;
axis.Scale.MaxAuto = false;
axis.Scale.MinAuto = false;
}
}
}
7 changes: 7 additions & 0 deletions Bonsai.Harp.Visualizers/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Bonsai;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: XmlNamespacePrefix("clr-namespace:Bonsai.Harp.Visualizers", "harpviz")]
[assembly: WorkflowNamespaceIcon("Bonsai:ElementIcon.Visualizer")]
10 changes: 10 additions & 0 deletions Bonsai.Harp.Visualizers/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"profiles": {
"Bonsai": {
"commandName": "Executable",
"executablePath": "$(registry:HKEY_CURRENT_USER\\Software\\Bonsai Foundation\\Bonsai@InstallDir)Bonsai.exe",
"commandLineArgs": "--lib:\"$(TargetDir).\"",
"nativeDebugging": true
}
}
}
157 changes: 157 additions & 0 deletions Bonsai.Harp.Visualizers/QueueList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace Bonsai.Harp.Visualizers
{
internal class QueueList<T> : IReadOnlyList<T>
{
int head;
int tail;
int count;
T[] buffer;

public QueueList() : this(capacity: 4)
{
}

public QueueList(int capacity)
{
if (capacity < 0)
{
throw new ArgumentOutOfRangeException(nameof(capacity));
}

EnsureCapacity(capacity);
}

private void EnsureCapacity(int capacity)
{
var array = new T[capacity];
if (count > 0)
{
if (head < tail)
{
Array.Copy(buffer, head, array, 0, count);
}
else
{
Array.Copy(buffer, head, array, 0, buffer.Length - head);
Array.Copy(buffer, 0, array, buffer.Length - head, tail);
}
}

buffer = array;
head = 0;
tail = count;
}

private int GetIndexInternal(int index)
{
return (head + index) % buffer.Length;
}

public T this[int index]
{
get
{
if (index < 0 || index >= count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}

return buffer[GetIndexInternal(index)];
}
set
{
if (index < 0 || index >= count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}

buffer[GetIndexInternal(index)] = value;
}
}

public int Count => count;

public void Enqueue(T item)
{
if (count >= buffer.Length)
{
EnsureCapacity(buffer.Length * 2);
}

buffer[tail] = item;
tail = (tail + 1) % buffer.Length;
count++;
}

public bool TryDequeue(out T result)
{
if (count == 0)
{
result = default;
return false;
}

result = buffer[head];
buffer[head] = default;
head = (head + 1) % buffer.Length;
count--;
return true;
}

public bool TryDequeueLast(out T result)
{
if (count == 0)
{
result = default;
return false;
}

var index = tail - 1;
if (index < 0) index += buffer.Length;
result = buffer[index];
buffer[index] = default;
tail = index;
count--;
return true;
}

public void Clear()
{
if (head < tail)
{
Array.Clear(buffer, head, count);
}
else
{
Array.Clear(buffer, head, buffer.Length - head);
Array.Clear(buffer, 0, tail);
}

head = 0;
tail = 0;
count = 0;
}

public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < count; i++)
{
yield return buffer[GetIndexInternal(i)];
}
}

public void RemoveAt(int index)
{
throw new NotSupportedException();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
Loading

0 comments on commit affa52f

Please sign in to comment.