removing submodule
Some checks failed
build lightwatch / build (push) Failing after 5m6s

This commit is contained in:
2023-09-03 01:14:34 -04:00
parent d539dc9119
commit ed18dd911a
190 changed files with 1174526 additions and 4 deletions

View File

@@ -0,0 +1,5 @@
**/.vs
**/bin
**/obj
**/*.sln
**/*.csproj.user

View File

@@ -0,0 +1,85 @@
global using static RPiRgbLEDMatrix.Bindings;
using System.Runtime.InteropServices;
namespace RPiRgbLEDMatrix;
/*
Some of the extern methods listed below are marked with [SuppressGCTransition].
This disables some GC checks that may take a long time. But such methods should
be fast and trivial, otherwise the managed code may become unstable (see docs).
Keep this in mind when changing the C/C++ side.
https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.suppressgctransitionattribute
*/
internal static class Bindings
{
private const string Lib = "librgbmatrix.so.1";
[DllImport(Lib)]
public static extern IntPtr led_matrix_create(int rows, int chained, int parallel);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern IntPtr led_matrix_create_from_options_const_argv(
ref InternalRGBLedMatrixOptions options,
int argc,
string[] argv);
[DllImport(Lib)]
public static extern void led_matrix_delete(IntPtr matrix);
[DllImport(Lib)]
public static extern IntPtr led_matrix_create_offscreen_canvas(IntPtr matrix);
[DllImport(Lib)]
public static extern IntPtr led_matrix_swap_on_vsync(IntPtr matrix, IntPtr canvas);
[DllImport(Lib)]
public static extern IntPtr led_matrix_get_canvas(IntPtr matrix);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern byte led_matrix_get_brightness(IntPtr matrix);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_matrix_set_brightness(IntPtr matrix, byte brightness);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern IntPtr load_font(string bdf_font_file);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern int draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b,
string utf8_text, int extra_spacing);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern int vertical_draw_text(IntPtr canvas, IntPtr font, int x, int y, byte r, byte g, byte b,
string utf8_text, int kerning_offset);
[DllImport(Lib, CharSet = CharSet.Ansi)]
public static extern void delete_font(IntPtr font);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_canvas_get_size(IntPtr canvas, out int width, out int height);
[DllImport(Lib)]
[SuppressGCTransition]
public static extern void led_canvas_set_pixel(IntPtr canvas, int x, int y, byte r, byte g, byte b);
[DllImport(Lib)]
public static extern void led_canvas_set_pixels(IntPtr canvas, int x, int y, int width, int height,
ref Color colors);
[DllImport(Lib)]
public static extern void led_canvas_clear(IntPtr canvas);
[DllImport(Lib)]
public static extern void led_canvas_fill(IntPtr canvas, byte r, byte g, byte b);
[DllImport(Lib)]
public static extern void draw_circle(IntPtr canvas, int xx, int y, int radius, byte r, byte g, byte b);
[DllImport(Lib)]
public static extern void draw_line(IntPtr canvas, int x0, int y0, int x1, int y1, byte r, byte g, byte b);
}

View File

@@ -0,0 +1,38 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents an RGB (red, green, blue) color
/// </summary>
public struct Color
{
/// <summary>
/// The red component value of this instance.
/// </summary>
public byte R;
/// <summary>
/// The green component value of this instance.
/// </summary>
public byte G;
/// <summary>
/// The blue component value of this instance.
/// </summary>
public byte B;
/// <summary>
/// Creates a new color from the specified color values (red, green, and blue).
/// </summary>
/// <param name="r">The red component value.</param>
/// <param name="g">The green component value.</param>
/// <param name="b">The blue component value.</param>
public Color(int r, int g, int b) : this((byte)r, (byte)g, (byte)b) { }
/// <summary>
/// Creates a new color from the specified color values (red, green, and blue).
/// </summary>
/// <param name="r">The red component value.</param>
/// <param name="g">The green component value.</param>
/// <param name="b">The blue component value.</param>
public Color(byte r, byte g, byte b) => (R, G, B) = (r, g, b);
}

View File

@@ -0,0 +1,50 @@
using System.Runtime.InteropServices;
namespace RPiRgbLEDMatrix;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct InternalRGBLedMatrixOptions
{
public IntPtr hardware_mapping;
public int rows;
public int cols;
public int chain_length;
public int parallel;
public int pwm_bits;
public int pwm_lsb_nanoseconds;
public int pwm_dither_bits;
public int brightness;
public int scan_mode;
public int row_address_type;
public int multiplexing;
public IntPtr led_rgb_sequence;
public IntPtr pixel_mapper_config;
public IntPtr panel_type;
public byte disable_hardware_pulsing;
public byte show_refresh_rate;
public byte inverse_colors;
public int limit_refresh_rate_hz;
public InternalRGBLedMatrixOptions(RGBLedMatrixOptions opt)
{
chain_length = opt.ChainLength;
rows = opt.Rows;
cols = opt.Cols;
hardware_mapping = Marshal.StringToHGlobalAnsi(opt.HardwareMapping);
inverse_colors = (byte)(opt.InverseColors ? 1 : 0);
led_rgb_sequence = Marshal.StringToHGlobalAnsi(opt.LedRgbSequence);
pixel_mapper_config = Marshal.StringToHGlobalAnsi(opt.PixelMapperConfig);
panel_type = Marshal.StringToHGlobalAnsi(opt.PanelType);
parallel = opt.Parallel;
multiplexing = (int)opt.Multiplexing;
pwm_bits = opt.PwmBits;
pwm_lsb_nanoseconds = opt.PwmLsbNanoseconds;
pwm_dither_bits = opt.PwmDitherBits;
scan_mode = (int)opt.ScanMode;
show_refresh_rate = (byte)(opt.ShowRefreshRate ? 1 : 0);
limit_refresh_rate_hz = opt.LimitRefreshRateHz;
brightness = opt.Brightness;
disable_hardware_pulsing = (byte)(opt.DisableHardwarePulsing ? 1 : 0);
row_address_type = opt.RowAddressType;
}
};

View File

@@ -0,0 +1,34 @@
# This Makefile is intended to be used only by the toplevel Makefile.
# For any other purposes, use .NET SDK build tools directly
# Don't forget to synchronize these variables with the 'RPiRgbLEDMatrix.csproj' file
RGB_LIBDIR=../../lib
RGB_LIBRARY_NAME=rgbmatrix
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).so.1
NUGET_VERSION = 1.0.0
NUGET_ID = HZeller.RPiRgbLEDMatrix
NUGET_CONFIG = Release
NUGET_PACKAGE = /bin/$(NUGET_CONFIG)/$(NUGET_ID).$(NUGET_VERSION).nupkg
$(NUGET_PACKAGE): $(RGB_LIBRARY)
dotnet pack -c $(NUGET_CONFIG) -p:SkipNative=false -p:PackageId=$(NUGET_ID) -p:Version=$(NUGET_VERSION)
# The examples also depend on the 'RPiRgbLEDMatrix.csproj', but this will be handled by 'dotnet'
build: $(RGB_LIBRARY)
dotnet build examples/FontExample/FontExample.csproj -p:SkipNative=false
dotnet build examples/MatrixRain/MatrixRain.csproj -p:SkipNative=false
dotnet build examples/MinimalExample/MinimalExample.csproj -p:SkipNative=false
dotnet build examples/PulsingBrightness/PulsingBrightness.csproj -p:SkipNative=false
dotnet build examples/Rotating3DCube/Rotating3DCube.csproj -p:SkipNative=false
dotnet build examples/PlayGIF/PlayGIF.csproj -p:SkipNative=false
$(RGB_LIBRARY):
$(MAKE) -C $(RGB_LIBDIR)
# Used by toplevel Makefile
nuget: $(NUGET_PACKAGE)
# Used in 'RPiRgbLEDMatrix.csproj'
library: $(RGB_LIBRARY)

View File

@@ -0,0 +1,11 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Type of multiplexing.
/// </summary>
public enum Multiplexing : int
{
Direct = 0,
Stripe = 1,
Checker = 2
}

View File

@@ -0,0 +1,16 @@
C# bindings for RGB Matrix library
======================================
Building
--------
To build the C# wrapper for the RGB Matrix C library you need to first have __.NET SDK__ installed.
### Install .NET SDK
`sudo apt install dotnet6` should work in most cases.
For some old distributions, read [docs](https://learn.microsoft.com/dotnet/core/install/linux)
Then, in the `bindings/c#` directory type: `dotnet build`
To run the example applications in the c#\examples\EXAMPLE folder: `sudo dotnet run`

View File

@@ -0,0 +1,100 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents a canvas whose pixels can be manipulated.
/// </summary>
public class RGBLedCanvas
{
// This is a wrapper for canvas no need to implement IDisposable here
// because RGBLedMatrix has ownership and takes care of disposing canvases
internal IntPtr _canvas;
// this is not called directly by the consumer code,
// consumer uses factory methods in RGBLedMatrix
internal RGBLedCanvas(IntPtr canvas)
{
_canvas = canvas;
led_canvas_get_size(_canvas, out var width, out var height);
Width = width;
Height = height;
}
/// <summary>
/// The width of the canvas in pixels.
/// </summary>
public int Width { get; private set; }
/// <summary>
/// The height of the canvas in pixels.
/// </summary>
public int Height { get; private set; }
/// <summary>
/// Sets the color of a specific pixel.
/// </summary>
/// <param name="x">The X coordinate of the pixel.</param>
/// <param name="y">The Y coordinate of the pixel.</param>
/// <param name="color">New pixel color.</param>
public void SetPixel(int x, int y, Color color) => led_canvas_set_pixel(_canvas, x, y, color.R, color.G, color.B);
/// <summary>
/// Copies the colors from the specified buffer to a rectangle on the canvas.
/// </summary>
/// <param name="x">The X coordinate of the top-left pixel of the rectangle.</param>
/// <param name="y">The Y coordinate of the top-left pixel of the rectangle.</param>
/// <param name="width">Width of the rectangle.</param>
/// <param name="height">Height of the rectangle.</param>
/// <param name="colors">Buffer containing the colors to copy.</param>
public void SetPixels(int x, int y, int width, int height, Span<Color> colors)
{
if (colors.Length < width * height)
throw new ArgumentOutOfRangeException(nameof(colors));
led_canvas_set_pixels(_canvas, x, y, width, height, ref colors[0]);
}
/// <summary>
/// Sets the color of the entire canvas.
/// </summary>
/// <param name="color">New canvas color.</param>
public void Fill(Color color) => led_canvas_fill(_canvas, color.R, color.G, color.B);
/// <summary>
/// Cleans the entire canvas.
/// </summary>
public void Clear() => led_canvas_clear(_canvas);
/// <summary>
/// Draws a circle of the specified color.
/// </summary>
/// <param name="x">The X coordinate of the center.</param>
/// <param name="y">The Y coordinate of the center.</param>
/// <param name="radius">The radius of the circle, in pixels.</param>
/// <param name="color">The color of the circle.</param>
public void DrawCircle(int x, int y, int radius, Color color) =>
draw_circle(_canvas, x, y, radius, color.R, color.G, color.B);
/// <summary>
/// Draws a line of the specified color.
/// </summary>
/// <param name="x0">The X coordinate of the first point.</param>
/// <param name="y0">The Y coordinate of the first point.</param>
/// <param name="x1">The X coordinate of the second point.</param>
/// <param name="y1">The Y coordinate of the second point.</param>
/// <param name="color">The color of the line.</param>
public void DrawLine(int x0, int y0, int x1, int y1, Color color) =>
draw_line(_canvas, x0, y0, x1, y1, color.R, color.G, color.B);
/// <summary>
/// Draws the text with the specified color.
/// </summary>
/// <param name="font">Font to draw text with.</param>
/// <param name="x">The X coordinate of the starting point.</param>
/// <param name="y">The Y coordinate of the starting point.</param>
/// <param name="color">The color of the text.</param>
/// <param name="text">Text to draw.</param>
/// <param name="spacing">Additional spacing between characters.</param>
/// <param name="vertical">Whether to draw the text vertically.</param>
/// <returns>How many pixels was advanced on the screen.</returns>
public int DrawText(RGBLedFont font, int x, int y, Color color, string text, int spacing = 0, bool vertical = false) =>
font.DrawText(_canvas, x, y, color, text, spacing, vertical);
}

View File

@@ -0,0 +1,40 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents a <c>.BDF</c> font.
/// </summary>
public class RGBLedFont : IDisposable
{
internal IntPtr _font;
private bool disposedValue = false;
/// <summary>
/// Loads the BDF font from the specified file.
/// </summary>
/// <param name="bdfFontPath">The path to the BDF file to load.</param>
public RGBLedFont(string bdfFontPath) => _font = load_font(bdfFontPath);
internal int DrawText(IntPtr canvas, int x, int y, Color color, string text, int spacing = 0, bool vertical = false)
{
if (!vertical)
return draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing);
else
return vertical_draw_text(canvas, _font, x, y, color.R, color.G, color.B, text, spacing);
}
protected virtual void Dispose(bool disposing)
{
if (disposedValue) return;
delete_font(_font);
disposedValue = true;
}
~RGBLedFont() => Dispose(false);
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

View File

@@ -0,0 +1,112 @@
using System.Buffers;
using System.Runtime.InteropServices;
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents a RGB matrix.
/// </summary>
public class RGBLedMatrix : IDisposable
{
private IntPtr matrix;
private bool disposedValue = false;
/// <summary>
/// Initializes a new matrix.
/// </summary>
/// <param name="rows">Size of a single module. Can be 32, 16 or 8.</param>
/// <param name="chained">How many modules are connected in a chain.</param>
/// <param name="parallel">How many modules are connected in a parallel.</param>
public RGBLedMatrix(int rows, int chained, int parallel)
{
matrix = led_matrix_create(rows, chained, parallel);
if (matrix == (IntPtr)0)
throw new ArgumentException("Could not initialize a new matrix");
}
/// <summary>
/// Initializes a new matrix.
/// </summary>
/// <param name="options">A configuration of a matrix.</param>
public RGBLedMatrix(RGBLedMatrixOptions options)
{
InternalRGBLedMatrixOptions opt = default;
try
{
opt = new(options);
var args = Environment.GetCommandLineArgs();
// Because gpio-slowdown is not provided in the options struct,
// we manually add it.
// Let's add it first to the command-line we pass to the
// matrix constructor, so that it can be overridden with the
// users' commandline.
// As always, as the _very_ first, we need to provide the
// program name argv[0].
var argv = new string[args.Length + 1];
argv[0] = args[0];
argv[1] = $"--led-slowdown-gpio={options.GpioSlowdown}";
Array.Copy(args, 1, argv, 2, args.Length - 1);
matrix = led_matrix_create_from_options_const_argv(ref opt, argv.Length, argv);
if (matrix == (IntPtr)0)
throw new ArgumentException("Could not initialize a new matrix");
}
finally
{
if(options.HardwareMapping is not null) Marshal.FreeHGlobal(opt.hardware_mapping);
if(options.LedRgbSequence is not null) Marshal.FreeHGlobal(opt.led_rgb_sequence);
if(options.PixelMapperConfig is not null) Marshal.FreeHGlobal(opt.pixel_mapper_config);
if(options.PanelType is not null) Marshal.FreeHGlobal(opt.panel_type);
}
}
/// <summary>
/// Creates a new backbuffer canvas for drawing on.
/// </summary>
/// <returns>An instance of <see cref="RGBLedCanvas"/> representing the canvas.</returns>
public RGBLedCanvas CreateOffscreenCanvas() => new(led_matrix_create_offscreen_canvas(matrix));
/// <summary>
/// Returns a canvas representing the current frame buffer.
/// </summary>
/// <returns>An instance of <see cref="RGBLedCanvas"/> representing the canvas.</returns>
/// <remarks>Consider using <see cref="CreateOffscreenCanvas"/> instead.</remarks>
public RGBLedCanvas GetCanvas() => new(led_matrix_get_canvas(matrix));
/// <summary>
/// Swaps this canvas with the currently active canvas. The active canvas
/// becomes a backbuffer and is mapped to <paramref name="canvas"/> instance.
/// <br/>
/// This operation guarantees vertical synchronization.
/// </summary>
/// <param name="canvas">Backbuffer canvas to swap.</param>
public void SwapOnVsync(RGBLedCanvas canvas) =>
canvas._canvas = led_matrix_swap_on_vsync(matrix, canvas._canvas);
/// <summary>
/// The general brightness of the matrix.
/// </summary>
public byte Brightness
{
get => led_matrix_get_brightness(matrix);
set => led_matrix_set_brightness(matrix, value);
}
protected virtual void Dispose(bool disposing)
{
if (disposedValue) return;
led_matrix_delete(matrix);
disposedValue = true;
}
~RGBLedMatrix() => Dispose(false);
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

View File

@@ -0,0 +1,124 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Represents the matrix settings.
/// </summary>
public struct RGBLedMatrixOptions
{
/// <summary>
/// Name of the hardware mapping used. If passed
/// <see langword="null"/> here, the default is used.
/// </summary>
public string? HardwareMapping = null;
/// <summary>
/// The "rows" are the number of rows supported by the display, so 32 or 16.
/// Default: 32.
/// </summary>
public int Rows = 32;
/// <summary>
/// The "cols" are the number of columns per panel. Typically something
/// like 32, but also 64 is possible. Sometimes even 40.
/// <c>cols * chain_length</c> is the total length of the display, so you can
/// represent a 64 wide display as cols=32, chain=2 or cols=64, chain=1;
/// same thing, but more convenient to think of.
/// </summary>
public int Cols = 32;
/// <summary>
/// The chain_length is the number of displays daisy-chained together
/// (output of one connected to input of next). Default: 1
/// </summary>
public int ChainLength = 1;
/// <summary>
/// The number of parallel chains connected to the Pi; in old Pis with 26
/// GPIO pins, that is 1, in newer Pis with 40 interfaces pins, that can also
/// be 2 or 3. The effective number of pixels in vertical direction is then
/// thus <c>rows * parallel</c>. Default: 1
/// </summary>
public int Parallel = 1;
/// <summary>
/// Set PWM bits used for output. Default is 11, but if you only deal with limited
/// comic-colors, 1 might be sufficient. Lower require less CPU and increases refresh-rate.
/// </summary>
public int PwmBits = 11;
/// <summary>
/// Change the base time-unit for the on-time in the lowest significant bit in
/// nanoseconds. Higher numbers provide better quality (more accurate color, less
/// ghosting), but have a negative impact on the frame rate.
/// </summary>
public int PwmLsbNanoseconds = 130;
/// <summary>
/// The lower bits can be time-dithered for higher refresh rate.
/// </summary>
public int PwmDitherBits = 0;
/// <summary>
/// The initial brightness of the panel in percent. Valid range is 1..100
/// </summary>
public int Brightness = 100;
/// <summary>
/// Scan mode.
/// </summary>
public ScanModes ScanMode = ScanModes.Progressive;
/// <summary>
/// Default row address type is 0, corresponding to direct setting of the
/// row, while row address type 1 is used for panels that only have A/B,
/// typically some 64x64 panels
/// </summary>
public int RowAddressType = 0;
/// <summary>
/// Type of multiplexing.
/// </summary>
public Multiplexing Multiplexing = Multiplexing.Direct;
/// <summary>
/// In case the internal sequence of mapping is not <c>"RGB"</c>, this
/// contains the real mapping. Some panels mix up these colors.
/// </summary>
public string? LedRgbSequence = null;
/// <summary>
/// A string describing a sequence of pixel mappers that should be applied
/// to this matrix. A semicolon-separated list of pixel-mappers with optional
/// parameter.
public string? PixelMapperConfig = null;
/// <summary>
/// Panel type. Typically just empty, but certain panels (FM6126)
/// requie an initialization sequence
/// </summary>
public string? PanelType = null;
/// <summary>
/// Allow to use the hardware subsystem to create pulses. This won't do
/// anything if output enable is not connected to GPIO 18.
/// </summary>
public bool DisableHardwarePulsing = false;
public bool ShowRefreshRate = false;
public bool InverseColors = false;
/// <summary>
/// Limit refresh rate of LED panel. This will help on a loaded system
/// to keep a constant refresh rate. &lt;= 0 for no limit.
/// </summary>
public int LimitRefreshRateHz = 0;
/// <summary>
/// Slowdown GPIO. Needed for faster Pis/slower panels.
/// </summary>
public int GpioSlowdown = 1;
/// <summary>
/// Creates default matrix settings.
/// </summary>
public RGBLedMatrixOptions() { }
}

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<!-- Skip builing native libraries by default on Windows -->
<PropertyGroup Condition="$(SkipNative) == '' and $(OS) != 'Unix'">
<SkipNative>true</SkipNative>
</PropertyGroup>
<ItemGroup>
<Compile Remove="examples\**" />
<None Remove="examples\**" />
<None Condition="$(SkipNative) != 'true'" Pack="true"
Include="..\..\lib\librgbmatrix.so.1"
PackagePath="\runtimes\linux-arm64\native" >
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="CompileNative" BeforeTargets="Compile" Condition="$(SkipNative) != 'true'">
<Message Text="Building native libraries" />
<Exec Command="make library" />
</Target>
</Project>

View File

@@ -0,0 +1,10 @@
namespace RPiRgbLEDMatrix;
/// <summary>
/// Scan modes.
/// </summary>
public enum ScanModes
{
Progressive = 0,
Interlaced = 1
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,29 @@
using RPiRgbLEDMatrix;
if (args.Length < 1)
{
Console.WriteLine("font-example.exe [font_path] <text>");
return -1;
}
string text = "Hello World!";
if (args.Length > 1)
text = args[1];
using var matrix = new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
using var font = new RGBLedFont(args[0]);
canvas.DrawText(font, 1, 6, new Color(0, 255, 0), text);
matrix.SwapOnVsync(canvas);
// run until user presses Ctrl+C
var running = true;
Console.CancelKeyPress += (_, e) =>
{
running = false;
e.Cancel = true; // do not terminate program with Ctrl+C, we need to dispose
};
while (running) Thread.Yield();
return 0;

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,74 @@
using RPiRgbLEDMatrix;
const int MaxHeight = 16;
const int ColorStep = 15;
const int FrameStep = 1;
using var matrix = new RGBLedMatrix(new RGBLedMatrixOptions { ChainLength = 2 });
var canvas = matrix.CreateOffscreenCanvas();
var rnd = new Random();
var points = new List<Point>();
var recycled = new Stack<Point>();
var frame = 0;
var running = true;
Console.CancelKeyPress += (s, e) =>
{
running = false;
e.Cancel = true; // don't terminate, we need to dispose
};
// run until user presses Ctrl+C
while (running)
{
var frameStart = Environment.TickCount64;
frame++;
if (frame % FrameStep == 0)
{
if (recycled.Count == 0)
points.Add(new Point(rnd.Next(0, canvas.Width - 1), 0));
else
{
var point = recycled.Pop();
point.X = rnd.Next(0, canvas.Width - 1);
point.Y = 0;
point.Recycled = false;
}
}
canvas.Clear();
foreach (var point in points)
{
if (point.Recycled) continue;
point.Y++;
if (point.Y - MaxHeight > canvas.Height)
{
point.Recycled = true;
recycled.Push(point);
}
for (var i = 0; i < MaxHeight; i++)
{
canvas.SetPixel(point.X, point.Y - i, new Color(0, 255 - i * ColorStep, 0));
}
}
matrix.SwapOnVsync(canvas);
// force 30 FPS
var elapsed = Environment.TickCount64 - frameStart;
if (elapsed < 33) Thread.Sleep(33 - (int)elapsed);
}
class Point
{
public int X;
public int Y;
public bool Recycled = false;
public Point(int x, int y) => (X, Y) = (x, y);
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,19 @@
using RPiRgbLEDMatrix;
using var matrix = new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
var centerX = canvas.Width / 2;
var centerY = canvas.Height / 2;
for (var i = 0; i < 1000; ++i)
{
for (var y = 0; y < canvas.Height; ++y)
for (var x = 0; x < canvas.Width; ++x)
canvas.SetPixel(x, y, new Color(i & 0xFF, x, y));
canvas.DrawCircle(centerX, centerY, 6, new Color(0, 0, 255));
canvas.DrawLine(centerX - 3, centerY - 3, centerX + 3, centerY + 3, new Color(0, 0, 255));
canvas.DrawLine(centerX - 3, centerY + 3, centerX + 3, centerY - 3, new Color(0, 0, 255));
matrix.SwapOnVsync(canvas);
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,40 @@
using RPiRgbLEDMatrix;
using System.Runtime.InteropServices;
using Color = RPiRgbLEDMatrix.Color;
Console.Write("GIF path: ");
var path = Console.ReadLine()!;
using var matrix = new RGBLedMatrix(32, 2, 1);
var canvas = matrix.CreateOffscreenCanvas();
Configuration.Default.PreferContiguousImageBuffers = true;
using var image = Image.Load<Rgb24>(path);
image.Mutate(o => o.Resize(canvas.Width, canvas.Height));
var running = true;
Console.CancelKeyPress += (s, e) =>
{
running = false;
e.Cancel = true; // don't terminate, we need to dispose
};
var frame = -1;
// preprocess frames to get delays and pixel buffers
var frames = image.Frames
.Select(f => (
Pixels: f.DangerousTryGetSinglePixelMemory(out var memory) ? memory : throw new("Could not get pixel buffer"),
Delay: f.Metadata.GetGifMetadata().FrameDelay * 10
)).ToArray();
// run until user presses Ctrl+C
while (running)
{
frame = (frame + 1) % frames.Length;
var data = MemoryMarshal.Cast<Rgb24, Color>(frames[frame].Pixels.Span);
canvas.SetPixels(0, 0, canvas.Width, canvas.Height, data);
matrix.SwapOnVsync(canvas);
Thread.Sleep(frames[frame].Delay);
}

View File

@@ -0,0 +1,33 @@
using RPiRgbLEDMatrix;
using var matrix = new RGBLedMatrix(new RGBLedMatrixOptions { Rows = 32, Cols = 64 });
var canvas = matrix.CreateOffscreenCanvas();
var maxBrightness = matrix.Brightness;
var rnd = new Random();
// run until user presses Ctrl+C
var running = true;
Console.CancelKeyPress += (_, e) =>
{
running = false;
e.Cancel = true; // do not terminate program with Ctrl+C, we need to dispose
};
var color = new Color(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256));
while (running)
{
if (matrix.Brightness < 1)
{
matrix.Brightness = maxBrightness;
color = new Color(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256));
}
else
{
matrix.Brightness--;
}
canvas.Fill(color);
matrix.SwapOnVsync(canvas);
Thread.Sleep(20);
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,90 @@
using RPiRgbLEDMatrix;
using System.Numerics;
const float MaxModuleSpeed = 0.1f;
const float FOV = 60f;
const float Scale = 1.1f;
const float LerpPow = 0.002f;
const int ChangePerFrames = 50;
using var leds = new RGBLedMatrix(32, 1, 1);
var canvas = leds.CreateOffscreenCanvas();
var (centerX, centerY) = (canvas.Width / 2, canvas.Height / 2);
var rnd = new Random();
var angleSpeed = new Vector3();
var nextAngleSpeed = new Vector3();
var frame = -1;
var rotateMatrix = Matrix4x4.Identity;
var scaleMatrix = Matrix4x4.CreateScale(Scale);
var projectMatrix = Matrix4x4.CreatePerspectiveFieldOfView(FOV / 180 * MathF.PI, 1, 0.1f, 100f);
var cameraMatrix = Matrix4x4.CreateLookAt(new(0, 0, 4), new(0, 0, 0), new(0, 1, 0));
// run until user presses Ctrl+C
var running = true;
Console.CancelKeyPress += (_, e) =>
{
running = false;
e.Cancel = true; // do not terminate program with Ctrl+C, we need to dispose
};
while (running)
{
var frameStart = Environment.TickCount64;
// update angle speed
frame = (frame + 1) % ChangePerFrames;
if(frame == 0)
nextAngleSpeed = new Vector3(
(rnd.NextSingle() * 2 - 1) * MaxModuleSpeed,
(rnd.NextSingle() * 2 - 1) * MaxModuleSpeed,
(rnd.NextSingle() * 2 - 1) * MaxModuleSpeed
);
angleSpeed = Vector3.Lerp(angleSpeed, nextAngleSpeed, LerpPow);
// update matrices
rotateMatrix *= Matrix4x4.CreateRotationX(angleSpeed.X);
rotateMatrix *= Matrix4x4.CreateRotationY(angleSpeed.Y);
rotateMatrix *= Matrix4x4.CreateRotationZ(angleSpeed.Z);
var matrix = scaleMatrix * rotateMatrix * cameraMatrix * projectMatrix;
// calculate points
var top1 = Vector4.Transform(new Vector3( 1, 1, 1), matrix);
var top2 = Vector4.Transform(new Vector3(-1, 1, 1), matrix);
var top3 = Vector4.Transform(new Vector3(-1, 1, -1), matrix);
var top4 = Vector4.Transform(new Vector3( 1, 1, -1), matrix);
var bot1 = Vector4.Transform(new Vector3( 1, -1, 1), matrix);
var bot2 = Vector4.Transform(new Vector3(-1, -1, 1), matrix);
var bot3 = Vector4.Transform(new Vector3(-1, -1, -1), matrix);
var bot4 = Vector4.Transform(new Vector3( 1, -1, -1), matrix);
// draw
canvas.Fill(new(0, 0, 0));
DrawLine(top1, top2);
DrawLine(top2, top3);
DrawLine(top3, top4);
DrawLine(top4, top1);
DrawLine(bot1, bot2);
DrawLine(bot2, bot3);
DrawLine(bot3, bot4);
DrawLine(bot4, bot1);
DrawLine(top1, bot1);
DrawLine(top2, bot2);
DrawLine(top3, bot3);
DrawLine(top4, bot4);
leds.SwapOnVsync(canvas);
// force 30 FPS
var elapsed = Environment.TickCount64 - frameStart;
if (elapsed < 33) Thread.Sleep(33 - (int)elapsed);
}
void DrawLine(Vector4 a, Vector4 b) => canvas.DrawLine(
(int)(a.X * a.W + centerX), (int)(a.Y * a.W + centerY),
(int)(b.X * b.W + centerX), (int)(b.Y * b.W + centerY),
new(255, 255, 255));

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\RPiRgbLEDMatrix.csproj"/>
</ItemGroup>
</Project>