mirror of
https://github.com/DarthAffe/RGB.NET.git
synced 2025-12-12 17:48:31 +00:00
commit
be3f2bfa29
61
.github/workflows/ci.yml
vendored
Normal file
61
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
name: RGB.NET-CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ Development ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
- '**.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Git Semantic Version
|
||||
id: versioning
|
||||
uses: PaulHatch/semantic-version@v4.0.3
|
||||
with:
|
||||
short_tags: false
|
||||
format: "${major}.${minor}.${patch}-prerelease.${increment}"
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.versioning.outputs.version }}
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal --configuration Release
|
||||
- name: Upload a Build Artifact NET5
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET5
|
||||
path: bin/net5.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET6
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET6
|
||||
path: bin/net6.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET7
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET7
|
||||
path: bin/net7.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload Nuget Build Artifact
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-Nugets
|
||||
path: bin/*nupkg
|
||||
if-no-files-found: error
|
||||
- name: Nuget Push
|
||||
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
|
||||
24
.github/workflows/pr_verify.yml
vendored
Normal file
24
.github/workflows/pr_verify.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: PR-Verify
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, Development ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore --configuration Release /p:Version=0.0.0
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal --configuration Release
|
||||
|
||||
66
.github/workflows/release.yml
vendored
Normal file
66
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
name: RGB.NET-Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
- '**.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Git Semantic Version
|
||||
id: versioning
|
||||
uses: PaulHatch/semantic-version@v4.0.3
|
||||
with:
|
||||
short_tags: false
|
||||
format: "${major}.${minor}.${patch}"
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.versioning.outputs.version }}
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal --configuration Release
|
||||
- name: Upload a Build Artifact NET5
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET5
|
||||
path: bin/net5.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET6
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET6
|
||||
path: bin/net6.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload a Build Artifact NET7
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-NET7
|
||||
path: bin/net7.0/RGB.NET.*.dll
|
||||
if-no-files-found: error
|
||||
- name: Upload Nuget Build Artifact
|
||||
uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
name: RGB.NET-Nugets
|
||||
path: bin/*nupkg
|
||||
if-no-files-found: error
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ steps.versioning.outputs.version_tag }}
|
||||
generate_release_notes: true
|
||||
files: bin/net7.0/RGB.NET.*.dll
|
||||
- name: Nuget Push
|
||||
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
|
||||
@ -7,17 +7,22 @@
|
||||
<xsd:element name="Description" type="xsd:string" />
|
||||
<xsd:element name="Author" type="xsd:string" />
|
||||
<xsd:element name="Type" type="xsd:string" />
|
||||
<xsd:element name="Lighting" type="xsd:string" />
|
||||
<xsd:element name="Vendor" type="xsd:string" />
|
||||
<xsd:element name="Model" type="xsd:string" />
|
||||
<xsd:element name="Shape" type="xsd:string" />
|
||||
<xsd:element name="Width" type="xsd:double" />
|
||||
<xsd:element name="Height" type="xsd:double" />
|
||||
<xsd:element name="ImageBasePath" type="xsd:string" />
|
||||
<xsd:element name="DeviceImage" type="xsd:string" />
|
||||
<xsd:element name="LedUnitWidth" type="xsd:double" />
|
||||
<xsd:element name="LedUnitHeight" type="xsd:double" />
|
||||
<xsd:element name="Leds">
|
||||
<xsd:element name="CustomData">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:any />
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="Leds">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element maxOccurs="unbounded" name="Led">
|
||||
@ -28,6 +33,13 @@
|
||||
<xsd:element name="Y" type="xsd:string" />
|
||||
<xsd:element name="Width" type="xsd:string" />
|
||||
<xsd:element name="Height" type="xsd:string" />
|
||||
<xsd:element name="CustomData">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:any />
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
@ -35,32 +47,7 @@
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="LedImageLayouts">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element maxOccurs="unbounded" name="LedImageLayout">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="LedImages">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element maxOccurs="unbounded" name="LedImage">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="Id" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="Image" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Layout" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xs:schema>
|
||||
95
README.md
95
README.md
@ -1,30 +1,87 @@
|
||||
# RGB.NET
|
||||
[](https://github.com/DarthAffe/RGB.NET/releases)
|
||||
[](https://www.nuget.org/packages?q=rgb.net)
|
||||
[](https://github.com/DarthAffe/RGB.NET/blob/master/LICENSE)
|
||||
[](https://github.com/DarthAffe/RGB.NET/stargazers)
|
||||
[](https://discord.gg/9kytURv)
|
||||
|
||||
This project aims to unify the use of various RGB-devices.
|
||||
**It is currently under heavy development and will have breaking changes in the future!** Right now a lot of devices aren't working as expected and there are bugs/unfinished features. Please think about that when you consider using the library in this early stage.
|
||||
|
||||
If you want to help with layouting/testing devices or if you need support using the library feel free to join the [RGB.NET discord-channel](https://discord.gg/9kytURv).
|
||||
> **IMPORTANT NOTE**
|
||||
This is a library to integrate RGB-devices into your own application. It does not contain any executables!
|
||||
If you're looking for a full blown software solution to manage your RGB-devices, take a look at [Artemis](https://artemis-rgb.com/).
|
||||
|
||||
## Getting Started
|
||||
### Setup
|
||||
1. Add the [RGB.NET.Core](https://www.nuget.org/packages/RGB.NET.Core) and [Devices](https://www.nuget.org/packages?q=rgb.net.Devices)-Nugets for all devices you want to use.
|
||||
2. For some of the vendors SDK-libraries are needed. Check the contained Readmes for more information in that case.
|
||||
3. Create a new `RGBSurface`.
|
||||
```csharp
|
||||
RGBSurface surface = new RGBSurface();
|
||||
```
|
||||
|
||||
## Adding prerelease packages using NuGet ##
|
||||
This is the easiest and therefore preferred way to include RGB.NET in your project.
|
||||
4. Initialize the providers for all devices you want to use and add the devices to the surface. For example:
|
||||
```csharp
|
||||
CorsairDeviceProvider.Instance.Initialize(throwExceptions: true);
|
||||
surface.Attach(CorsairDeviceProvider.Instance.Devices);
|
||||
```
|
||||
The `Initialize`-method allows to load only devices of specific types by setting a filter and for debugging purposes allows to enable exception throwing. (By default they are catched and provided through the `Exception`-event.)
|
||||
You can also use the `Load`-Extension on the surface.
|
||||
```csharp
|
||||
surface.Load(CorsairDeviceProvider.Instance);
|
||||
```
|
||||
> While most device-providers are implemented in a way that supports fast loading like this some may have a different loading procedures. (For example the `WS281XDeviceProvider` requires device-definitions before loading.)
|
||||
|
||||
Since there aren't any release-packages right now you'll have to use the CI-feed from [http://nuget.arge.be](http://nuget.arge.be).
|
||||
You can include it either by adding ```http://nuget.arge.be/v3/index.json``` to your Visual Studio package sources or by adding this [NuGet.Config](https://github.com/DarthAffe/RGB.NET/tree/master/Documentation/NuGet.Config) to your project (at the same level as your solution).
|
||||
5. Add an update-trigger. In most cases the TimerUpdateTrigger is preferable, but you can also implement your own to fit your needs.
|
||||
```csharp
|
||||
surface.RegisterUpdateTrigger(new TimerUpdateTrigger());
|
||||
```
|
||||
> If you want to trigger updates manually the `ManualUpdateTrigger` should be used.
|
||||
|
||||
### .NET 4.5 Support ###
|
||||
At the end of the year with the release of .NET 5 the support for old .NET-Framwork versions will be droppped!
|
||||
It's not recommended to use RGB.NET in projects targeting .NET 4.x that aren't planned to be moved to Core/.NET 5 in the future.
|
||||
6. *This step is optional but recommended.* For rendering the location of each LED on the surface can be important. Since not all SDKs provide useful layout-information you might want to add Layouts to your devices. (TODO: add wiki article for this)
|
||||
Same goes for the location of the device on the surface. If you don't care about the exact location of the devices you can use:
|
||||
```csharp
|
||||
surface.AlignDevices();
|
||||
```
|
||||
|
||||
The basic setup is now complete and you can start setting up your rendering.
|
||||
|
||||
### Device-Layouts
|
||||
To be able to have devices with correct LED-locations and sizes they need to be layouted. Pre-created layouts can be found at https://github.com/DarthAffe/RGB.NET-Resources.
|
||||
### Basic Rendering
|
||||
As an example we'll add a moving rainbow over all devices on the surface.
|
||||
1. Create a led-group containing all leds on the surface (all devices)
|
||||
```csharp
|
||||
ILedGroup allLeds = new ListLedGroup(surface, surface.Leds);
|
||||
```
|
||||
|
||||
If you plan to create layouts for your own devices check out https://github.com/DarthAffe/RGB.NET/wiki/Creating-Layouts first. There's also a layout-editor which strongly simplifies most of the work: https://github.com/SpoinkyNL/RGB.NET-Layout-Editor
|
||||
2. Create a rainbow-gradient.
|
||||
```csharp
|
||||
RainbowGradient rainbow = new RainbowGradient();
|
||||
```
|
||||
|
||||
### Example usage of RGB.NET
|
||||
[](http://www.youtube.com/watch?v=JLRa0Wv4qso)
|
||||
3. Add a decorator to the gradient to make it move. (Decorators are
|
||||
```csharp
|
||||
rainbow.AddDecorator(new MoveGradientDecorator(surface));
|
||||
```
|
||||
|
||||
#### Example Projects
|
||||
[https://github.com/DarthAffe/KeyboardAudioVisualizer](https://github.com/DarthAffe/KeyboardAudioVisualizer)
|
||||
[https://github.com/DarthAffe/RGBSyncPlus](https://github.com/DarthAffe/RGBSyncPlus)
|
||||
4. Create a texture (the size - in this example 10, 10 - is not important here since the gradient shoukd be stretched anyway)
|
||||
```csharp
|
||||
ITexture texture = new ConicalGradientTexture(new Size(10, 10), rainbow);
|
||||
```
|
||||
|
||||
5. Add a brush rendering the texture to the led-group
|
||||
```csharp
|
||||
allLeds.Brush = new TextureBrush(texture);
|
||||
```
|
||||
|
||||
### Full example
|
||||
```csharp
|
||||
RGBSurface surface = new RGBSurface();
|
||||
surface.Load(CorsairDeviceProvider.Instance);
|
||||
surface.AlignDevices();
|
||||
|
||||
surface.RegisterUpdateTrigger(new TimerUpdateTrigger());
|
||||
|
||||
ILedGroup allLeds = new ListLedGroup(surface, surface.Leds);
|
||||
RainbowGradient rainbow = new RainbowGradient();
|
||||
rainbow.AddDecorator(new MoveGradientDecorator(surface));
|
||||
ITexture texture = new ConicalGradientTexture(new Size(10, 10), rainbow);
|
||||
allLeds.Brush = new TextureBrush(texture);
|
||||
```
|
||||
@ -1,119 +0,0 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable MemberCanBeProtected.Global
|
||||
// ReSharper disable ReturnTypeCanBeEnumerable.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using RGB.NET.Brushes.Gradients;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes
|
||||
{
|
||||
/// <inheritdoc cref="AbstractBrush" />
|
||||
/// <inheritdoc cref="IGradientBrush" />
|
||||
/// <summary>
|
||||
/// Represents a brush drawing a conical gradient.
|
||||
/// </summary>
|
||||
public class ConicalGradientBrush : AbstractBrush, IGradientBrush
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private float _origin = (float)Math.Atan2(-1, 0);
|
||||
/// <summary>
|
||||
/// Gets or sets the origin (radian-angle) this <see cref="ConicalGradientBrush"/> is drawn to. (default: -π/2)
|
||||
/// </summary>
|
||||
public float Origin
|
||||
{
|
||||
get => _origin;
|
||||
set => SetProperty(ref _origin, value);
|
||||
}
|
||||
|
||||
private Point _center = new Point(0.5, 0.5);
|
||||
/// <summary>
|
||||
/// Gets or sets the center <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="ConicalGradientBrush"/>. (default: 0.5, 0.5)
|
||||
/// </summary>
|
||||
public Point Center
|
||||
{
|
||||
get => _center;
|
||||
set => SetProperty(ref _center, value);
|
||||
}
|
||||
|
||||
private IGradient _gradient;
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets or sets the gradient drawn by the brush. If null it will default to full transparent.
|
||||
/// </summary>
|
||||
public IGradient Gradient
|
||||
{
|
||||
get => _gradient;
|
||||
set => SetProperty(ref _gradient, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
|
||||
/// </summary>
|
||||
public ConicalGradientBrush()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
|
||||
public ConicalGradientBrush(IGradient gradient)
|
||||
{
|
||||
this.Gradient = gradient;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="center">The center <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
|
||||
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
|
||||
public ConicalGradientBrush(Point center, IGradient gradient)
|
||||
{
|
||||
this.Center = center;
|
||||
this.Gradient = gradient;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="center">The center <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
|
||||
/// <param name="origin">The origin (radian-angle) the <see cref="T:RGB.NET.Core.IBrush" /> is drawn to.</param>
|
||||
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
|
||||
public ConicalGradientBrush(Point center, float origin, IGradient gradient)
|
||||
{
|
||||
this.Center = center;
|
||||
this.Origin = origin;
|
||||
this.Gradient = gradient;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
|
||||
{
|
||||
double centerX = rectangle.Size.Width * Center.X;
|
||||
double centerY = rectangle.Size.Height * Center.Y;
|
||||
|
||||
double angle = Math.Atan2(renderTarget.Point.Y - centerY, renderTarget.Point.X - centerX) - Origin;
|
||||
if (angle < 0) angle += Math.PI * 2;
|
||||
double offset = angle / (Math.PI * 2);
|
||||
|
||||
return Gradient.GetColor(offset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
using RGB.NET.Brushes.Gradients;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic gradient-brush.
|
||||
/// </summary>
|
||||
public interface IGradientBrush : IBrush
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IGradient"/> used by this <see cref="IGradientBrush"/>.
|
||||
/// </summary>
|
||||
IGradient Gradient { get; }
|
||||
}
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
// ReSharper disable CollectionNeverUpdated.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable MemberCanBeProtected.Global
|
||||
// ReSharper disable ReturnTypeCanBeEnumerable.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using RGB.NET.Brushes.Gradients;
|
||||
using RGB.NET.Brushes.Helper;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes
|
||||
{
|
||||
/// <inheritdoc cref="AbstractBrush" />
|
||||
/// <inheritdoc cref="IGradientBrush" />
|
||||
/// <summary>
|
||||
/// Represents a brush drawing a linear gradient.
|
||||
/// </summary>
|
||||
public class LinearGradientBrush : AbstractBrush, IGradientBrush
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private Point _startPoint = new Point(0, 0.5);
|
||||
/// <summary>
|
||||
/// Gets or sets the start <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="LinearGradientBrush"/>. (default: 0.0, 0.5)
|
||||
/// </summary>
|
||||
public Point StartPoint
|
||||
{
|
||||
get => _startPoint;
|
||||
set => SetProperty(ref _startPoint, value);
|
||||
}
|
||||
|
||||
private Point _endPoint = new Point(1, 0.5);
|
||||
/// <summary>
|
||||
/// Gets or sets the end <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="LinearGradientBrush"/>. (default: 1.0, 0.5)
|
||||
/// </summary>
|
||||
public Point EndPoint
|
||||
{
|
||||
get => _endPoint;
|
||||
set => SetProperty(ref _endPoint, value);
|
||||
}
|
||||
|
||||
private IGradient _gradient;
|
||||
/// <inheritdoc />
|
||||
public IGradient Gradient
|
||||
{
|
||||
get => _gradient;
|
||||
set => SetProperty(ref _gradient, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
|
||||
/// </summary>
|
||||
public LinearGradientBrush()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.LinearGradientBrush" />.</param>
|
||||
public LinearGradientBrush(IGradient gradient)
|
||||
{
|
||||
this.Gradient = gradient;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="startPoint">The start <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
|
||||
/// <param name="endPoint">The end <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
|
||||
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.LinearGradientBrush" />.</param>
|
||||
public LinearGradientBrush(Point startPoint, Point endPoint, IGradient gradient)
|
||||
{
|
||||
this.StartPoint = startPoint;
|
||||
this.EndPoint = endPoint;
|
||||
this.Gradient = gradient;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets the color at an specific point assuming the brush is drawn into the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
|
||||
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
|
||||
/// <returns>The color at the specified point.</returns>
|
||||
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
|
||||
{
|
||||
if (Gradient == null) return Color.Transparent;
|
||||
|
||||
Point startPoint = new Point(StartPoint.X * rectangle.Size.Width, StartPoint.Y * rectangle.Size.Height);
|
||||
Point endPoint = new Point(EndPoint.X * rectangle.Size.Width, EndPoint.Y * rectangle.Size.Height);
|
||||
|
||||
double offset = GradientHelper.CalculateLinearGradientOffset(startPoint, endPoint, renderTarget.Point);
|
||||
return Gradient.GetColor(offset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using RGB.NET.Brushes.Gradients;
|
||||
using RGB.NET.Brushes.Helper;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes
|
||||
{
|
||||
/// <inheritdoc cref="AbstractBrush" />
|
||||
/// <inheritdoc cref="IGradientBrush" />
|
||||
/// <summary>
|
||||
/// Represents a brush drawing a radial gradient around a center point.
|
||||
/// </summary>
|
||||
public class RadialGradientBrush : AbstractBrush, IGradientBrush
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private Point _center = new Point(0.5, 0.5);
|
||||
/// <summary>
|
||||
/// Gets or sets the center <see cref="Point"/> (as percentage in the range [0..1]) around which the <see cref="RadialGradientBrush"/> should be drawn. (default: 0.5, 0.5)
|
||||
/// </summary>
|
||||
public Point Center
|
||||
{
|
||||
get => _center;
|
||||
set => SetProperty(ref _center, value);
|
||||
}
|
||||
|
||||
private IGradient _gradient;
|
||||
/// <inheritdoc />
|
||||
public IGradient Gradient
|
||||
{
|
||||
get => _gradient;
|
||||
set => SetProperty(ref _gradient, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
|
||||
/// </summary>
|
||||
public RadialGradientBrush()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="gradient">The gradient drawn by the brush.</param>
|
||||
public RadialGradientBrush(IGradient gradient)
|
||||
{
|
||||
this.Gradient = gradient;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="center">The center point (as percentage in the range [0..1]).</param>
|
||||
/// <param name="gradient">The gradient drawn by the brush.</param>
|
||||
public RadialGradientBrush(Point center, IGradient gradient)
|
||||
{
|
||||
this.Center = center;
|
||||
this.Gradient = gradient;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
|
||||
{
|
||||
if (Gradient == null) return Color.Transparent;
|
||||
|
||||
Point centerPoint = new Point(rectangle.Location.X + (rectangle.Size.Width * Center.X), rectangle.Location.Y + (rectangle.Size.Height * Center.Y));
|
||||
|
||||
// Calculate the distance to the farthest point from the center as reference (this has to be a corner)
|
||||
// ReSharper disable once RedundantCast - never trust this ...
|
||||
double refDistance = Math.Max(Math.Max(Math.Max(GradientHelper.CalculateDistance(rectangle.Location, centerPoint),
|
||||
GradientHelper.CalculateDistance(new Point(rectangle.Location.X + rectangle.Size.Width, rectangle.Location.Y), centerPoint)),
|
||||
GradientHelper.CalculateDistance(new Point(rectangle.Location.X, rectangle.Location.Y + rectangle.Size.Height), centerPoint)),
|
||||
GradientHelper.CalculateDistance(new Point(rectangle.Location.X + rectangle.Size.Width, rectangle.Location.Y + rectangle.Size.Height), centerPoint));
|
||||
|
||||
double distance = GradientHelper.CalculateDistance(renderTarget.Point, centerPoint);
|
||||
double offset = distance / refDistance;
|
||||
return Gradient.GetColor(offset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a brush drawing only a single color.
|
||||
/// </summary>
|
||||
public class SolidColorBrush : AbstractBrush
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private Color _color;
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Color"/> drawn by this <see cref="SolidColorBrush"/>.
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get => _color;
|
||||
set => SetProperty(ref _color, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.SolidColorBrush" /> class.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="P:RGB.NET.Brushes.SolidColorBrush.Color" /> drawn by this <see cref="T:RGB.NET.Brushes.SolidColorBrush" />.</param>
|
||||
public SolidColorBrush(Color color)
|
||||
{
|
||||
this.Color = color;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget) => Color;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Color" /> to a <see cref="SolidColorBrush" />.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to convert.</param>
|
||||
public static explicit operator SolidColorBrush(Color color) => new SolidColorBrush(color);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="SolidColorBrush" /> to a <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <param name="brush">The <see cref="Color"/> to convert.</param>
|
||||
public static implicit operator Color(SolidColorBrush brush) => brush.Color;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Brushes.Gradients.IGradient" />.
|
||||
/// </summary>
|
||||
public interface IGradientDecorator : IDecorator
|
||||
{ }
|
||||
}
|
||||
@ -1,150 +0,0 @@
|
||||
// ReSharper disable MemberCanBeProtected.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes.Gradients
|
||||
{
|
||||
/// <inheritdoc cref="AbstractDecoratable{T}" />
|
||||
/// <inheritdoc cref="IGradient" />
|
||||
/// <summary>
|
||||
/// Represents a basic gradient.
|
||||
/// </summary>
|
||||
public abstract class AbstractGradient : AbstractDecoratable<IGradientDecorator>, IGradient
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of the stops used by this <see cref="AbstractGradient"/>.
|
||||
/// </summary>
|
||||
public ObservableCollection<GradientStop> GradientStops { get; } = new ObservableCollection<GradientStop>();
|
||||
|
||||
private bool _wrapGradient;
|
||||
/// <summary>
|
||||
/// Gets or sets if the Gradient wraps around if there isn't a second stop to take.
|
||||
/// Example: There is a stop at offset 0.0, 0.5 and 0.75.
|
||||
/// Without wrapping offset 1.0 will be calculated the same as 0.75; with wrapping it would be the same as 0.0.
|
||||
/// </summary>
|
||||
public bool WrapGradient
|
||||
{
|
||||
get => _wrapGradient;
|
||||
set => SetProperty(ref _wrapGradient, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler GradientChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractGradient"/> class.
|
||||
/// </summary>
|
||||
protected AbstractGradient()
|
||||
{
|
||||
GradientStops.CollectionChanged += GradientCollectionChanged;
|
||||
PropertyChanged += (sender, args) => OnGradientChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractGradient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
|
||||
protected AbstractGradient(params GradientStop[] gradientStops)
|
||||
{
|
||||
GradientStops.CollectionChanged += GradientCollectionChanged;
|
||||
PropertyChanged += (sender, args) => OnGradientChanged();
|
||||
|
||||
foreach (GradientStop gradientStop in gradientStops)
|
||||
GradientStops.Add(gradientStop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractGradient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="wrapGradient">Specifies whether the gradient should wrapp or not (see <see cref="WrapGradient"/> for an example of what this means).</param>
|
||||
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
|
||||
protected AbstractGradient(bool wrapGradient, params GradientStop[] gradientStops)
|
||||
{
|
||||
this.WrapGradient = wrapGradient;
|
||||
|
||||
GradientStops.CollectionChanged += GradientCollectionChanged;
|
||||
PropertyChanged += (sender, args) => OnGradientChanged();
|
||||
|
||||
foreach (GradientStop gradientStop in gradientStops)
|
||||
GradientStops.Add(gradientStop);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clips the offset and ensures, that it is inside the bounds of the stop list.
|
||||
/// </summary>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
protected double ClipOffset(double offset)
|
||||
{
|
||||
double max = GradientStops.Max(n => n.Offset);
|
||||
if (offset > max)
|
||||
return max;
|
||||
|
||||
double min = GradientStops.Min(n => n.Offset);
|
||||
return offset < min ? min : offset;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Color GetColor(double offset);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Move(double offset)
|
||||
{
|
||||
offset /= 360.0;
|
||||
|
||||
foreach (GradientStop gradientStop in GradientStops)
|
||||
gradientStop.Offset += offset;
|
||||
|
||||
while (GradientStops.All(x => x.Offset > 1))
|
||||
foreach (GradientStop gradientStop in GradientStops)
|
||||
gradientStop.Offset -= 1;
|
||||
|
||||
while (GradientStops.All(x => x.Offset < 0))
|
||||
foreach (GradientStop gradientStop in GradientStops)
|
||||
gradientStop.Offset += 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should be called to indicate that the gradient was changed.
|
||||
/// </summary>
|
||||
protected void OnGradientChanged() => GradientChanged?.Invoke(this, null);
|
||||
|
||||
private void GradientCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.OldItems != null)
|
||||
foreach (GradientStop gradientStop in e.OldItems)
|
||||
gradientStop.PropertyChanged -= GradientStopChanged;
|
||||
|
||||
if (e.NewItems != null)
|
||||
foreach (GradientStop gradientStop in e.NewItems)
|
||||
gradientStop.PropertyChanged += GradientStopChanged;
|
||||
|
||||
OnGradientChanged();
|
||||
}
|
||||
|
||||
private void GradientStopChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) => OnGradientChanged();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes.Gradients
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a stop on a gradient.
|
||||
/// </summary>
|
||||
public class GradientStop : AbstractBindable
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private double _offset;
|
||||
/// <summary>
|
||||
/// Gets or sets the percentage offset to place this <see cref="GradientStop"/>. This should be inside the range of [0..1] but it's not necessary.
|
||||
/// </summary>
|
||||
public double Offset
|
||||
{
|
||||
get => _offset;
|
||||
set => SetProperty(ref _offset, value);
|
||||
}
|
||||
|
||||
private Color _color;
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Color"/> of this <see cref="GradientStop"/>.
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get => _color;
|
||||
set => SetProperty(ref _color, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GradientStop"/> class.
|
||||
/// </summary>
|
||||
/// <param name="offset">The percentage offset to place this <see cref="GradientStop"/>.</param>
|
||||
/// <param name="color">The <see cref="Color"/> of the <see cref="GradientStop"/>.</param>
|
||||
public GradientStop(double offset, Color color)
|
||||
{
|
||||
this.Offset = offset;
|
||||
this.Color = color;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes.Gradients
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic gradient.
|
||||
/// </summary>
|
||||
public interface IGradient : IDecoratable<IGradientDecorator>
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs if the <see cref="IGradient"/> is changed.
|
||||
/// </summary>
|
||||
event EventHandler GradientChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Color"/> of the <see cref="IGradient"/> on the specified offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">The percentage offset to take the <see cref="Color"/> from.</param>
|
||||
/// <returns>The <see cref="Color"/> at the specific offset.</returns>
|
||||
Color GetColor(double offset);
|
||||
|
||||
/// <summary>
|
||||
/// Moves the <see cref="IGradient"/> by the provided offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset the <see cref="IGradient"/> should be moved.</param>
|
||||
void Move(double offset);
|
||||
}
|
||||
}
|
||||
@ -1,152 +0,0 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes.Gradients
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a linear interpolated gradient with n stops.
|
||||
/// </summary>
|
||||
public class LinearGradient : AbstractGradient
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private bool _isOrderedGradientListDirty = true;
|
||||
private LinkedList<GradientStop> _orderedGradientStops;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.Gradients.LinearGradient" /> class.
|
||||
/// </summary>
|
||||
public LinearGradient()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.Gradients.LinearGradient" /> class.
|
||||
/// </summary>
|
||||
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
|
||||
public LinearGradient(params GradientStop[] gradientStops)
|
||||
: base(gradientStops)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.Gradients.AbstractGradient" /> class.
|
||||
/// </summary>
|
||||
/// <param name="wrapGradient">Specifies whether the gradient should wrapp or not (see <see cref="P:RGB.NET.Brushes.Gradients.AbstractGradient.WrapGradient" /> for an example of what this means).</param>
|
||||
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
|
||||
public LinearGradient(bool wrapGradient, params GradientStop[] gradientStops)
|
||||
: base(wrapGradient, gradientStops)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
void OnGradientStopOnPropertyChanged(object sender, PropertyChangedEventArgs args) => _isOrderedGradientListDirty = true;
|
||||
|
||||
foreach (GradientStop gradientStop in GradientStops)
|
||||
gradientStop.PropertyChanged += OnGradientStopOnPropertyChanged;
|
||||
|
||||
GradientStops.CollectionChanged += (sender, args) =>
|
||||
{
|
||||
if (args.OldItems != null)
|
||||
foreach (GradientStop gradientStop in args.OldItems)
|
||||
gradientStop.PropertyChanged -= OnGradientStopOnPropertyChanged;
|
||||
|
||||
if (args.NewItems != null)
|
||||
foreach (GradientStop gradientStop in args.NewItems)
|
||||
gradientStop.PropertyChanged += OnGradientStopOnPropertyChanged;
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets the linear interpolated <see cref="T:RGB.NET.Core.Color" /> at the given offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">The percentage offset to take the color from.</param>
|
||||
/// <returns>The <see cref="T:RGB.NET.Core.Color" /> at the specific offset.</returns>
|
||||
public override Color GetColor(double offset)
|
||||
{
|
||||
if (GradientStops.Count == 0) return Color.Transparent;
|
||||
if (GradientStops.Count == 1) return GradientStops[0].Color;
|
||||
|
||||
if (_isOrderedGradientListDirty)
|
||||
_orderedGradientStops = new LinkedList<GradientStop>(GradientStops.OrderBy(x => x.Offset));
|
||||
|
||||
(GradientStop gsBefore, GradientStop gsAfter) = GetEnclosingGradientStops(offset, _orderedGradientStops, WrapGradient);
|
||||
|
||||
double blendFactor = 0;
|
||||
if (!gsBefore.Offset.Equals(gsAfter.Offset))
|
||||
blendFactor = ((offset - gsBefore.Offset) / (gsAfter.Offset - gsBefore.Offset));
|
||||
|
||||
double colA = ((gsAfter.Color.A - gsBefore.Color.A) * blendFactor) + gsBefore.Color.A;
|
||||
double colR = ((gsAfter.Color.R - gsBefore.Color.R) * blendFactor) + gsBefore.Color.R;
|
||||
double colG = ((gsAfter.Color.G - gsBefore.Color.G) * blendFactor) + gsBefore.Color.G;
|
||||
double colB = ((gsAfter.Color.B - gsBefore.Color.B) * blendFactor) + gsBefore.Color.B;
|
||||
|
||||
return new Color(colA, colR, colG, colB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the two <see cref="GradientStop"/>s encapsulating the given offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">The reference offset.</param>
|
||||
/// <param name="orderedStops">The ordered list of <see cref="GradientStop"/> to choose from.</param>
|
||||
/// <param name="wrap">Bool indicating if the gradient should be wrapped or not.</param>
|
||||
/// <returns></returns>
|
||||
protected virtual (GradientStop gsBefore, GradientStop gsAfter) GetEnclosingGradientStops(double offset, LinkedList<GradientStop> orderedStops, bool wrap)
|
||||
{
|
||||
LinkedList<GradientStop> gradientStops = new LinkedList<GradientStop>(orderedStops);
|
||||
|
||||
if (wrap)
|
||||
{
|
||||
GradientStop gsBefore, gsAfter;
|
||||
|
||||
do
|
||||
{
|
||||
gsBefore = gradientStops.LastOrDefault(n => n.Offset <= offset);
|
||||
if (gsBefore == null)
|
||||
{
|
||||
GradientStop lastStop = gradientStops.Last.Value;
|
||||
gradientStops.AddFirst(new GradientStop(lastStop.Offset - 1, lastStop.Color));
|
||||
gradientStops.RemoveLast();
|
||||
}
|
||||
|
||||
gsAfter = gradientStops.FirstOrDefault(n => n.Offset >= offset);
|
||||
if (gsAfter == null)
|
||||
{
|
||||
GradientStop firstStop = gradientStops.First.Value;
|
||||
gradientStops.AddLast(new GradientStop(firstStop.Offset + 1, firstStop.Color));
|
||||
gradientStops.RemoveFirst();
|
||||
}
|
||||
|
||||
} while ((gsBefore == null) || (gsAfter == null));
|
||||
|
||||
return (gsBefore, gsAfter);
|
||||
}
|
||||
|
||||
offset = ClipOffset(offset);
|
||||
return (gradientStops.Last(n => n.Offset <= offset), gradientStops.First(n => n.Offset >= offset));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,108 +0,0 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes.Gradients
|
||||
{
|
||||
/// <inheritdoc cref="AbstractDecoratable{T}" />
|
||||
/// <inheritdoc cref="IGradient" />
|
||||
/// <summary>
|
||||
/// Represents a rainbow gradient which circles through all colors of the HUE-color-space.<br />
|
||||
/// See <see href="http://upload.wikimedia.org/wikipedia/commons/a/ad/HueScale.svg" /> as reference.
|
||||
/// </summary>
|
||||
public class RainbowGradient : AbstractDecoratable<IGradientDecorator>, IGradient
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private double _startHue;
|
||||
/// <summary>
|
||||
/// Gets or sets the hue (in degrees) to start from.
|
||||
/// </summary>
|
||||
public double StartHue
|
||||
{
|
||||
get => _startHue;
|
||||
set => SetProperty(ref _startHue, value);
|
||||
}
|
||||
|
||||
private double _endHue;
|
||||
/// <summary>
|
||||
/// Gets or sets the hue (in degrees) to end the with.
|
||||
/// </summary>
|
||||
public double EndHue
|
||||
{
|
||||
get => _endHue;
|
||||
set => SetProperty(ref _endHue, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler GradientChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RainbowGradient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="startHue">The hue (in degrees) to start from (default: 0)</param>
|
||||
/// <param name="endHue">The hue (in degrees) to end with (default: 360)</param>
|
||||
public RainbowGradient(double startHue = 0, double endHue = 360)
|
||||
{
|
||||
this.StartHue = startHue;
|
||||
this.EndHue = endHue;
|
||||
|
||||
PropertyChanged += (sender, args) => OnGradientChanged();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets the color on the rainbow at the given offset.
|
||||
/// </summary>
|
||||
/// <param name="offset">The percentage offset to take the color from.</param>
|
||||
/// <returns>The color at the specific offset.</returns>
|
||||
public Color GetColor(double offset)
|
||||
{
|
||||
double range = EndHue - StartHue;
|
||||
double hue = StartHue + (range * offset);
|
||||
return HSVColor.Create(hue, 1, 1);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Move(double offset)
|
||||
{
|
||||
// RainbowGradient is calculated inverse
|
||||
offset *= -1;
|
||||
|
||||
StartHue += offset;
|
||||
EndHue += offset;
|
||||
|
||||
while ((StartHue > 360) && (EndHue > 360))
|
||||
{
|
||||
StartHue -= 360;
|
||||
EndHue -= 360;
|
||||
}
|
||||
while ((StartHue < -360) && (EndHue < -360))
|
||||
{
|
||||
StartHue += 360;
|
||||
EndHue += 360;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should be called to indicate that the gradient was changed.
|
||||
/// </summary>
|
||||
protected void OnGradientChanged() => GradientChanged?.Invoke(this, null);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
using System;
|
||||
using RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Brushes.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for gradient related things.
|
||||
/// </summary>
|
||||
public static class GradientHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
// Based on https://web.archive.org/web/20170125201230/https://dotupdate.wordpress.com/2008/01/28/find-the-color-of-a-point-in-a-lineargradientbrush/
|
||||
/// <summary>
|
||||
/// Calculates the offset of an given <see cref="Point"/> on an gradient.
|
||||
/// </summary>
|
||||
/// <param name="startPoint">The start <see cref="Point"/> of the gradient.</param>
|
||||
/// <param name="endPoint">The end <see cref="Point"/> of the gradient.</param>
|
||||
/// <param name="point">The <see cref="Point"/> on the gradient to which the offset is calculated.</param>
|
||||
/// <returns>The offset of the <see cref="Point"/> on the gradient.</returns>
|
||||
public static double CalculateLinearGradientOffset(Point startPoint, Point endPoint, Point point)
|
||||
{
|
||||
Point intersectingPoint;
|
||||
if (startPoint.Y.Equals(endPoint.Y)) // Horizontal case
|
||||
intersectingPoint = new Point(point.X, startPoint.Y);
|
||||
|
||||
else if (startPoint.X.Equals(endPoint.X)) // Vertical case
|
||||
intersectingPoint = new Point(startPoint.X, point.Y);
|
||||
|
||||
else // Diagonal case
|
||||
{
|
||||
double slope = (endPoint.Y - startPoint.Y) / (endPoint.X - startPoint.X);
|
||||
double orthogonalSlope = -1 / slope;
|
||||
|
||||
double startYIntercept = startPoint.Y - (slope * startPoint.X);
|
||||
double pointYIntercept = point.Y - (orthogonalSlope * point.X);
|
||||
|
||||
double intersectingPointX = (pointYIntercept - startYIntercept) / (slope - orthogonalSlope);
|
||||
double intersectingPointY = (slope * intersectingPointX) + startYIntercept;
|
||||
intersectingPoint = new Point(intersectingPointX, intersectingPointY);
|
||||
}
|
||||
|
||||
// Calculate distances relative to the vector start
|
||||
double intersectDistance = CalculateDistance(intersectingPoint, startPoint, endPoint);
|
||||
double gradientLength = CalculateDistance(endPoint, startPoint, endPoint);
|
||||
|
||||
return intersectDistance / gradientLength;
|
||||
}
|
||||
|
||||
// Based on https://web.archive.org/web/20170125201230/https://dotupdate.wordpress.com/2008/01/28/find-the-color-of-a-point-in-a-lineargradientbrush/
|
||||
/// <summary>
|
||||
/// Returns the signed magnitude of a <see cref="Point"/> on a vector.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> on the vector of which the magnitude should be calculated.</param>
|
||||
/// <param name="origin">The origin of the vector.</param>
|
||||
/// <param name="direction">The direction of the vector.</param>
|
||||
/// <returns>The signed magnitude of a <see cref="Point"/> on a vector.</returns>
|
||||
public static double CalculateDistance(Point point, Point origin, Point direction)
|
||||
{
|
||||
double distance = CalculateDistance(point, origin);
|
||||
|
||||
return (((point.Y < origin.Y) && (direction.Y > origin.Y)) ||
|
||||
((point.Y > origin.Y) && (direction.Y < origin.Y)) ||
|
||||
((point.Y.Equals(origin.Y)) && (point.X < origin.X) && (direction.X > origin.X)) ||
|
||||
((point.Y.Equals(origin.Y)) && (point.X > origin.X) && (direction.X < origin.X)))
|
||||
? -distance : distance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculated the distance between two <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>The distance between the two <see cref="Point"/>.</returns>
|
||||
public static double CalculateDistance(Point point1, Point point2) => Math.Sqrt(((point1.Y - point2.Y) * (point1.Y - point2.Y)) + ((point1.X - point2.X) * (point1.X - point2.X)));
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,131 +0,0 @@
|
||||
// ReSharper disable VirtualMemberNeverOverriden.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable VirtualMemberNeverOverridden.Global
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <inheritdoc cref="AbstractDecoratable{T}" />
|
||||
/// <inheritdoc cref="IBrush" />
|
||||
/// <summary>
|
||||
/// Represents a basic brush.
|
||||
/// </summary>
|
||||
public abstract class AbstractBrush : AbstractDecoratable<IBrushDecorator>, IBrush
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public BrushCalculationMode BrushCalculationMode { get; set; } = BrushCalculationMode.Relative;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double Brightness { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public double Opacity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<IColorCorrection> ColorCorrections { get; } = new List<IColorCorrection>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Rectangle RenderedRectangle { get; protected set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<BrushRenderTarget, Color> RenderedTargets { get; } = new Dictionary<BrushRenderTarget, Color>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractBrush"/> class.
|
||||
/// </summary>
|
||||
/// <param name="brightness">The overall percentage brightness of the brush. (default: 1.0)</param>
|
||||
/// <param name="opacity">The overall percentage opacity of the brush. (default: 1.0)</param>
|
||||
protected AbstractBrush(double brightness = 1, double opacity = 1)
|
||||
{
|
||||
this.Brightness = brightness;
|
||||
this.Opacity = opacity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets)
|
||||
{
|
||||
RenderedRectangle = rectangle;
|
||||
RenderedTargets.Clear();
|
||||
|
||||
foreach (BrushRenderTarget renderTarget in renderTargets)
|
||||
{
|
||||
Color color = GetColorAtPoint(rectangle, renderTarget);
|
||||
color = ApplyDecorators(rectangle, renderTarget, color);
|
||||
RenderedTargets[renderTarget] = color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies all attached and enabled decorators to the brush.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
|
||||
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
|
||||
/// <param name="color">The <see cref="Color"/> to be modified.</param>
|
||||
protected virtual Color ApplyDecorators(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)
|
||||
{
|
||||
lock (Decorators)
|
||||
foreach (IBrushDecorator decorator in Decorators)
|
||||
if (decorator.IsEnabled)
|
||||
color = decorator.ManipulateColor(rectangle, renderTarget, color);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void PerformFinalize()
|
||||
{
|
||||
List<BrushRenderTarget> renderTargets = RenderedTargets.Keys.ToList();
|
||||
foreach (BrushRenderTarget renderTarget in renderTargets)
|
||||
RenderedTargets[renderTarget] = FinalizeColor(RenderedTargets[renderTarget]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color at an specific point assuming the brush is drawn into the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
|
||||
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
|
||||
/// <returns>The color at the specified point.</returns>
|
||||
protected abstract Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes the color by appliing the overall brightness and opacity.<br/>
|
||||
/// This method should always be the last call of a <see cref="GetColorAtPoint" /> implementation.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to finalize.</param>
|
||||
/// <returns>The finalized color.</returns>
|
||||
protected virtual Color FinalizeColor(Color color)
|
||||
{
|
||||
if (ColorCorrections.Count > 0)
|
||||
foreach (IColorCorrection colorCorrection in ColorCorrections)
|
||||
color = colorCorrection.ApplyTo(color);
|
||||
|
||||
// Since we use HSV to calculate there is no way to make a color 'brighter' than 100%
|
||||
// Be carefull with the naming: Since we use HSV the correct term is 'value' but outside we call it 'brightness'
|
||||
// THIS IS NOT A HSB CALCULATION!!!
|
||||
if (Brightness < 1)
|
||||
color = color.MultiplyHSV(value: Brightness.Clamp(0, 1));
|
||||
|
||||
if (Opacity < 1)
|
||||
color = color.MultiplyA(Opacity.Clamp(0, 1));
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a list of all brush calculation modes.
|
||||
/// </summary>
|
||||
public enum BrushCalculationMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The calculation <see cref="Rectangle"/> for <see cref="IBrush"/> will be the rectangle around the <see cref="ILedGroup"/> the <see cref="IBrush"/> is applied to.
|
||||
/// </summary>
|
||||
Relative,
|
||||
|
||||
/// <summary>
|
||||
/// The calculation <see cref="Rectangle"/> for <see cref="IBrush"/> will always be the rectangle completly containing all affected <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
Absolute
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single target of a brush render.
|
||||
/// </summary>
|
||||
public class BrushRenderTarget
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target-<see cref="Core.Led"/>.
|
||||
/// </summary>
|
||||
public Led Led { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Core.Rectangle"/> representing the area to render the target-<see cref="Core.Led"/>.
|
||||
/// </summary>
|
||||
public Rectangle Rectangle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Core.Point"/> representing the position to render the target-<see cref="Core.Led"/>.
|
||||
/// </summary>
|
||||
public Point Point { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BrushRenderTarget"/> class.
|
||||
/// </summary>
|
||||
/// <param name="led">The target-<see cref="Core.Led"/>.</param>
|
||||
/// <param name="rectangle">The <see cref="Core.Rectangle"/> representing the area to render the target-<see cref="Core.Led"/>.</param>
|
||||
public BrushRenderTarget(Led led, Rectangle rectangle)
|
||||
{
|
||||
this.Led = led;
|
||||
this.Rectangle = rectangle;
|
||||
|
||||
this.Point = rectangle.Center;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
// ReSharper disable UnusedMemberInSuper.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable ReturnTypeCanBeEnumerable.Global
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a basic brush.
|
||||
/// </summary>
|
||||
public interface IBrush : IDecoratable<IBrushDecorator>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets if the <see cref="IBrush"/> is enabled and will be drawn on an update.
|
||||
/// </summary>
|
||||
bool IsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the calculation mode used for the rectangle/points used for color-selection in brushes.
|
||||
/// </summary>
|
||||
BrushCalculationMode BrushCalculationMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the overall percentage brightness of the <see cref="IBrush"/>.
|
||||
/// </summary>
|
||||
double Brightness { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the overall percentage opacity of the <see cref="IBrush"/>.
|
||||
/// </summary>
|
||||
double Opacity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IColorCorrection"/> used to correct the colors of the <see cref="IBrush"/>.
|
||||
/// </summary>
|
||||
IList<IColorCorrection> ColorCorrections { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RenderedRectangle"/> used in the last render pass.
|
||||
/// </summary>
|
||||
Rectangle RenderedRectangle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="Color"/> for <see cref="BrushRenderTarget"/> calculated in the last render pass.
|
||||
/// </summary>
|
||||
Dictionary<BrushRenderTarget, Color> RenderedTargets { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs the render pass of the <see cref="IBrush"/> and calculates the raw <see cref="Color"/> for all requested <see cref="BrushRenderTarget"/>.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The <see cref="Rectangle"/> in which the brush should be drawn.</param>
|
||||
/// <param name="renderTargets">The <see cref="BrushRenderTarget"/> (keys/points) of which the color should be calculated.</param>
|
||||
void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets);
|
||||
|
||||
/// <summary>
|
||||
/// Performs the finalize pass of the <see cref="IBrush"/> and calculates the final <see cref="ColorCorrections"/> for all previously calculated <see cref="BrushRenderTarget"/>.
|
||||
/// </summary>
|
||||
void PerformFinalize();
|
||||
}
|
||||
}
|
||||
@ -1,80 +1,62 @@
|
||||
namespace RGB.NET.Core
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the default-behavior for the work with colors.
|
||||
/// </summary>
|
||||
public class DefaultColorBehavior : IColorBehavior
|
||||
{
|
||||
public class DefaultColorBehavior : IColorBehavior
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts the individual byte values of this <see cref="Color"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. For example "[A: 255, R: 255, G: 0, B: 0]".</returns>
|
||||
public virtual string ToString(in Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to test.</param>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool Equals(in Color color, object? obj)
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private static DefaultColorBehavior _instance = new DefaultColorBehavior();
|
||||
/// <summary>
|
||||
/// Gets the singleton instance of <see cref="DefaultColorBehavior"/>.
|
||||
/// </summary>
|
||||
public static DefaultColorBehavior Instance { get; } = _instance;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private DefaultColorBehavior()
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts the individual byte values of this <see cref="Color"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. For example "[A: 255, R: 255, G: 0, B: 0]".</returns>
|
||||
public virtual string ToString(Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public virtual bool Equals(Color color, object obj)
|
||||
{
|
||||
if (!(obj is Color)) return false;
|
||||
|
||||
(double a, double r, double g, double b) = ((Color)obj).GetRGB();
|
||||
return color.A.EqualsInTolerance(a) && color.R.EqualsInTolerance(r) && color.G.EqualsInTolerance(g) && color.B.EqualsInTolerance(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
|
||||
public virtual int GetHashCode(Color color)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = color.A.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ color.R.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ color.G.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ color.B.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blends a <see cref="Color"/> over this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to blend.</param>
|
||||
public virtual Color Blend(Color baseColor, Color blendColor)
|
||||
{
|
||||
if (blendColor.A.EqualsInTolerance(0)) return baseColor;
|
||||
|
||||
if (blendColor.A.EqualsInTolerance(1))
|
||||
return blendColor;
|
||||
|
||||
double resultA = (1.0 - ((1.0 - blendColor.A) * (1.0 - baseColor.A)));
|
||||
double resultR = (((blendColor.R * blendColor.A) / resultA) + ((baseColor.R * baseColor.A * (1.0 - blendColor.A)) / resultA));
|
||||
double resultG = (((blendColor.G * blendColor.A) / resultA) + ((baseColor.G * baseColor.A * (1.0 - blendColor.A)) / resultA));
|
||||
double resultB = (((blendColor.B * blendColor.A) / resultA) + ((baseColor.B * baseColor.A * (1.0 - blendColor.A)) / resultA));
|
||||
|
||||
return new Color(resultA, resultR, resultG, resultB);
|
||||
}
|
||||
|
||||
#endregion
|
||||
if (obj is not Color color2) return false;
|
||||
|
||||
return color.A.EqualsInTolerance(color2.A)
|
||||
&& color.R.EqualsInTolerance(color2.R)
|
||||
&& color.G.EqualsInTolerance(color2.G)
|
||||
&& color.B.EqualsInTolerance(color2.B);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
|
||||
public virtual int GetHashCode(in Color color) => HashCode.Combine(color.A, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Blends a <see cref="Color"/> over this color.
|
||||
/// </summary>
|
||||
/// <param name="baseColor">The <see cref="Color"/> to to blend over.</param>
|
||||
/// <param name="blendColor">The <see cref="Color"/> to blend.</param>
|
||||
public virtual Color Blend(in Color baseColor, in Color blendColor)
|
||||
{
|
||||
if (blendColor.A.EqualsInTolerance(0)) return baseColor;
|
||||
|
||||
if (blendColor.A.EqualsInTolerance(1))
|
||||
return blendColor;
|
||||
|
||||
float resultA = (1.0f - ((1.0f - blendColor.A) * (1.0f - baseColor.A)));
|
||||
float resultR = (((blendColor.R * blendColor.A) / resultA) + ((baseColor.R * baseColor.A * (1.0f - blendColor.A)) / resultA));
|
||||
float resultG = (((blendColor.G * blendColor.A) / resultA) + ((baseColor.G * baseColor.A * (1.0f - blendColor.A)) / resultA));
|
||||
float resultB = (((blendColor.B * blendColor.A) / resultA) + ((baseColor.B * baseColor.A * (1.0f - blendColor.A)) / resultA));
|
||||
|
||||
return new Color(resultA, resultR, resultG, resultB);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,13 +1,35 @@
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a behavior of a color for base operations.
|
||||
/// </summary>
|
||||
public interface IColorBehavior
|
||||
{
|
||||
public interface IColorBehavior
|
||||
{
|
||||
string ToString(Color color);
|
||||
/// <summary>
|
||||
/// Converts the specified <see cref="Color"/> to a string representation.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to convert.</param>
|
||||
/// <returns>The string representation of the specified color.</returns>
|
||||
string ToString(in Color color);
|
||||
|
||||
bool Equals(Color color, object obj);
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to test.</param>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
bool Equals(in Color color, object? obj);
|
||||
|
||||
int GetHashCode(Color color);
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
|
||||
int GetHashCode(in Color color);
|
||||
|
||||
Color Blend(Color baseColor, Color blendColor);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Blends a <see cref="Color"/> over this color.
|
||||
/// </summary>
|
||||
/// <param name="baseColor">The <see cref="Color"/> to to blend over.</param>
|
||||
/// <param name="blendColor">The <see cref="Color"/> to blend.</param>
|
||||
Color Blend(in Color baseColor, in Color blendColor);
|
||||
}
|
||||
@ -5,284 +5,279 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ARGB (alpha, red, green, blue) color.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
public readonly struct Color
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static readonly Color TRANSPARENT = new(0, 0, 0, 0);
|
||||
/// <summary>
|
||||
/// Gets an transparent color [A: 0, R: 0, G: 0, B: 0]
|
||||
/// </summary>
|
||||
public static ref readonly Color Transparent => ref TRANSPARENT;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IColorBehavior"/> used to perform operations on colors.
|
||||
/// </summary>
|
||||
public static IColorBehavior Behavior { get; set; } = new DefaultColorBehavior();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the alpha component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public readonly float A;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the red component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public readonly float R;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the green component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public readonly float G;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blue component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public readonly float B;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents an ARGB (alpha, red, green, blue) color.
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
|
||||
/// Alpha defaults to 255.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
public struct Color
|
||||
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
public Color(byte r, byte g, byte b)
|
||||
: this(byte.MaxValue, r, g, b)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
|
||||
/// Alpha defaults to 255.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
public Color(int r, int g, int b)
|
||||
: this((byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(byte a, byte r, byte g, byte b)
|
||||
: this(a.GetPercentageFromByteValue(), r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(int a, int r, int g, int b)
|
||||
: this((byte)a.Clamp(0, byte.MaxValue), (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using RGB-percent values.
|
||||
/// Alpha defaults to 1.0.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(float r, float g, float b)
|
||||
: this(1.0f, r, g, b)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(float a, byte r, byte g, byte b)
|
||||
: this(a, r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(float a, int r, int g, int b)
|
||||
: this(a, (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(int a, float r, float g, float b)
|
||||
: this((byte)a.Clamp(0, byte.MaxValue), r, g, b)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(byte a, float r, float g, float b)
|
||||
: this(a.GetPercentageFromByteValue(), r, g, b)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(float a, float r, float g, float b)
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Gets an transparent color [A: 0, R: 0, G: 0, B: 0]
|
||||
/// </summary>
|
||||
public static Color Transparent => new Color(0, 0, 0, 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
private static IColorBehavior _behavior = DefaultColorBehavior.Instance;
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IColorBehavior"/> used to perform operations on colors.
|
||||
/// </summary>
|
||||
public static IColorBehavior Behavior
|
||||
{
|
||||
get => _behavior;
|
||||
set => _behavior = value ?? DefaultColorBehavior.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the alpha component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public double A { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the red component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public double R { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the green component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public double G { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blue component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
public double B { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
|
||||
/// Alpha defaults to 255.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
public Color(byte r, byte g, byte b)
|
||||
: this(byte.MaxValue, r, g, b)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
|
||||
/// Alpha defaults to 255.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
|
||||
public Color(int r, int g, int b)
|
||||
: this((byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(byte a, byte r, byte g, byte b)
|
||||
: this(a.GetPercentageFromByteValue(), r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(int a, int r, int g, int b)
|
||||
: this((byte)a.Clamp(0, byte.MaxValue), (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using RGB-percent values.
|
||||
/// Alpha defaults to 1.0.
|
||||
/// </summary>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(double r, double g, double b)
|
||||
: this(1.0, r, g, b)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(double a, byte r, byte g, byte b)
|
||||
: this(a, r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(double a, int r, int g, int b)
|
||||
: this(a, (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(int a, double r, double g, double b)
|
||||
: this((byte)a.Clamp(0, byte.MaxValue), r, g, b)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(byte a, double r, double g, double b)
|
||||
: this(a.GetPercentageFromByteValue(), r, g, b)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
|
||||
public Color(double a, double r, double g, double b)
|
||||
{
|
||||
A = a.Clamp(0, 1);
|
||||
R = r.Clamp(0, 1);
|
||||
G = g.Clamp(0, 1);
|
||||
B = b.Clamp(0, 1);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct by cloning a existing <see cref="T:RGB.NET.Core.Color" />.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="T:RGB.NET.Core.Color" /> the values are copied from.</param>
|
||||
public Color(Color color)
|
||||
: this(color.A, color.R, color.G, color.B)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets a human-readable string, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. Default format: "[A: 255, R: 255, G: 0, B: 0]".</returns>
|
||||
public override string ToString() => Behavior.ToString(this);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object obj) => Behavior.Equals(this, obj);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
|
||||
public override int GetHashCode() => Behavior.GetHashCode(this);
|
||||
|
||||
/// <summary>
|
||||
/// Blends a <see cref="Color"/> over this color, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to blend.</param>
|
||||
public Color Blend(Color color) => Behavior.Blend(this, color);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Blends the provided colors as if <see cref="Blend"/> would've been called on <paramref name="color1" />.
|
||||
/// </summary>
|
||||
/// <param name="color1">The base color.</param>
|
||||
/// <param name="color2">The color to blend.</param>
|
||||
/// <returns>The blended color.</returns>
|
||||
public static Color operator +(Color color1, Color color2) => color1.Blend(color2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
|
||||
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(Color color1, Color color2) => color1.Equals(color2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
|
||||
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are not equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(Color color1, Color color2) => !(color1 == color2);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((byte r, byte g, byte b) components) => new Color(components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((byte a, byte r, byte g, byte b) components) => new Color(components.a, components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((int r, int g, int b) components) => new Color(components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((int a, int r, int g, int b) components) => new Color(components.a, components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((double r, double g, double b) components) => new Color(components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((double a, double r, double g, double b) components) => new Color(components.a, components.r, components.g, components.b);
|
||||
|
||||
#endregion
|
||||
A = a.Clamp(0, 1);
|
||||
R = r.Clamp(0, 1);
|
||||
G = g.Clamp(0, 1);
|
||||
B = b.Clamp(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct by cloning a existing <see cref="T:RGB.NET.Core.Color" />.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="T:RGB.NET.Core.Color" /> the values are copied from.</param>
|
||||
public Color(in Color color)
|
||||
: this(color.A, color.R, color.G, color.B)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets a human-readable string, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. Default format: "[A: 255, R: 255, G: 0, B: 0]".</returns>
|
||||
public override string ToString() => Behavior.ToString(this);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object? obj) => Behavior.Equals(this, obj);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
|
||||
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||
public override int GetHashCode() => Behavior.GetHashCode(this);
|
||||
|
||||
/// <summary>
|
||||
/// Blends a <see cref="Color"/> over this color, as defined by the current <see cref="Behavior"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to blend.</param>
|
||||
public Color Blend(in Color color) => Behavior.Blend(this, color);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Blends the provided colors as if <see cref="Blend"/> would've been called on <paramref name="color1" />.
|
||||
/// </summary>
|
||||
/// <param name="color1">The base color.</param>
|
||||
/// <param name="color2">The color to blend.</param>
|
||||
/// <returns>The blended color.</returns>
|
||||
public static Color operator +(in Color color1, in Color color2) => color1.Blend(color2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
|
||||
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(in Color color1, in Color color2) => color1.Equals(color2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
|
||||
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are not equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(in Color color1, in Color color2) => !(color1 == color2);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((byte r, byte g, byte b) components) => new(components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((byte a, byte r, byte g, byte b) components) => new(components.a, components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((int r, int g, int b) components) => new(components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((int a, int r, int g, int b) components) => new(components.a, components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((float r, float g, float b) components) => new(components.r, components.g, components.b);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
|
||||
/// <returns>The color.</returns>
|
||||
public static implicit operator Color((float a, float r, float g, float b) components) => new(components.a, components.r, components.g, components.b);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -2,226 +2,227 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the HSV color space.
|
||||
/// </summary>
|
||||
public static class HSVColor
|
||||
{
|
||||
public static class HSVColor
|
||||
#region Getter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hue component value (HSV-color space) of this <see cref="Color"/> as degree in the range [0..360].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The hue component value of the color.</returns>
|
||||
public static float GetHue(this in Color color) => color.GetHSV().hue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the saturation component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The saturation component value of the color.</returns>
|
||||
public static float GetSaturation(this in Color color) => color.GetHSV().saturation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The value component value of the color.</returns>
|
||||
public static float GetValue(this in Color color) => color.GetHSV().value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hue, saturation and value component values (HSV-color space) of this <see cref="Color"/>.
|
||||
/// Hue as degree in the range [0..1].
|
||||
/// Saturation in the range [0..1].
|
||||
/// Value in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>A tuple containing the hue, saturation and value component value of the color.</returns>
|
||||
public static (float hue, float saturation, float value) GetHSV(this in Color color)
|
||||
=> CaclulateHSVFromRGB(color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="hue">The hue value to add.</param>
|
||||
/// <param name="saturation">The saturation value to add.</param>
|
||||
/// <param name="value">The value value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddHSV(this in Color color, float hue = 0, float saturation = 0, float value = 0)
|
||||
{
|
||||
#region Getter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hue component value (HSV-color space) of this <see cref="Color"/> as degree in the range [0..360].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetHue(this Color color) => color.GetHSV().hue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the saturation component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetSaturation(this Color color) => color.GetHSV().saturation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetValue(this Color color) => color.GetHSV().value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hue, saturation and value component values (HSV-color space) of this <see cref="Color"/>.
|
||||
/// Hue as degree in the range [0..1].
|
||||
/// Saturation in the range [0..1].
|
||||
/// Value in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static (double hue, double saturation, double value) GetHSV(this Color color)
|
||||
=> CaclulateHSVFromRGB(color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="hue">The hue value to add.</param>
|
||||
/// <param name="saturation">The saturation value to add.</param>
|
||||
/// <param name="value">The value value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddHSV(this Color color, double hue = 0, double saturation = 0, double value = 0)
|
||||
{
|
||||
(double cHue, double cSaturation, double cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue + hue, cSaturation + saturation, cValue + value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the given HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="hue">The hue value to subtract.</param>
|
||||
/// <param name="saturation">The saturation value to subtract.</param>
|
||||
/// <param name="value">The value value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractHSV(this Color color, double hue = 0, double saturation = 0, double value = 0)
|
||||
{
|
||||
(double cHue, double cSaturation, double cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue - hue, cSaturation - saturation, cValue - value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the given HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="hue">The hue value to multiply.</param>
|
||||
/// <param name="saturation">The saturation value to multiply.</param>
|
||||
/// <param name="value">The value value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyHSV(this Color color, double hue = 1, double saturation = 1, double value = 1)
|
||||
{
|
||||
(double cHue, double cSaturation, double cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue * hue, cSaturation * saturation, cValue * value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the given HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="hue">The hue value to divide.</param>
|
||||
/// <param name="saturation">The saturation value to divide.</param>
|
||||
/// <param name="value">The value value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideHSV(this Color color, double hue = 1, double saturation = 1, double value = 1)
|
||||
{
|
||||
(double cHue, double cSaturation, double cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue / hue, cSaturation / saturation, cValue / value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given hue value of this color.
|
||||
/// </summary>
|
||||
/// <param name="hue">The hue value to set.</param>
|
||||
/// <param name="saturation">The saturation value to set.</param>
|
||||
/// <param name="value">The value value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetHSV(this Color color, double? hue = null, double? saturation = null, double? value = null)
|
||||
{
|
||||
(double cHue, double cSaturation, double cValue) = color.GetHSV();
|
||||
return Create(color.A, hue ?? cHue, saturation ?? cSaturation, value ?? cValue);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using HSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(double hue, double saturation, double value)
|
||||
=> Create(1.0, hue, saturation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(byte a, double hue, double saturation, double value)
|
||||
=> Create((double)a / byte.MaxValue, hue, saturation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(int a, double hue, double saturation, double value)
|
||||
=> Create((double)a / byte.MaxValue, hue, saturation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(double a, double hue, double saturation, double value)
|
||||
{
|
||||
(double r, double g, double b) = CalculateRGBFromHSV(hue, saturation, value);
|
||||
return new Color(a, r, g, b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper
|
||||
|
||||
private static (double h, double s, double v) CaclulateHSVFromRGB(double r, double g, double b)
|
||||
{
|
||||
if (r.EqualsInTolerance(g) && g.EqualsInTolerance(b)) return (0, 0, r);
|
||||
|
||||
double min = Math.Min(Math.Min(r, g), b);
|
||||
double max = Math.Max(Math.Max(r, g), b);
|
||||
|
||||
double hue;
|
||||
if (max.EqualsInTolerance(min))
|
||||
hue = 0;
|
||||
else if (max.EqualsInTolerance(r)) // r is max
|
||||
hue = (g - b) / (max - min);
|
||||
else if (max.EqualsInTolerance(g)) // g is max
|
||||
hue = 2.0 + ((b - r) / (max - min));
|
||||
else // b is max
|
||||
hue = 4.0 + ((r - g) / (max - min));
|
||||
|
||||
hue = hue * 60.0;
|
||||
hue = hue.Wrap(0, 360);
|
||||
|
||||
double saturation = max.EqualsInTolerance(0) ? 0 : 1.0 - (min / max);
|
||||
double value = Math.Max(r, Math.Max(g, b));
|
||||
|
||||
return (hue, saturation, value);
|
||||
}
|
||||
|
||||
private static (double r, double g, double b) CalculateRGBFromHSV(double h, double s, double v)
|
||||
{
|
||||
h = h.Wrap(0, 360);
|
||||
s = s.Clamp(0, 1);
|
||||
v = v.Clamp(0, 1);
|
||||
|
||||
if (s <= 0.0)
|
||||
return (v, v, v);
|
||||
|
||||
double hh = h / 60.0;
|
||||
int i = (int)hh;
|
||||
double ff = hh - i;
|
||||
double p = v * (1.0 - s);
|
||||
double q = v * (1.0 - (s * ff));
|
||||
double t = v * (1.0 - (s * (1.0 - ff)));
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return (v, t, p);
|
||||
case 1:
|
||||
return (q, v, p);
|
||||
case 2:
|
||||
return (p, v, t);
|
||||
case 3:
|
||||
return (p, q, v);
|
||||
case 4:
|
||||
return (t, p, v);
|
||||
default:
|
||||
return (v, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
(float cHue, float cSaturation, float cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue + hue, cSaturation + saturation, cValue + value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="hue">The hue value to subtract.</param>
|
||||
/// <param name="saturation">The saturation value to subtract.</param>
|
||||
/// <param name="value">The value value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractHSV(this in Color color, float hue = 0, float saturation = 0, float value = 0)
|
||||
{
|
||||
(float cHue, float cSaturation, float cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue - hue, cSaturation - saturation, cValue - value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the specified HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="hue">The hue value to multiply.</param>
|
||||
/// <param name="saturation">The saturation value to multiply.</param>
|
||||
/// <param name="value">The value value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyHSV(this in Color color, float hue = 1, float saturation = 1, float value = 1)
|
||||
{
|
||||
(float cHue, float cSaturation, float cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue * hue, cSaturation * saturation, cValue * value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the specified HSV values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="hue">The hue value to divide.</param>
|
||||
/// <param name="saturation">The saturation value to divide.</param>
|
||||
/// <param name="value">The value value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideHSV(this in Color color, float hue = 1, float saturation = 1, float value = 1)
|
||||
{
|
||||
(float cHue, float cSaturation, float cValue) = color.GetHSV();
|
||||
return Create(color.A, cHue / hue, cSaturation / saturation, cValue / value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified hue value of this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="hue">The hue value to set.</param>
|
||||
/// <param name="saturation">The saturation value to set.</param>
|
||||
/// <param name="value">The value value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetHSV(this in Color color, float? hue = null, float? saturation = null, float? value = null)
|
||||
{
|
||||
(float cHue, float cSaturation, float cValue) = color.GetHSV();
|
||||
return Create(color.A, hue ?? cHue, saturation ?? cSaturation, value ?? cValue);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using HSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float hue, float saturation, float value)
|
||||
=> Create(1.0f, hue, saturation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(byte a, float hue, float saturation, float value)
|
||||
=> Create((float)a / byte.MaxValue, hue, saturation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(int a, float hue, float saturation, float value)
|
||||
=> Create((float)a / byte.MaxValue, hue, saturation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float a, float hue, float saturation, float value)
|
||||
{
|
||||
(float r, float g, float b) = CalculateRGBFromHSV(hue, saturation, value);
|
||||
return new Color(a, r, g, b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper
|
||||
|
||||
private static (float h, float s, float v) CaclulateHSVFromRGB(float r, float g, float b)
|
||||
{
|
||||
if (r.EqualsInTolerance(g) && g.EqualsInTolerance(b)) return (0, 0, r);
|
||||
|
||||
float min = Math.Min(Math.Min(r, g), b);
|
||||
float max = Math.Max(Math.Max(r, g), b);
|
||||
|
||||
float hue;
|
||||
if (max.EqualsInTolerance(min))
|
||||
hue = 0;
|
||||
else if (max.EqualsInTolerance(r)) // r is max
|
||||
hue = (g - b) / (max - min);
|
||||
else if (max.EqualsInTolerance(g)) // g is max
|
||||
hue = 2.0f + ((b - r) / (max - min));
|
||||
else // b is max
|
||||
hue = 4.0f + ((r - g) / (max - min));
|
||||
|
||||
hue *= 60.0f;
|
||||
hue = hue.Wrap(0, 360);
|
||||
|
||||
float saturation = max.EqualsInTolerance(0) ? 0 : 1.0f - (min / max);
|
||||
float value = Math.Max(r, Math.Max(g, b));
|
||||
|
||||
return (hue, saturation, value);
|
||||
}
|
||||
|
||||
private static (float r, float g, float b) CalculateRGBFromHSV(float h, float s, float v)
|
||||
{
|
||||
h = h.Wrap(0, 360);
|
||||
s = s.Clamp(0, 1);
|
||||
v = v.Clamp(0, 1);
|
||||
|
||||
if (s <= 0.0)
|
||||
return (v, v, v);
|
||||
|
||||
float hh = h / 60.0f;
|
||||
int i = (int)hh;
|
||||
float ff = hh - i;
|
||||
float p = v * (1.0f - s);
|
||||
float q = v * (1.0f - (s * ff));
|
||||
float t = v * (1.0f - (s * (1.0f - ff)));
|
||||
|
||||
return i switch
|
||||
{
|
||||
0 => (v, t, p),
|
||||
1 => (q, v, p),
|
||||
2 => (p, v, t),
|
||||
3 => (p, q, v),
|
||||
4 => (t, p, v),
|
||||
_ => (v, p, q)
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
211
RGB.NET.Core/Color/HclColor.cs
Normal file
211
RGB.NET.Core/Color/HclColor.cs
Normal file
@ -0,0 +1,211 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the Hcl color space.
|
||||
/// </summary>
|
||||
public static class HclColor
|
||||
{
|
||||
#region Getter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the H component value (Hcl-color space) of this <see cref="Color"/> in the range [0..360].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The H component value of the color. </returns>
|
||||
public static float GetHclH(this in Color color) => color.GetHcl().h;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the c component value (Hcl-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The c component value of the color. </returns>
|
||||
public static float GetHclC(this in Color color) => color.GetHcl().c;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the l component value (Hcl-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The l component value of the color. </returns>
|
||||
public static float GetHclL(this in Color color) => color.GetHcl().l;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the H, c and l component values (Hcl-color space) of this <see cref="Color"/>.
|
||||
/// H in the range [0..360].
|
||||
/// c in the range [0..1].
|
||||
/// l in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>A tuple containing the H, c and l component value of the color.</returns>
|
||||
public static (float h, float c, float l) GetHcl(this in Color color)
|
||||
=> CalculateHclFromRGB(color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified Hcl values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="h">The H value to add.</param>
|
||||
/// <param name="c">The c value to add.</param>
|
||||
/// <param name="l">The l value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddHcl(this in Color color, float h = 0, float c = 0, float l = 0)
|
||||
{
|
||||
(float cH, float cC, float cL) = color.GetHcl();
|
||||
return Create(color.A, cH + h, cC + c, cL + l);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified Hcl values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="h">The H value to subtract.</param>
|
||||
/// <param name="c">The c value to subtract.</param>
|
||||
/// <param name="l">The l value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractHcl(this in Color color, float h = 0, float c = 0, float l = 0)
|
||||
{
|
||||
(float cH, float cC, float cL) = color.GetHcl();
|
||||
return Create(color.A, cH - h, cC - c, cL - l);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the specified Hcl values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="h">The H value to multiply.</param>
|
||||
/// <param name="c">The c value to multiply.</param>
|
||||
/// <param name="l">The l value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyHcl(this in Color color, float h = 1, float c = 1, float l = 1)
|
||||
{
|
||||
(float cH, float cC, float cL) = color.GetHcl();
|
||||
return Create(color.A, cH * h, cC * c, cL * l);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the specified Hcl values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="h">The H value to divide.</param>
|
||||
/// <param name="c">The c value to divide.</param>
|
||||
/// <param name="l">The l value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideHcl(this in Color color, float h = 1, float c = 1, float l = 1)
|
||||
{
|
||||
(float cH, float cC, float cL) = color.GetHcl();
|
||||
return Create(color.A, cH / h, cC / c, cL / l);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified X value of this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="h">The H value to set.</param>
|
||||
/// <param name="c">The c value to set.</param>
|
||||
/// <param name="l">The l value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetHcl(this in Color color, float? h = null, float? c = null, float? l = null)
|
||||
{
|
||||
(float cH, float cC, float cL) = color.GetHcl();
|
||||
return Create(color.A, h ?? cH, c ?? cC, l ?? cL);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using Hcl-Values.
|
||||
/// </summary>
|
||||
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float h, float c, float l)
|
||||
=> Create(1.0f, h, c, l);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
|
||||
/// </summary>
|
||||
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(byte alpha, float h, float c, float l)
|
||||
=> Create((float)alpha / byte.MaxValue, h, c, l);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
|
||||
/// </summary>
|
||||
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(int alpha, float h, float c, float l)
|
||||
=> Create((float)alpha / byte.MaxValue, h, c, l);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
|
||||
/// </summary>
|
||||
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float alpha, float h, float c, float l)
|
||||
{
|
||||
(float r, float g, float b) = CalculateRGBFromHcl(h, c, l);
|
||||
return new Color(alpha, r, g, b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper
|
||||
|
||||
private static (float h, float c, float l) CalculateHclFromRGB(float r, float g, float b)
|
||||
{
|
||||
const float RADIANS_DEGREES_CONVERSION = 180.0f / MathF.PI;
|
||||
|
||||
// ReSharper disable once InconsistentNaming - b is used above
|
||||
(float l, float a, float _b) = LabColor.CalculateLabFromRGB(r, g, b);
|
||||
|
||||
float h, c;
|
||||
if (r.EqualsInTolerance(g) && r.EqualsInTolerance(b)) //DarthAffe 26.02.2021: The cumulated rounding errors are big enough to cause problems in that case
|
||||
{
|
||||
h = 0;
|
||||
c = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = MathF.Atan2(_b, a);
|
||||
if (h >= 0) h *= RADIANS_DEGREES_CONVERSION;
|
||||
else h = 360 - (-h * RADIANS_DEGREES_CONVERSION);
|
||||
|
||||
c = MathF.Sqrt((a * a) + (_b * _b));
|
||||
}
|
||||
|
||||
return (h, c, l);
|
||||
}
|
||||
|
||||
private static (float r, float g, float b) CalculateRGBFromHcl(float h, float c, float l)
|
||||
{
|
||||
const float DEGREES_RADIANS_CONVERSION = MathF.PI / 180.0f;
|
||||
|
||||
h *= DEGREES_RADIANS_CONVERSION;
|
||||
float a = c * MathF.Cos(h);
|
||||
float b = c * MathF.Sin(h);
|
||||
|
||||
return LabColor.CalculateRGBFromLab(l, a, b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
227
RGB.NET.Core/Color/LabColor.cs
Normal file
227
RGB.NET.Core/Color/LabColor.cs
Normal file
@ -0,0 +1,227 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the Lab color space.
|
||||
/// </summary>
|
||||
public static class LabColor
|
||||
{
|
||||
#region Getter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the L component value (Lab-color space) of this <see cref="Color"/> in the range [0..100].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The L component value of the color.</returns>
|
||||
public static float GetLabL(this in Color color) => color.GetLab().l;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the a component value (Lab-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The a component value of the color.</returns>
|
||||
public static float GetLabA(this in Color color) => color.GetLab().a;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the b component value (Lab-color space) of this <see cref="Color"/> in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The b component value of the color.</returns>
|
||||
public static float GetLabB(this in Color color) => color.GetLab().b;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the L, a and b component values (Lab-color space) of this <see cref="Color"/>.
|
||||
/// L in the range [0..100].
|
||||
/// a in the range [0..1].
|
||||
/// b in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>A tuple containing the L, a and b component value of the color.</returns>
|
||||
public static (float l, float a, float b) GetLab(this in Color color)
|
||||
=> CalculateLabFromRGB(color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified Lab values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="l">The L value to add.</param>
|
||||
/// <param name="a">The a value to add.</param>
|
||||
/// <param name="b">The b value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddLab(this in Color color, float l = 0, float a = 0, float b = 0)
|
||||
{
|
||||
(float cL, float cA, float cB) = color.GetLab();
|
||||
return Create(color.A, cL + l, cA + a, cB + b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified Lab values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="l">The L value to subtract.</param>
|
||||
/// <param name="a">The a value to subtract.</param>
|
||||
/// <param name="b">The b value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractLab(this in Color color, float l = 0, float a = 0, float b = 0)
|
||||
{
|
||||
(float cL, float cA, float cB) = color.GetLab();
|
||||
return Create(color.A, cL - l, cA - a, cB - b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the specified Lab values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="l">The L value to multiply.</param>
|
||||
/// <param name="a">The a value to multiply.</param>
|
||||
/// <param name="b">The b value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyLab(this in Color color, float l = 1, float a = 1, float b = 1)
|
||||
{
|
||||
(float cL, float cA, float cB) = color.GetLab();
|
||||
return Create(color.A, cL * l, cA * a, cB * b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the specified Lab values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="l">The L value to divide.</param>
|
||||
/// <param name="a">The a value to divide.</param>
|
||||
/// <param name="b">The b value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideLab(this in Color color, float l = 1, float a = 1, float b = 1)
|
||||
{
|
||||
(float cL, float cA, float cB) = color.GetLab();
|
||||
return Create(color.A, cL / l, cA / a, cB / b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified X valueof this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="l">The L value to set.</param>
|
||||
/// <param name="a">The a value to set.</param>
|
||||
/// <param name="b">The b value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetLab(this in Color color, float? l = null, float? a = null, float? b = null)
|
||||
{
|
||||
(float cL, float cA, float cB) = color.GetLab();
|
||||
return Create(color.A, l ?? cL, a ?? cA, b ?? cB);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using Lab-Values.
|
||||
/// </summary>
|
||||
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float l, float a, float b)
|
||||
=> Create(1.0f, l, a, b);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
|
||||
/// </summary>
|
||||
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(byte alpha, float l, float a, float b)
|
||||
=> Create((float)alpha / byte.MaxValue, l, a, b);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
|
||||
/// </summary>
|
||||
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(int alpha, float l, float a, float b)
|
||||
=> Create((float)alpha / byte.MaxValue, l, a, b);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
|
||||
/// </summary>
|
||||
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float alpha, float l, float a, float b)
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming - b is used above
|
||||
(float r, float g, float _b) = CalculateRGBFromLab(l, a, b);
|
||||
return new Color(alpha, r, g, _b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper
|
||||
|
||||
internal static (float l, float a, float b) CalculateLabFromRGB(float r, float g, float b)
|
||||
{
|
||||
(float x, float y, float z) = XYZColor.CaclulateXYZFromRGB(r, g, b);
|
||||
return CaclulateLabFromXYZ(x, y, z);
|
||||
}
|
||||
|
||||
internal static (float r, float g, float b) CalculateRGBFromLab(float l, float a, float b)
|
||||
{
|
||||
(float x, float y, float z) = CalculateXYZFromLab(l, a, b);
|
||||
return XYZColor.CalculateRGBFromXYZ(x, y, z);
|
||||
}
|
||||
|
||||
private static (float l, float a, float b) CaclulateLabFromXYZ(float x, float y, float z)
|
||||
{
|
||||
const float ONETHRID = 1.0f / 3.0f;
|
||||
const float FACTOR2 = 16.0f / 116.0f;
|
||||
|
||||
x /= 95.047f;
|
||||
y /= 100.0f;
|
||||
z /= 108.883f;
|
||||
|
||||
x = ((x > 0.008856f) ? (MathF.Pow(x, ONETHRID)) : ((7.787f * x) + FACTOR2));
|
||||
y = ((y > 0.008856f) ? (MathF.Pow(y, ONETHRID)) : ((7.787f * y) + FACTOR2));
|
||||
z = ((z > 0.008856f) ? (MathF.Pow(z, ONETHRID)) : ((7.787f * z) + FACTOR2));
|
||||
|
||||
float l = (116.0f * y) - 16.0f;
|
||||
float a = 500.0f * (x - y);
|
||||
float b = 200.0f * (y - z);
|
||||
|
||||
return (l, a, b);
|
||||
}
|
||||
|
||||
private static (float x, float y, float z) CalculateXYZFromLab(float l, float a, float b)
|
||||
{
|
||||
const float FACTOR2 = 16.0f / 116.0f;
|
||||
|
||||
float y = (l + 16.0f) / 116.0f;
|
||||
float x = (a / 500.0f) + y;
|
||||
float z = y - (b / 200.0f);
|
||||
|
||||
float powX = MathF.Pow(x, 3.0f);
|
||||
float powY = MathF.Pow(y, 3.0f);
|
||||
float powZ = MathF.Pow(z, 3.0f);
|
||||
|
||||
x = ((powX > 0.008856f) ? (powX) : ((x - FACTOR2) / 7.787f));
|
||||
y = ((powY > 0.008856f) ? (powY) : ((y - FACTOR2) / 7.787f));
|
||||
z = ((powZ > 0.008856f) ? (powZ) : ((z - FACTOR2) / 7.787f));
|
||||
|
||||
return (x * 95.047f, y * 100.0f, z * 108.883f);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -2,274 +2,294 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the RGB color space.
|
||||
/// </summary>
|
||||
public static class RGBColor
|
||||
{
|
||||
public static class RGBColor
|
||||
#region Getter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the A component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The A component value of the color.</returns>
|
||||
public static byte GetA(this in Color color) => color.A.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the R component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The R component value of the color.</returns>
|
||||
public static byte GetR(this in Color color) => color.R.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the G component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The G component value of the color.</returns>
|
||||
public static byte GetG(this in Color color) => color.G.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the B component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The B component value of the color.</returns>
|
||||
public static byte GetB(this in Color color) => color.B.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the A, R, G and B component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>A tuple containing the A, R, G and B component value of the color.</returns>
|
||||
public static (byte a, byte r, byte g, byte b) GetRGBBytes(this in Color color)
|
||||
=> (color.GetA(), color.GetR(), color.GetG(), color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the A, R, G and B component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>A tuple containing the A, R, G and B component value of the color.</returns>
|
||||
public static (float a, float r, float g, float b) GetRGB(this in Color color)
|
||||
=> (color.A, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
#region Add
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to add.</param>
|
||||
/// <param name="g">The green value to add.</param>
|
||||
/// <param name="b">The blue value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddRGB(this in Color color, int r = 0, int g = 0, int b = 0)
|
||||
=> new(color.A, color.GetR() + r, color.GetG() + g, color.GetB() + b);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified RGB-percent values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to add.</param>
|
||||
/// <param name="g">The green value to add.</param>
|
||||
/// <param name="b">The blue value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddRGB(this in Color color, float r = 0, float g = 0, float b = 0)
|
||||
=> new(color.A, color.R + r, color.G + g, color.B + b);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="a">The alpha value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddA(this in Color color, int a)
|
||||
=> new(color.GetA() + a, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified alpha-percent value to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="a">The alpha value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddA(this in Color color, float a)
|
||||
=> new(color.A + a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Subtract
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to subtract.</param>
|
||||
/// <param name="g">The green value to subtract.</param>
|
||||
/// <param name="b">The blue value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractRGB(this in Color color, int r = 0, int g = 0, int b = 0)
|
||||
=> new(color.A, color.GetR() - r, color.GetG() - g, color.GetB() - b);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to subtract.</param>
|
||||
/// <param name="g">The green value to subtract.</param>
|
||||
/// <param name="b">The blue value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractRGB(this in Color color, float r = 0, float g = 0, float b = 0)
|
||||
=> new(color.A, color.R - r, color.G - g, color.B - b);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="a">The alpha value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractA(this in Color color, int a)
|
||||
=> new(color.GetA() - a, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified alpha-percent value to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="aPercent">The alpha value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractA(this in Color color, float aPercent)
|
||||
=> new(color.A - aPercent, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Multiply
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the specified RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to multiply.</param>
|
||||
/// <param name="g">The green value to multiply.</param>
|
||||
/// <param name="b">The blue value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyRGB(this in Color color, float r = 1, float g = 1, float b = 1)
|
||||
=> new(color.A, color.R * r, color.G * g, color.B * b);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the specified alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="a">The alpha value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyA(this in Color color, float a)
|
||||
=> new(color.A * a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Divide
|
||||
|
||||
/// <summary>
|
||||
/// Divides the specified RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to divide.</param>
|
||||
/// <param name="g">The green value to divide.</param>
|
||||
/// <param name="b">The blue value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideRGB(this in Color color, float r = 1, float g = 1, float b = 1)
|
||||
=> new(color.A, color.R / r, color.G / g, color.B / b);
|
||||
|
||||
/// <summary>
|
||||
/// Divides the specified alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="a">The alpha value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideA(this in Color color, float a)
|
||||
=> new(color.A / a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Set
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified RGB value of this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to set.</param>
|
||||
/// <param name="g">The green value to set.</param>
|
||||
/// <param name="b">The blue value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetRGB(this in Color color, byte? r = null, byte? g = null, byte? b = null)
|
||||
=> new(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified RGB value of this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to set.</param>
|
||||
/// <param name="g">The green value to set.</param>
|
||||
/// <param name="b">The blue value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetRGB(this in Color color, int? r = null, int? g = null, int? b = null)
|
||||
=> new(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified RGB value of this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="r">The red value to set.</param>
|
||||
/// <param name="g">The green value to set.</param>
|
||||
/// <param name="b">The blue value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetRGB(this in Color color, float? r = null, float? g = null, float? b = null)
|
||||
=> new(color.A, r ?? color.R, g ?? color.G, b ?? color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified alpha value of this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="a">The alpha value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetA(this in Color color, int a) => new(a, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified alpha value of this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="a">The alpha value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetA(this in Color color, float a) => new(a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current color as a RGB-HEX-string.
|
||||
/// </summary>
|
||||
/// <returns>The RGB-HEX-string.</returns>
|
||||
public static string AsRGBHexString(this in Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetR(), color.GetG(), color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current color as a ARGB-HEX-string.
|
||||
/// </summary>
|
||||
/// <returns>The ARGB-HEX-string.</returns>
|
||||
public static string AsARGBHexString(this in Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetA(), color.GetR(), color.GetG(), color.GetB());
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using a HEX-string.
|
||||
/// </summary>
|
||||
/// <param name="hexString">The HEX-representation of the color.</param>
|
||||
/// <returns>The color created from the HEX-string.</returns>
|
||||
public static Color FromHexString(string hexString)
|
||||
{
|
||||
#region Getter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the A component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static byte GetA(this Color color) => color.A.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the R component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static byte GetR(this Color color) => color.R.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the G component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static byte GetG(this Color color) => color.G.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the B component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static byte GetB(this Color color) => color.B.GetByteValueFromPercentage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the A, R, G and B component value of this <see cref="Color"/> as byte in the range [0..255].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static (byte a, byte r, byte g, byte b) GetRGBBytes(this Color color)
|
||||
=> (color.GetA(), color.GetR(), color.GetG(), color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the A, R, G and B component value of this <see cref="Color"/> as percentage in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static (double a, double r, double g, double b) GetRGB(this Color color)
|
||||
=> (color.A, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
#region Add
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to add.</param>
|
||||
/// <param name="g">The green value to add.</param>
|
||||
/// <param name="b">The blue value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddRGB(this Color color, int r = 0, int g = 0, int b = 0)
|
||||
=> new Color(color.A, color.GetR() + r, color.GetG() + g, color.GetB() + b);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given RGB-percent values to this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to add.</param>
|
||||
/// <param name="g">The green value to add.</param>
|
||||
/// <param name="b">The blue value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddRGB(this Color color, double r = 0, double g = 0, double b = 0)
|
||||
=> new Color(color.A, color.R + r, color.G + g, color.B + b);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddA(this Color color, int a)
|
||||
=> new Color(color.GetA() + a, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given alpha-percent value to this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddA(this Color color, double a)
|
||||
=> new Color(color.A + a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Subtract
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the given RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to subtract.</param>
|
||||
/// <param name="g">The green value to subtract.</param>
|
||||
/// <param name="b">The blue value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractRGB(this Color color, int r = 0, int g = 0, int b = 0)
|
||||
=> new Color(color.A, color.GetR() - r, color.GetG() - g, color.GetB() - b);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the given RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to subtract.</param>
|
||||
/// <param name="g">The green value to subtract.</param>
|
||||
/// <param name="b">The blue value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractRGB(this Color color, double r = 0, double g = 0, double b = 0)
|
||||
=> new Color(color.A, color.R - r, color.G - g, color.B - b);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the given alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractA(this Color color, int a)
|
||||
=> new Color(color.GetA() - a, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the given alpha-percent value to this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractA(this Color color, double aPercent)
|
||||
=> new Color(color.A - aPercent, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Multiply
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the given RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to multiply.</param>
|
||||
/// <param name="g">The green value to multiply.</param>
|
||||
/// <param name="b">The blue value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyRGB(this Color color, double r = 1, double g = 1, double b = 1)
|
||||
=> new Color(color.A, color.R * r, color.G * g, color.B * b);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the given alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyA(this Color color, double a)
|
||||
=> new Color(color.A * a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Divide
|
||||
|
||||
/// <summary>
|
||||
/// Divides the given RGB values to this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to divide.</param>
|
||||
/// <param name="g">The green value to divide.</param>
|
||||
/// <param name="b">The blue value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideRGB(this Color color, double r = 1, double g = 1, double b = 1)
|
||||
=> new Color(color.A, color.R / r, color.G / g, color.B / b);
|
||||
|
||||
/// <summary>
|
||||
/// Divides the given alpha value to this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideA(this Color color, double a)
|
||||
=> new Color(color.A / a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Set
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given RGB value of this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to set.</param>
|
||||
/// <param name="g">The green value to set.</param>
|
||||
/// <param name="b">The blue value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetRGB(this Color color, byte? r = null, byte? g = null, byte? b = null)
|
||||
=> new Color(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given RGB value of this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to set.</param>
|
||||
/// <param name="g">The green value to set.</param>
|
||||
/// <param name="b">The blue value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetRGB(this Color color, int? r = null, int? g = null, int? b = null)
|
||||
=> new Color(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given RGB value of this color.
|
||||
/// </summary>
|
||||
/// <param name="r">The red value to set.</param>
|
||||
/// <param name="g">The green value to set.</param>
|
||||
/// <param name="b">The blue value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetRGB(this Color color, double? r = null, double? g = null, double? b = null)
|
||||
=> new Color(color.A, r ?? color.R, g ?? color.G, b ?? color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given alpha value of this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetA(this Color color, int a) => new Color(a, color.R, color.G, color.B);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given alpha value of this color.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetA(this Color color, double a) => new Color(a, color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current color as a RGB-HEX-string.
|
||||
/// </summary>
|
||||
/// <returns>The RGB-HEX-string.</returns>
|
||||
public static string AsRGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetR(), color.GetG(), color.GetB());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current color as a ARGB-HEX-string.
|
||||
/// </summary>
|
||||
/// <returns>The ARGB-HEX-string.</returns>
|
||||
public static string AsARGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetA(), color.GetR(), color.GetG(), color.GetB());
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using a HEX-string.
|
||||
/// </summary>
|
||||
/// <param name="hexString">The HEX-representation of the color.</param>
|
||||
/// <returns>The color created from the HEX-string.</returns>
|
||||
public static Color FromHexString(string hexString)
|
||||
{
|
||||
if ((hexString == null) || (hexString.Length < 6))
|
||||
throw new ArgumentException("Invalid hex string", nameof(hexString));
|
||||
|
||||
if (hexString[0] == '#')
|
||||
hexString = hexString.Substring(1);
|
||||
|
||||
byte[] data = ConversionHelper.HexToBytes(hexString);
|
||||
if (data.Length == 3)
|
||||
return new Color(data[0], data[1], data[2]);
|
||||
if (data.Length == 4)
|
||||
return new Color(data[0], data[1], data[2], data[3]);
|
||||
|
||||
if ((hexString == null) || (hexString.Length < 6))
|
||||
throw new ArgumentException("Invalid hex string", nameof(hexString));
|
||||
}
|
||||
|
||||
#endregion
|
||||
ReadOnlySpan<char> span = hexString.AsSpan();
|
||||
if (span[0] == '#')
|
||||
span = span[1..];
|
||||
|
||||
byte[] data = ConversionHelper.HexToBytes(span);
|
||||
return data.Length switch
|
||||
{
|
||||
3 => new Color(data[0], data[1], data[2]),
|
||||
4 => new Color(data[0], data[1], data[2], data[3]),
|
||||
_ => throw new ArgumentException($"Invalid hex string '{hexString}'", nameof(hexString))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
207
RGB.NET.Core/Color/XYZColor.cs
Normal file
207
RGB.NET.Core/Color/XYZColor.cs
Normal file
@ -0,0 +1,207 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the XYZ color space.
|
||||
/// </summary>
|
||||
public static class XYZColor
|
||||
{
|
||||
#region Getter
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X component value (XYZ-color space) of this <see cref="Color"/> in the range [0..95.047].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The X component value of the color.</returns>
|
||||
public static float GetX(this in Color color) => color.GetXYZ().x;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y component value (XYZ-color space) of this <see cref="Color"/> in the range [0..100].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The Y component value of the color.</returns>
|
||||
public static float GetY(this in Color color) => color.GetXYZ().y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Z component value (XYZ-color space) of this <see cref="Color"/> in the range [0..108.883].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>The Z component value of the color.</returns>
|
||||
public static float GetZ(this in Color color) => color.GetXYZ().z;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X, Y and Z component values (XYZ-color space) of this <see cref="Color"/>.
|
||||
/// X in the range [0..95.047].
|
||||
/// Y in the range [0..100].
|
||||
/// Z in the range [0..108.883].
|
||||
/// </summary>
|
||||
/// <param name="color">The color to get the value from.</param>
|
||||
/// <returns>A tuple containing the X, Y and Z component value of the color.</returns>
|
||||
public static (float x, float y, float z) GetXYZ(this in Color color)
|
||||
=> CaclulateXYZFromRGB(color.R, color.G, color.B);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified XYZ values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="x">The X value to add.</param>
|
||||
/// <param name="y">The Y value to add.</param>
|
||||
/// <param name="z">The Z value to add.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color AddXYZ(this in Color color, float x = 0, float y = 0, float z = 0)
|
||||
{
|
||||
(float cX, float cY, float cZ) = color.GetXYZ();
|
||||
return Create(color.A, cX + x, cY + y, cZ + z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the specified XYZ values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="x">The X value to subtract.</param>
|
||||
/// <param name="y">The Y value to subtract.</param>
|
||||
/// <param name="z">The Z value to subtract.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SubtractXYZ(this in Color color, float x = 0, float y = 0, float z = 0)
|
||||
{
|
||||
(float cX, float cY, float cZ) = color.GetXYZ();
|
||||
return Create(color.A, cX - x, cY - y, cZ - z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the specified XYZ values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="x">The X value to multiply.</param>
|
||||
/// <param name="y">The Y value to multiply.</param>
|
||||
/// <param name="z">The Z value to multiply.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color MultiplyXYZ(this in Color color, float x = 1, float y = 1, float z = 1)
|
||||
{
|
||||
(float cX, float cY, float cZ) = color.GetXYZ();
|
||||
return Create(color.A, cX * x, cY * y, cZ * z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the specified XYZ values to this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="x">The X value to divide.</param>
|
||||
/// <param name="y">The Y value to divide.</param>
|
||||
/// <param name="z">The Z value to divide.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color DivideXYZ(this in Color color, float x = 1, float y = 1, float z = 1)
|
||||
{
|
||||
(float cX, float cY, float cZ) = color.GetXYZ();
|
||||
return Create(color.A, cX / x, cY / y, cZ / z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified X valueof this color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to modify.</param>
|
||||
/// <param name="x">The X value to set.</param>
|
||||
/// <param name="y">The Y value to set.</param>
|
||||
/// <param name="z">The Z value to set.</param>
|
||||
/// <returns>The new color after the modification.</returns>
|
||||
public static Color SetXYZ(this in Color color, float? x = null, float? y = null, float? z = null)
|
||||
{
|
||||
(float cX, float cY, float cZ) = color.GetXYZ();
|
||||
return Create(color.A, x ?? cX, y ?? cY, z ?? cZ);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using XYZ-Values.
|
||||
/// </summary>
|
||||
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float x, float y, float z)
|
||||
=> Create(1.0f, x, y, z);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(byte a, float x, float y, float z)
|
||||
=> Create((float)a / byte.MaxValue, x, y, z);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(int a, float x, float y, float z)
|
||||
=> Create((float)a / byte.MaxValue, x, y, z);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
|
||||
/// </summary>
|
||||
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
|
||||
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
|
||||
/// <returns>The color created from the values.</returns>
|
||||
public static Color Create(float a, float x, float y, float z)
|
||||
{
|
||||
(float r, float g, float b) = CalculateRGBFromXYZ(x, y, z);
|
||||
return new Color(a, r, g, b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper
|
||||
|
||||
internal static (float x, float y, float z) CaclulateXYZFromRGB(float r, float g, float b)
|
||||
{
|
||||
r = ((r > 0.04045f) ? MathF.Pow(((r + 0.055f) / 1.055f), 2.4f) : (r / 12.92f)) * 100.0f;
|
||||
g = ((g > 0.04045f) ? MathF.Pow(((g + 0.055f) / 1.055f), 2.4f) : (g / 12.92f)) * 100.0f;
|
||||
b = ((b > 0.04045f) ? MathF.Pow(((b + 0.055f) / 1.055f), 2.4f) : (b / 12.92f)) * 100.0f;
|
||||
|
||||
float x = (r * 0.4124f) + (g * 0.3576f) + (b * 0.1805f);
|
||||
float y = (r * 0.2126f) + (g * 0.7152f) + (b * 0.0722f);
|
||||
float z = (r * 0.0193f) + (g * 0.1192f) + (b * 0.9505f);
|
||||
|
||||
return (x, y, z);
|
||||
}
|
||||
|
||||
internal static (float r, float g, float b) CalculateRGBFromXYZ(float x, float y, float z)
|
||||
{
|
||||
const float INVERSE_EXPONENT = 1.0f / 2.4f;
|
||||
|
||||
x /= 100.0f;
|
||||
y /= 100.0f;
|
||||
z /= 100.0f;
|
||||
|
||||
float r = (x * 3.2406f) + (y * -1.5372f) + (z * -0.4986f);
|
||||
float g = (x * -0.9689f) + (y * 1.8758f) + (z * 0.0415f);
|
||||
float b = (x * 0.0557f) + (y * -0.2040f) + (z * 1.0570f);
|
||||
|
||||
r = ((r > 0.0031308f) ? ((1.055f * (MathF.Pow(r, INVERSE_EXPONENT))) - 0.055f) : (12.92f * r));
|
||||
g = ((g > 0.0031308f) ? ((1.055f * (MathF.Pow(g, INVERSE_EXPONENT))) - 0.055f) : (12.92f * g));
|
||||
b = ((b > 0.0031308f) ? ((1.055f * (MathF.Pow(b, INVERSE_EXPONENT))) - 0.055f) : (12.92f * b));
|
||||
|
||||
return (r, g, b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,16 +1,15 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic color-correction.
|
||||
/// </summary>
|
||||
public interface IColorCorrection
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic color-correction.
|
||||
/// Applies the <see cref="IColorCorrection"/> to the specified <see cref="Color"/>.
|
||||
/// </summary>
|
||||
public interface IColorCorrection
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies the <see cref="IColorCorrection"/> to the given <see cref="Color"/>.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to correct.</param>
|
||||
Color ApplyTo(Color color);
|
||||
}
|
||||
}
|
||||
/// <param name="color">The <see cref="Color"/> to correct.</param>
|
||||
void ApplyTo(ref Color color);
|
||||
}
|
||||
@ -2,64 +2,68 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc cref="AbstractBindable" />
|
||||
/// <inheritdoc cref="IDecoratable{T}" />
|
||||
public abstract class AbstractDecoratable<T> : AbstractBindable, IDecoratable<T>
|
||||
where T : IDecorator
|
||||
{
|
||||
/// <inheritdoc cref="AbstractBindable" />
|
||||
/// <inheritdoc cref="IDecoratable{T}" />
|
||||
public abstract class AbstractDecoratable<T> : AbstractBindable, IDecoratable<T>
|
||||
where T : IDecorator
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly List<T> _decorators = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<T> Decorators { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractDecoratable{T}"/> class.
|
||||
/// </summary>
|
||||
protected AbstractDecoratable()
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly List<T> _decorators = new List<T>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<T> Decorators
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_decorators)
|
||||
return new ReadOnlyCollection<T>(_decorators);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddDecorator(T decorator)
|
||||
{
|
||||
lock (Decorators)
|
||||
{
|
||||
_decorators.Add(decorator);
|
||||
_decorators.Sort((d1, d2) => d1.Order.CompareTo(d2.Order));
|
||||
}
|
||||
|
||||
decorator.OnAttached(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveDecorator(T decorator)
|
||||
{
|
||||
lock (Decorators)
|
||||
_decorators.Remove(decorator);
|
||||
|
||||
decorator.OnDetached(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveAllDecorators()
|
||||
{
|
||||
IEnumerable<T> decorators;
|
||||
|
||||
lock (Decorators)
|
||||
decorators = Decorators.ToList();
|
||||
|
||||
foreach (T decorator in decorators)
|
||||
RemoveDecorator(decorator);
|
||||
}
|
||||
|
||||
#endregion
|
||||
Decorators = new ReadOnlyCollection<T>(_decorators);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddDecorator(T decorator)
|
||||
{
|
||||
lock (Decorators)
|
||||
{
|
||||
_decorators.Add(decorator);
|
||||
_decorators.Sort((d1, d2) => d1.Order.CompareTo(d2.Order));
|
||||
}
|
||||
|
||||
decorator.OnAttached(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveDecorator(T decorator)
|
||||
{
|
||||
lock (Decorators)
|
||||
_decorators.Remove(decorator);
|
||||
|
||||
decorator.OnDetached(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveAllDecorators()
|
||||
{
|
||||
IEnumerable<T> decorators;
|
||||
|
||||
lock (Decorators)
|
||||
decorators = Decorators.ToList();
|
||||
|
||||
foreach (T decorator in decorators)
|
||||
RemoveDecorator(decorator);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -2,61 +2,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc cref="AbstractBindable" />
|
||||
/// <inheritdoc cref="IDecorator" />
|
||||
public abstract class AbstractDecorator : AbstractBindable, IDecorator
|
||||
{
|
||||
/// <inheritdoc cref="AbstractBindable" />
|
||||
/// <inheritdoc cref="IDecorator" />
|
||||
public abstract class AbstractDecorator : AbstractBindable, IDecorator
|
||||
#region Properties & Fields
|
||||
|
||||
private bool _isEnabled = true;
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private bool _isEnabled = true;
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set => SetProperty(ref _isEnabled, value);
|
||||
}
|
||||
|
||||
private int _order;
|
||||
/// <inheritdoc />
|
||||
public int Order
|
||||
{
|
||||
get => _order;
|
||||
set => SetProperty(ref _order, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a readonly-list of all <see cref="IDecoratable"/> this decorator is attached to.
|
||||
/// </summary>
|
||||
protected List<IDecoratable> DecoratedObjects { get; } = new List<IDecoratable>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnAttached(IDecoratable decoratable) => DecoratedObjects.Add(decoratable);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnDetached(IDecoratable decoratable) => DecoratedObjects.Remove(decoratable);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches the decorator from all <see cref="IDecoratable"/> it is currently attached to.
|
||||
/// </summary>
|
||||
protected virtual void Detach()
|
||||
{
|
||||
List<IDecoratable> decoratables = new List<IDecoratable>(DecoratedObjects);
|
||||
foreach (IDecoratable decoratable in decoratables)
|
||||
{
|
||||
IEnumerable<Type> types = decoratable.GetType().GetInterfaces().Where(t => t.IsGenericType
|
||||
&& (t.Name == typeof(IDecoratable<>).Name)
|
||||
&& t.GenericTypeArguments[0].IsInstanceOfType(this));
|
||||
foreach (Type decoratableType in types)
|
||||
decoratableType.GetMethod(nameof(IDecoratable<IDecorator>.RemoveDecorator))?.Invoke(decoratable, new object[] { this });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
get => _isEnabled;
|
||||
set => SetProperty(ref _isEnabled, value);
|
||||
}
|
||||
}
|
||||
|
||||
private int _order;
|
||||
/// <inheritdoc />
|
||||
public int Order
|
||||
{
|
||||
get => _order;
|
||||
set => SetProperty(ref _order, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a readonly-list of all <see cref="IDecoratable"/> this decorator is attached to.
|
||||
/// </summary>
|
||||
protected List<IDecoratable> DecoratedObjects { get; } = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnAttached(IDecoratable decoratable) => DecoratedObjects.Add(decoratable);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnDetached(IDecoratable decoratable) => DecoratedObjects.Remove(decoratable);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches the decorator from all <see cref="IDecoratable"/> it is currently attached to.
|
||||
/// </summary>
|
||||
protected virtual void Detach()
|
||||
{
|
||||
List<IDecoratable> decoratables = new(DecoratedObjects);
|
||||
foreach (IDecoratable decoratable in decoratables)
|
||||
{
|
||||
IEnumerable<Type> types = decoratable.GetType().GetInterfaces().Where(t => t.IsGenericType
|
||||
&& (t.Name == typeof(IDecoratable<>).Name)
|
||||
&& t.GenericTypeArguments[0].IsInstanceOfType(this));
|
||||
foreach (Type decoratableType in types)
|
||||
decoratableType.GetMethod(nameof(IDecoratable<IDecorator>.RemoveDecorator))?.Invoke(decoratable, new object[] { this });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,65 +1,71 @@
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic decorator which is aware of the <see cref="E:RGB.NET.Core.RGBSurface.Updating" /> event.
|
||||
/// </summary>
|
||||
public abstract class AbstractUpdateAwareDecorator : AbstractDecorator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic decorator which is aware of the <see cref="E:RGB.NET.Core.RGBSurface.Updating" /> event.
|
||||
/// Gets the surface this decorator is attached to.
|
||||
/// </summary>
|
||||
public abstract class AbstractUpdateAwareDecorator : AbstractDecorator
|
||||
protected RGBSurface Surface { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.
|
||||
/// </summary>
|
||||
protected bool UpdateIfDisabled { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractUpdateAwareDecorator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="surface">The surface this decorator is attached to.</param>
|
||||
/// <param name="updateIfDisabled">Bool indicating if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.</param>
|
||||
protected AbstractUpdateAwareDecorator(RGBSurface surface, bool updateIfDisabled = false)
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.
|
||||
/// </summary>
|
||||
protected bool UpdateIfDisabled { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractUpdateAwareDecorator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="updateIfDisabled">Bool indicating if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.</param>
|
||||
protected AbstractUpdateAwareDecorator(bool updateIfDisabled = false)
|
||||
{
|
||||
this.UpdateIfDisabled = updateIfDisabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAttached(IDecoratable decoratable)
|
||||
{
|
||||
if (DecoratedObjects.Count == 0)
|
||||
RGBSurface.Instance.Updating += OnSurfaceUpdating;
|
||||
|
||||
base.OnAttached(decoratable);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDetached(IDecoratable decoratable)
|
||||
{
|
||||
base.OnDetached(decoratable);
|
||||
|
||||
if (DecoratedObjects.Count == 0)
|
||||
RGBSurface.Instance.Updating -= OnSurfaceUpdating;
|
||||
}
|
||||
|
||||
private void OnSurfaceUpdating(UpdatingEventArgs args)
|
||||
{
|
||||
if (IsEnabled || UpdateIfDisabled)
|
||||
Update(args.DeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this <see cref="AbstractUpdateAwareDecorator"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
|
||||
protected abstract void Update(double deltaTime);
|
||||
|
||||
#endregion
|
||||
this.Surface = surface;
|
||||
this.UpdateIfDisabled = updateIfDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnAttached(IDecoratable decoratable)
|
||||
{
|
||||
if (DecoratedObjects.Count == 0)
|
||||
Surface.Updating += OnSurfaceUpdating;
|
||||
|
||||
base.OnAttached(decoratable);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDetached(IDecoratable decoratable)
|
||||
{
|
||||
base.OnDetached(decoratable);
|
||||
|
||||
if (DecoratedObjects.Count == 0)
|
||||
Surface.Updating -= OnSurfaceUpdating;
|
||||
}
|
||||
|
||||
private void OnSurfaceUpdating(UpdatingEventArgs args)
|
||||
{
|
||||
if (IsEnabled || UpdateIfDisabled)
|
||||
Update(args.DeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this <see cref="AbstractUpdateAwareDecorator"/>.
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
|
||||
protected abstract void Update(double deltaTime);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,17 +1,16 @@
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a <see cref="T:RGB.NET.Core.IDecorator" /> decorating a <see cref="T:RGB.NET.Core.IBrush" />.
|
||||
/// </summary>
|
||||
public interface IBrushDecorator : IDecorator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a <see cref="T:RGB.NET.Core.IDecorator" /> decorating a <see cref="T:RGB.NET.Core.IBrush" />.
|
||||
/// Decorator-Method called by the <see cref="IBrush"/>.
|
||||
/// </summary>
|
||||
public interface IBrushDecorator : IDecorator
|
||||
{
|
||||
/// <summary>
|
||||
/// Decorator-Method called by the <see cref="IBrush"/>.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The rectangle in which the <see cref="IBrush"/> should be drawn.</param>
|
||||
/// <param name="renderTarget">The target (key/point) from which the <see cref="Color"/> should be taken.</param>
|
||||
/// <param name="color">The <see cref="Color"/> to be modified.</param>
|
||||
Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color);
|
||||
}
|
||||
}
|
||||
/// <param name="rectangle">The rectangle in which the <see cref="IBrush"/> should be drawn.</param>
|
||||
/// <param name="renderTarget">The target (key/point) from which the <see cref="Color"/> should be taken.</param>
|
||||
/// <param name="color">The <see cref="Color"/> to be modified.</param>
|
||||
void ManipulateColor(in Rectangle rectangle, in RenderTarget renderTarget, ref Color color);
|
||||
}
|
||||
@ -1,42 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic decoratable.
|
||||
/// </summary>
|
||||
public interface IDecoratable : INotifyPropertyChanged
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic decoratable for a specific type of <see cref="T:RGB.NET.Core.IDecorator" />
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of decorators this decoratable can be decorated with.</typeparam>
|
||||
public interface IDecoratable<T> : IDecoratable
|
||||
where T : IDecorator
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a basic decoratable.
|
||||
/// Gets a readonly-list of all <see cref="IDecorator"/> attached to this <see cref="IDecoratable{T}"/>.
|
||||
/// </summary>
|
||||
public interface IDecoratable : INotifyPropertyChanged
|
||||
{ }
|
||||
IReadOnlyList<T> Decorators { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic decoratable for a specific type of <see cref="T:RGB.NET.Core.IDecorator" />
|
||||
/// Adds an <see cref="IDecorator"/> to the <see cref="IDecoratable"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IDecoratable<T> : IDecoratable
|
||||
where T : IDecorator
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a readonly-list of all <see cref="IDecorator"/> attached to this <see cref="IDecoratable{T}"/>.
|
||||
/// </summary>
|
||||
IReadOnlyCollection<T> Decorators { get; }
|
||||
/// <param name="decorator">The <see cref="IDecorator"/> to be added.</param>
|
||||
void AddDecorator(T decorator);
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IDecorator"/> to the <see cref="IDecoratable"/>.
|
||||
/// </summary>
|
||||
/// <param name="decorator">The <see cref="IDecorator"/> to be added.</param>
|
||||
void AddDecorator(T decorator);
|
||||
/// <summary>
|
||||
/// Removes an <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
|
||||
/// </summary>
|
||||
/// <param name="decorator">The <see cref="IDecorator"/> to be removed.</param>
|
||||
void RemoveDecorator(T decorator);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
|
||||
/// </summary>
|
||||
/// <param name="decorator">The <see cref="IDecorator"/> to be removed.</param>
|
||||
void RemoveDecorator(T decorator);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
|
||||
/// </summary>
|
||||
void RemoveAllDecorators();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
|
||||
/// </summary>
|
||||
void RemoveAllDecorators();
|
||||
}
|
||||
@ -1,39 +1,38 @@
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic decorator.
|
||||
/// </summary>
|
||||
public interface IDecorator
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic decorator.
|
||||
/// Gets or sets if the <see cref="IDecorator"/> is enabled and will be used.
|
||||
/// </summary>
|
||||
public interface IDecorator
|
||||
{
|
||||
#region Properties & Fields
|
||||
bool IsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the <see cref="IDecorator"/> is enabled and will be used.
|
||||
/// </summary>
|
||||
bool IsEnabled { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the order in which multiple decorators should be applied on the same object.
|
||||
/// Higher orders are processed first.
|
||||
/// </summary>
|
||||
int Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order in which multiple decorators should be applied on the same object.
|
||||
/// Higher orders are processed first.
|
||||
/// </summary>
|
||||
int Order { get; set; }
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
#region Methods
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Attaches this <see cref="IDecorator"/> to the specified target.
|
||||
/// </summary>
|
||||
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be attached to.</param>
|
||||
void OnAttached(IDecoratable decoratable);
|
||||
|
||||
/// <summary>
|
||||
/// Attaches this <see cref="IDecorator"/> to the given target.
|
||||
/// </summary>
|
||||
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be attached to.</param>
|
||||
void OnAttached(IDecoratable decoratable);
|
||||
/// <summary>
|
||||
/// Detaches this <see cref="IDecorator"/> from the specified target.
|
||||
/// </summary>
|
||||
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be detached from.</param>
|
||||
void OnDetached(IDecoratable decoratable);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches this <see cref="IDecorator"/> from the given target.
|
||||
/// </summary>
|
||||
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be detached from.</param>
|
||||
void OnDetached(IDecoratable decoratable);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -1,9 +1,8 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Core.ILedGroup" />.
|
||||
/// </summary>
|
||||
public interface ILedGroupDecorator : IDecorator
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Core.ILedGroup" />.
|
||||
/// </summary>
|
||||
public interface ILedGroupDecorator : IDecorator
|
||||
{ }
|
||||
@ -2,297 +2,237 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using RGB.NET.Core.Layout;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc cref="AbstractBindable" />
|
||||
/// <inheritdoc cref="IRGBDevice{TDeviceInfo}" />
|
||||
/// <summary>
|
||||
/// Represents a generic RGB-device.
|
||||
/// </summary>
|
||||
public abstract class AbstractRGBDevice<TDeviceInfo> : Placeable, IRGBDevice<TDeviceInfo>
|
||||
where TDeviceInfo : class, IRGBDeviceInfo
|
||||
{
|
||||
/// <inheritdoc cref="AbstractBindable" />
|
||||
/// <inheritdoc cref="IRGBDevice{TDeviceInfo}" />
|
||||
/// <summary>
|
||||
/// Represents a generic RGB-device.
|
||||
/// </summary>
|
||||
public abstract class AbstractRGBDevice<TDeviceInfo> : AbstractBindable, IRGBDevice<TDeviceInfo>
|
||||
where TDeviceInfo : class, IRGBDeviceInfo
|
||||
private RGBSurface? _surface;
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
RGBSurface? IRGBDevice.Surface
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract TDeviceInfo DeviceInfo { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo;
|
||||
|
||||
private Point _location = new Point(0, 0);
|
||||
/// <inheritdoc />
|
||||
public Point Location
|
||||
get => _surface;
|
||||
set
|
||||
{
|
||||
get => _location;
|
||||
set
|
||||
if (SetProperty(ref _surface, value))
|
||||
{
|
||||
if (SetProperty(ref _location, value))
|
||||
UpdateActualData();
|
||||
if (value == null) OnDetached();
|
||||
else OnAttached();
|
||||
}
|
||||
}
|
||||
|
||||
private Size _size = Size.Invalid;
|
||||
/// <inheritdoc />
|
||||
public Size Size
|
||||
{
|
||||
get => _size;
|
||||
protected set
|
||||
{
|
||||
if (SetProperty(ref _size, value))
|
||||
UpdateActualData();
|
||||
}
|
||||
}
|
||||
|
||||
private Size _actualSize;
|
||||
/// <inheritdoc />
|
||||
public Size ActualSize
|
||||
{
|
||||
get => _actualSize;
|
||||
private set => SetProperty(ref _actualSize, value);
|
||||
}
|
||||
|
||||
private Rectangle _deviceRectangle;
|
||||
/// <inheritdoc />
|
||||
public Rectangle DeviceRectangle
|
||||
{
|
||||
get => _deviceRectangle;
|
||||
private set => SetProperty(ref _deviceRectangle, value);
|
||||
}
|
||||
|
||||
private Scale _scale = new Scale(1);
|
||||
/// <inheritdoc />
|
||||
public Scale Scale
|
||||
{
|
||||
get => _scale;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _scale, value))
|
||||
UpdateActualData();
|
||||
}
|
||||
}
|
||||
|
||||
private Rotation _rotation = new Rotation(0);
|
||||
/// <inheritdoc />
|
||||
public Rotation Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _rotation, value))
|
||||
UpdateActualData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the device needs to be flushed on every update.
|
||||
/// </summary>
|
||||
protected bool RequiresFlush { get; set; } = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DeviceUpdateMode UpdateMode { get; set; } = DeviceUpdateMode.Sync;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="Led"/> of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
protected Dictionary<LedId, Led> LedMapping { get; } = new Dictionary<LedId, Led>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="IRGBDeviceSpecialPart"/> associated with this <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
protected Dictionary<Type, IRGBDeviceSpecialPart> SpecialDeviceParts { get; } = new Dictionary<Type, IRGBDeviceSpecialPart>();
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <inheritdoc />
|
||||
Led IRGBDevice.this[LedId ledId] => LedMapping.TryGetValue(ledId, out Led led) ? led : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location));
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerable<Led> IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage]
|
||||
=> LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.LedRectangle) >= minOverlayPercentage);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private void UpdateActualData()
|
||||
{
|
||||
ActualSize = Size * Scale;
|
||||
DeviceRectangle = new Rectangle(Location, new Rectangle(new Rectangle(Location, ActualSize).Rotate(Rotation)).Size);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Update(bool flushLeds = false)
|
||||
{
|
||||
// Device-specific updates
|
||||
DeviceUpdate();
|
||||
|
||||
// Send LEDs to SDK
|
||||
List<Led> ledsToUpdate = GetLedsToUpdate(flushLeds)?.ToList() ?? new List<Led>();
|
||||
foreach (Led ledToUpdate in ledsToUpdate)
|
||||
ledToUpdate.Update();
|
||||
|
||||
if (UpdateMode.HasFlag(DeviceUpdateMode.Sync))
|
||||
UpdateLeds(ledsToUpdate);
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty));
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void SyncBack()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
SpecialDeviceParts.Clear();
|
||||
LedMapping.Clear();
|
||||
}
|
||||
catch { /* this really shouldn't happen */ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs device specific updates.
|
||||
/// </summary>
|
||||
protected virtual void DeviceUpdate()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Sends all the updated <see cref="Led"/> to the device.
|
||||
/// </summary>
|
||||
protected abstract void UpdateLeds(IEnumerable<Led> ledsToUpdate);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="Led"/> with the specified id.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The <see cref="LedId"/> to initialize.</param>
|
||||
/// <param name="ledRectangle">The <see cref="Rectangle"/> representing the position of the <see cref="Led"/> to initialize.</param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use InitializeLed(LedId ledId, Point location, Size size) instead.")]
|
||||
protected virtual Led InitializeLed(LedId ledId, Rectangle rectangle) => InitializeLed(ledId, rectangle.Location, rectangle.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="Led"/> with the specified id.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The <see cref="LedId"/> to initialize.</param>
|
||||
/// <param name="location">The location of the <see cref="Led"/> to initialize.</param>
|
||||
/// <param name="size">The size of the <see cref="Led"/> to initialize.</param>
|
||||
/// <returns>The initialized led.</returns>
|
||||
protected virtual Led InitializeLed(LedId ledId, Point location, Size size)
|
||||
{
|
||||
if ((ledId == LedId.Invalid) || LedMapping.ContainsKey(ledId)) return null;
|
||||
|
||||
Led led = new Led(this, ledId, location, size, CreateLedCustomData(ledId));
|
||||
LedMapping.Add(ledId, led);
|
||||
return led;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the give <see cref="Color"/> to the <see cref="Led"/> ignoring internal workflows regarding locks and update-requests.
|
||||
/// This should be only used for syncbacks!
|
||||
/// </summary>
|
||||
/// <param name="led">The <see cref="Led"/> the <see cref="Color"/> should be aplied to.</param>
|
||||
/// <param name="color">The <see cref="Color"/> to apply.</param>
|
||||
protected virtual void SetLedColorWithoutRequest(Led led, Color color)
|
||||
{
|
||||
if (led == null) return;
|
||||
|
||||
led.InternalColor = color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the given layout.
|
||||
/// </summary>
|
||||
/// <param name="layoutPath">The file containing the layout.</param>
|
||||
/// <param name="imageLayout">The name of the layout used to get the images of the leds.</param>
|
||||
/// <param name="createMissingLeds">If set to true a new led is initialized for every id in the layout if it doesn't already exist.</param>
|
||||
protected virtual DeviceLayout ApplyLayoutFromFile(string layoutPath, string imageLayout, bool createMissingLeds = false)
|
||||
{
|
||||
DeviceLayout layout = DeviceLayout.Load(layoutPath);
|
||||
if (layout != null)
|
||||
{
|
||||
string imageBasePath = string.IsNullOrWhiteSpace(layout.ImageBasePath) ? null : PathHelper.GetAbsolutePath(this, layout.ImageBasePath);
|
||||
if ((imageBasePath != null) && !string.IsNullOrWhiteSpace(layout.DeviceImage) && (DeviceInfo != null))
|
||||
DeviceInfo.Image = new Uri(Path.Combine(imageBasePath, layout.DeviceImage), UriKind.Absolute);
|
||||
|
||||
LedImageLayout ledImageLayout = layout.LedImageLayouts.FirstOrDefault(x => string.Equals(x.Layout, imageLayout, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
Size = new Size(layout.Width, layout.Height);
|
||||
|
||||
if (layout.Leds != null)
|
||||
foreach (LedLayout layoutLed in layout.Leds)
|
||||
{
|
||||
if (Enum.TryParse(layoutLed.Id, true, out LedId ledId))
|
||||
{
|
||||
if (!LedMapping.TryGetValue(ledId, out Led led) && createMissingLeds)
|
||||
led = InitializeLed(ledId, new Point(), new Size());
|
||||
|
||||
if (led != null)
|
||||
{
|
||||
led.Location = new Point(layoutLed.X, layoutLed.Y);
|
||||
led.Size = new Size(layoutLed.Width, layoutLed.Height);
|
||||
led.Shape = layoutLed.Shape;
|
||||
led.ShapeData = layoutLed.ShapeData;
|
||||
|
||||
LedImage image = ledImageLayout?.LedImages.FirstOrDefault(x => x.Id == layoutLed.Id);
|
||||
if ((imageBasePath != null) && !string.IsNullOrEmpty(image?.Image))
|
||||
led.Image = new Uri(Path.Combine(imageBasePath, image.Image), UriKind.Absolute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates provider-specific data associated with this <see cref="LedId"/>.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The <see cref="LedId"/>.</param>
|
||||
protected virtual object CreateLedCustomData(LedId ledId) => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddSpecialDevicePart<T>(T specialDevicePart)
|
||||
where T : class, IRGBDeviceSpecialPart
|
||||
=> SpecialDeviceParts[typeof(T)] = specialDevicePart;
|
||||
|
||||
/// <inheritdoc />
|
||||
public T GetSpecialDevicePart<T>()
|
||||
where T : class, IRGBDeviceSpecialPart
|
||||
=> SpecialDeviceParts.TryGetValue(typeof(T), out IRGBDeviceSpecialPart devicePart) ? (T)devicePart : default;
|
||||
|
||||
#region Enumerator
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
|
||||
public IEnumerator<Led> GetEnumerator() => LedMapping.Values.GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TDeviceInfo DeviceInfo { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<IColorCorrection> ColorCorrections { get; } = new List<IColorCorrection>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the device needs to be flushed on every update.
|
||||
/// </summary>
|
||||
protected bool RequiresFlush { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary containing all <see cref="Led"/> of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
protected Dictionary<LedId, Led> LedMapping { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the update queue used to update this device.
|
||||
/// </summary>
|
||||
protected IUpdateQueue UpdateQueue { get; }
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <inheritdoc />
|
||||
Led? IRGBDevice.this[LedId ledId] => LedMapping.TryGetValue(ledId, out Led? led) ? led : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
Led? IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.Boundary.Contains(location));
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerable<Led> IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage]
|
||||
=> LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.Boundary) >= minOverlayPercentage);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractRGBDevice{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="deviceInfo">The device info of this device.</param>
|
||||
/// <param name="updateQueue">The queue used to update this device.</param>
|
||||
protected AbstractRGBDevice(TDeviceInfo deviceInfo, IUpdateQueue updateQueue)
|
||||
{
|
||||
this.DeviceInfo = deviceInfo;
|
||||
this.UpdateQueue = updateQueue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Update(bool flushLeds = false)
|
||||
{
|
||||
// Device-specific updates
|
||||
DeviceUpdate();
|
||||
|
||||
// Send LEDs to SDK
|
||||
List<Led> ledsToUpdate = GetLedsToUpdate(flushLeds).ToList();
|
||||
|
||||
foreach (Led led in ledsToUpdate)
|
||||
led.Update();
|
||||
|
||||
UpdateLeds(ledsToUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable of LEDs that are changed and requires an update.
|
||||
/// </summary>
|
||||
/// <param name="flushLeds">Forces all LEDs to be treated as dirty.</param>
|
||||
/// <returns>The collection LEDs to update.</returns>
|
||||
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty)).Where(led => led.RequestedColor?.A > 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable of a custom data and color tuple for the specified leds.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Applies all <see cref="ColorCorrections"/>.
|
||||
/// if no <see cref="Led.CustomData"/> ist specified the <see cref="Led.Id"/> is used.
|
||||
/// </remarks>
|
||||
/// <param name="leds">The enumerable of leds to convert.</param>
|
||||
/// <returns>The enumerable of custom data and color tuples for the specified leds.</returns>
|
||||
protected virtual IEnumerable<(object key, Color color)> GetUpdateData(IEnumerable<Led> leds)
|
||||
{
|
||||
if (ColorCorrections.Count > 0)
|
||||
{
|
||||
foreach (Led led in leds)
|
||||
{
|
||||
Color color = led.Color;
|
||||
object key = led.CustomData ?? led.Id;
|
||||
|
||||
foreach (IColorCorrection colorCorrection in ColorCorrections)
|
||||
colorCorrection.ApplyTo(ref color);
|
||||
|
||||
yield return (key, color);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Led led in leds)
|
||||
{
|
||||
Color color = led.Color;
|
||||
object key = led.CustomData ?? led.Id;
|
||||
|
||||
yield return (key, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends all the updated <see cref="Led"/> to the device.
|
||||
/// </summary>
|
||||
protected virtual void UpdateLeds(IEnumerable<Led> ledsToUpdate) => UpdateQueue.SetData(GetUpdateData(ledsToUpdate));
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dispose()
|
||||
{
|
||||
try { UpdateQueue.Dispose(); } catch { /* :( */ }
|
||||
try { LedMapping.Clear(); } catch { /* this really shouldn't happen */ }
|
||||
|
||||
IdGenerator.ResetCounter(GetType().Assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs device specific updates.
|
||||
/// </summary>
|
||||
protected virtual void DeviceUpdate()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Led? AddLed(LedId ledId, in Point location, in Size size, object? customData = null)
|
||||
{
|
||||
if ((ledId == LedId.Invalid) || LedMapping.ContainsKey(ledId)) return null;
|
||||
|
||||
Led led = new(this, ledId, location, size, customData ?? GetLedCustomData(ledId));
|
||||
LedMapping.Add(ledId, led);
|
||||
return led;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Led? RemoveLed(LedId ledId)
|
||||
{
|
||||
if (ledId == LedId.Invalid) return null;
|
||||
if (!LedMapping.TryGetValue(ledId, out Led? led)) return null;
|
||||
|
||||
LedMapping.Remove(ledId);
|
||||
return led;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom data associated with the specified LED.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The id of the led.</param>
|
||||
/// <returns>The custom data for the specified LED.</returns>
|
||||
protected virtual object? GetLedCustomData(LedId ledId) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the device is attached to a surface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When overriden base should be called to validate boundries.
|
||||
/// </remarks>
|
||||
protected virtual void OnAttached()
|
||||
{
|
||||
if (Location == Point.Invalid) Location = new Point(0, 0);
|
||||
if (Size == Size.Invalid)
|
||||
{
|
||||
Rectangle ledRectangle = new(this.Select(x => x.Boundary));
|
||||
Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the device is detached from a surface.
|
||||
/// </summary>
|
||||
protected virtual void OnDetached() { }
|
||||
|
||||
#region Enumerator
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
|
||||
public IEnumerator<Led> GetEnumerator() => LedMapping.Values.GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
205
RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs
Normal file
205
RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs
Normal file
@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the abstract base implementation for a <see cref="IRGBDeviceProvider"/>.
|
||||
/// </summary>
|
||||
public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly double _defaultUpdateRateHardLimit;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsInitialized { get; protected set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ThrowsExceptions { get; protected set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<IRGBDevice> Devices { get; protected set; } = Enumerable.Empty<IRGBDevice>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dictionary containing the registered update triggers.
|
||||
/// Normally <see cref="UpdateTriggers"/> should be used to access them.
|
||||
/// </summary>
|
||||
protected Dictionary<int, IDeviceUpdateTrigger> UpdateTriggerMapping { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers => new ReadOnlyCollection<(int id, IDeviceUpdateTrigger trigger)>(UpdateTriggerMapping.Select(x => (x.Key, x.Value)).ToList());
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<ExceptionEventArgs>? Exception;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractRGBDeviceProvider" /> class.
|
||||
/// </summary>
|
||||
/// <param name="defaultUpdateRateHardLimit">The update rate hard limit all update triggers for this device provider are initialized with.</param>
|
||||
protected AbstractRGBDeviceProvider(double defaultUpdateRateHardLimit = 0)
|
||||
{
|
||||
this._defaultUpdateRateHardLimit = defaultUpdateRateHardLimit;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
|
||||
{
|
||||
ThrowsExceptions = throwExceptions;
|
||||
|
||||
try
|
||||
{
|
||||
Reset();
|
||||
|
||||
InitializeSDK();
|
||||
|
||||
Devices = new ReadOnlyCollection<IRGBDevice>(GetLoadedDevices(loadFilter).ToList());
|
||||
|
||||
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
|
||||
updateTrigger.Start();
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
catch (DeviceProviderException)
|
||||
{
|
||||
Reset();
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Reset();
|
||||
Throw(ex, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads devices and returns a filtered list of them.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The underlying loading of the devices happens in <see cref="LoadDevices"/>.
|
||||
/// </remarks>
|
||||
/// <param name="loadFilter"><see cref="RGBDeviceType"/>-flags to filter the device with.</param>
|
||||
/// <returns>The filtered collection of loaded devices.</returns>
|
||||
protected virtual IEnumerable<IRGBDevice> GetLoadedDevices(RGBDeviceType loadFilter)
|
||||
{
|
||||
List<IRGBDevice> devices = new();
|
||||
foreach (IRGBDevice device in LoadDevices())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (loadFilter.HasFlag(device.DeviceInfo.DeviceType))
|
||||
devices.Add(device);
|
||||
else
|
||||
device.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Throw(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the underlying SDK.
|
||||
/// </summary>
|
||||
protected abstract void InitializeSDK();
|
||||
|
||||
/// <summary>
|
||||
/// Loads all devices this device provider is capable of loading.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Filtering happens in <see cref="GetLoadedDevices"/>.
|
||||
/// </remarks>
|
||||
/// <returns>A collection of loaded devices.</returns>
|
||||
protected abstract IEnumerable<IRGBDevice> LoadDevices();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IDeviceUpdateTrigger"/> mapped to the specified id or a new one if the id wasn't requested before.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The creation of the update trigger happens in <see cref="CreateUpdateTrigger"/>.
|
||||
/// </remarks>
|
||||
/// <param name="id">The id of the update trigger.</param>
|
||||
/// <param name="updateRateHardLimit">The update rate hard limit to be set in the update trigger.</param>
|
||||
/// <returns>The update trigger mapped to the specified id.</returns>
|
||||
protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null)
|
||||
{
|
||||
if (!UpdateTriggerMapping.TryGetValue(id, out IDeviceUpdateTrigger? updaeTrigger))
|
||||
UpdateTriggerMapping[id] = (updaeTrigger = CreateUpdateTrigger(id, updateRateHardLimit ?? _defaultUpdateRateHardLimit));
|
||||
|
||||
return updaeTrigger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a update trigger with the specified id and the specified update rate hard limit.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the update trigger.</param>
|
||||
/// <param name="updateRateHardLimit">The update rate hard limit tobe set in the update trigger.</param>
|
||||
/// <returns>The newly created update trigger.</returns>
|
||||
protected virtual IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the device provider and disposes all devices and update triggers.
|
||||
/// </summary>
|
||||
protected virtual void Reset()
|
||||
{
|
||||
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
|
||||
updateTrigger.Dispose();
|
||||
|
||||
foreach (IRGBDevice device in Devices)
|
||||
device.Dispose();
|
||||
|
||||
Devices = Enumerable.Empty<IRGBDevice>();
|
||||
UpdateTriggerMapping.Clear();
|
||||
IsInitialized = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the <see cref="Exception"/>-event and throws the specified exception if <see cref="ThrowsExceptions"/> is true and it is not overriden in the event.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception to throw.</param>
|
||||
/// <param name="isCritical">Indicates if the exception is critical for device provider to work correctly.</param>
|
||||
protected virtual void Throw(Exception ex, bool isCritical = false)
|
||||
{
|
||||
ExceptionEventArgs args = new(ex, isCritical, ThrowsExceptions);
|
||||
try { OnException(args); } catch { /* we don't want to throw due to bad event handlers */ }
|
||||
|
||||
if (args.Throw)
|
||||
throw new DeviceProviderException(ex, isCritical);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws the <see cref="Exception"/> event.
|
||||
/// </summary>
|
||||
/// <param name="args">The parameters passed to the event.</param>
|
||||
protected virtual void OnException(ExceptionEventArgs args) => Exception?.Invoke(this, args);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dispose()
|
||||
{
|
||||
Reset();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a list of different device device update modes.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DeviceUpdateMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents nothing.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode which updates the leds of the device.
|
||||
/// </summary>
|
||||
Sync = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mode which reads the color of the leds of the device.
|
||||
/// This isn't supported by all devices!
|
||||
/// </summary>
|
||||
SyncBack = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Represents all update modes.
|
||||
/// </summary>
|
||||
NoUpdate = 1 << 0xFF
|
||||
}
|
||||
}
|
||||
@ -1,129 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc cref="IEnumerable{Led}" />
|
||||
/// <inheritdoc cref="IBindable" />
|
||||
/// <inheritdoc cref="IDisposable" />
|
||||
/// <summary>
|
||||
/// Represents a generic RGB-device.
|
||||
/// </summary>
|
||||
public interface IRGBDevice : IEnumerable<Led>, IPlaceable, IBindable, IDisposable
|
||||
{
|
||||
/// <inheritdoc cref="IEnumerable{T}" />
|
||||
/// <inheritdoc cref="IBindable" />
|
||||
/// <inheritdoc cref="IDisposable" />
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic RGB-device.
|
||||
/// Gets the surface this device is attached to.
|
||||
/// </summary>
|
||||
public interface IRGBDevice : IEnumerable<Led>, IBindable, IDisposable
|
||||
{
|
||||
#region Properties
|
||||
RGBSurface? Surface { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets generic information about the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
IRGBDeviceInfo DeviceInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the location of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
Point Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Size"/> of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
Size Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual <see cref="Size"/> of the <see cref="IRGBDevice"/>.
|
||||
/// This includes the <see cref="Scale"/>.
|
||||
/// </summary>
|
||||
Size ActualSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Rectangle"/> representing the logical location of the <see cref="DeviceRectangle"/> relative to the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
Rectangle DeviceRectangle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scale of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
Scale Scale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
Rotation Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DeviceUpdateMode"/> of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
DeviceUpdateMode UpdateMode { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Led"/> with the specified <see cref="LedId"/>.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The <see cref="LedId"/> of the <see cref="Led"/> to get.</param>
|
||||
/// <returns>The <see cref="Led"/> with the specified <see cref="LedId"/> or null if no <see cref="Led"/> is found.</returns>
|
||||
Led this[LedId ledId] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Led" /> at the given physical location.
|
||||
/// </summary>
|
||||
/// <param name="location">The <see cref="Point"/> to get the location from.</param>
|
||||
/// <returns>The <see cref="Led"/> at the given <see cref="Point"/> or null if no location is found.</returns>
|
||||
Led this[Point location] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Led" /> inside the given <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="referenceRect">The <see cref="Rectangle"/> to check.</param>
|
||||
/// <param name="minOverlayPercentage">The minimal percentage overlay a <see cref="Led"/> must have with the <see cref="Rectangle" /> to be taken into the list.</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<Led> this[Rectangle referenceRect, double minOverlayPercentage = 0.5] { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Perform an update for all dirty <see cref="Led"/>, or all <see cref="Led"/> if flushLeds is set to true.
|
||||
/// </summary>
|
||||
/// <param name="flushLeds">Specifies whether all <see cref="Led"/> (including clean ones) should be updated.</param>
|
||||
void Update(bool flushLeds = false);
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the internal state of the device to the real (physical) state.
|
||||
/// This isn't supported by all devices! Check <see cref="IRGBDeviceInfo.SupportsSyncBack"/> to see if it's supported or not.
|
||||
/// </summary>
|
||||
void SyncBack();
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given <see cref="IRGBDeviceSpecialPart"/> to the device.
|
||||
/// This will override existing <see cref="IRGBDeviceSpecialPart"/> of the same type.
|
||||
/// </summary>
|
||||
/// <param name="specialDevicePart">The <see cref="IRGBDeviceSpecialPart"/> to add.</param>
|
||||
/// <typeparam name="T">The generic typeof of the <see cref="IRGBDeviceSpecialPart"/> to add.</typeparam>
|
||||
void AddSpecialDevicePart<T>(T specialDevicePart) where T : class, IRGBDeviceSpecialPart;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the requested <see cref="IRGBDeviceSpecialPart"/> if available on this <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The generic type of the requested <see cref="IRGBDeviceSpecialPart"/>.</typeparam>
|
||||
/// <returns>The requested <see cref="IRGBDeviceSpecialPart"/> or null if not available in this <see cref="IRGBDevice"/>.</returns>
|
||||
T GetSpecialDevicePart<T>() where T : class, IRGBDeviceSpecialPart;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a generic RGB-device with an known device-info type.
|
||||
/// Gets generic information about the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
public interface IRGBDevice<out TDeviceInfo> : IRGBDevice
|
||||
where TDeviceInfo : IRGBDeviceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets generic information about the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
new TDeviceInfo DeviceInfo { get; }
|
||||
}
|
||||
IRGBDeviceInfo DeviceInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of color corrections applied to this device.
|
||||
/// </summary>
|
||||
IList<IColorCorrection> ColorCorrections { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Led"/> with the specified <see cref="LedId"/>.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The <see cref="LedId"/> of the <see cref="Led"/> to get.</param>
|
||||
/// <returns>The <see cref="Led"/> with the specified <see cref="LedId"/> or null if no <see cref="Led"/> is found.</returns>
|
||||
Led? this[LedId ledId] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Led" /> at the specified physical location.
|
||||
/// </summary>
|
||||
/// <param name="location">The <see cref="Point"/> to get the location from.</param>
|
||||
/// <returns>The <see cref="Led"/> at the specified <see cref="Point"/> or null if no location is found.</returns>
|
||||
Led? this[Point location] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Led" /> inside the specified <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="referenceRect">The <see cref="Rectangle"/> to check.</param>
|
||||
/// <param name="minOverlayPercentage">The minimal percentage overlay a <see cref="Led"/> must have with the <see cref="Rectangle" /> to be taken into the list.</param>
|
||||
/// <returns>A enumerable of leds inside the specified rectangle.</returns>
|
||||
IEnumerable<Led> this[Rectangle referenceRect, double minOverlayPercentage = 0.5] { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Perform an update for all dirty <see cref="Led"/>, or all <see cref="Led"/> if flushLeds is set to true.
|
||||
/// </summary>
|
||||
/// <param name="flushLeds">Specifies whether all <see cref="Led"/> (including clean ones) should be updated.</param>
|
||||
void Update(bool flushLeds = false);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a led to the device.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The id of the led.</param>
|
||||
/// <param name="location">The location of the led on the device.</param>
|
||||
/// <param name="size">The size of the led.</param>
|
||||
/// <param name="customData">Custom data saved on the led.</param>
|
||||
/// <returns>The newly added led or <c>null</c> if a led with this id is already added.</returns>
|
||||
Led? AddLed(LedId ledId, in Point location, in Size size, object? customData = null);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the led with the specified id from the device.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The id of the led to remove.</param>
|
||||
/// <returns>The removed led or <c>null</c> if there was no led with the specified id.</returns>
|
||||
Led? RemoveLed(LedId ledId);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a generic RGB-device with an known device-info type.
|
||||
/// </summary>
|
||||
public interface IRGBDevice<out TDeviceInfo> : IRGBDevice
|
||||
where TDeviceInfo : IRGBDeviceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets generic information about the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
new TDeviceInfo DeviceInfo { get; }
|
||||
}
|
||||
@ -1,49 +1,36 @@
|
||||
using System;
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
/// <summary>
|
||||
/// Represents a generic information for a <see cref="IRGBDevice"/>
|
||||
/// </summary>
|
||||
public interface IRGBDeviceInfo
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic information for a <see cref="IRGBDevice"/>
|
||||
/// Gets the <see cref="RGBDeviceType"/> of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
public interface IRGBDeviceInfo
|
||||
{
|
||||
#region Properties & Fields
|
||||
RGBDeviceType DeviceType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RGBDeviceType"/> of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
RGBDeviceType DeviceType { get; }
|
||||
/// <summary>
|
||||
/// Unique name of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
string DeviceName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique name of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
string DeviceName { get; }
|
||||
/// <summary>
|
||||
/// Gets the manufacturer-name of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
string Manufacturer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the manufacturer-name of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
string Manufacturer { get; }
|
||||
/// <summary>
|
||||
/// Gets the model-name of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
string Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model-name of the <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
string Model { get; }
|
||||
/// <summary>
|
||||
/// Gets custom metadata added to the layout.
|
||||
/// </summary>
|
||||
object? LayoutMetadata { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lighting capability of the <see cref="IRGBDevice"/>
|
||||
/// </summary>
|
||||
RGBDeviceLighting Lighting { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bool indicating, if the <see cref="IRGBDevice"/> supports SynBacks or not.
|
||||
/// </summary>
|
||||
bool SupportsSyncBack { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URI of an image of the <see cref="IRGBDevice"/> or null if there is no image.
|
||||
/// </summary>
|
||||
Uri Image { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -1,48 +1,57 @@
|
||||
using System;
|
||||
// ReSharper disable EventNeverSubscribedTo.Global
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic device provider.
|
||||
/// </summary>
|
||||
public interface IRGBDeviceProvider : IDisposable
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic device provider.
|
||||
/// Indicates if the used SDK is initialized and ready to use.
|
||||
/// </summary>
|
||||
public interface IRGBDeviceProvider : IDisposable
|
||||
{
|
||||
#region Properties & Fields
|
||||
bool IsInitialized { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the used SDK is initialized and ready to use.
|
||||
/// </summary>
|
||||
bool IsInitialized { get; }
|
||||
/// <summary>
|
||||
/// Indicates if exceptions in the device provider are thrown or silently ignored.
|
||||
/// </summary>
|
||||
bool ThrowsExceptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IRGBDevice"/> loaded by this <see cref="IRGBDeviceProvider"/>.
|
||||
/// </summary>
|
||||
IEnumerable<IRGBDevice> Devices { get; }
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IRGBDevice"/> loaded by this <see cref="IRGBDeviceProvider"/>.
|
||||
/// </summary>
|
||||
IEnumerable<IRGBDevice> Devices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the application has exclusive access to devices or not.
|
||||
/// </summary>
|
||||
bool HasExclusiveAccess { get; }
|
||||
/// <summary>
|
||||
/// Gets a collection <see cref="IDeviceUpdateTrigger"/> registered to this device provider.
|
||||
/// </summary>
|
||||
IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers { get; }
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="IRGBDeviceProvider"/> if not already happened or reloads it if it is already initialized.
|
||||
/// </summary>
|
||||
/// <param name="loadFilter">Specifies which types of devices to load.</param>
|
||||
/// <param name="exclusiveAccessIfPossible">Specifies whether the application should request exclusive access of possible or not.</param>
|
||||
/// <param name="throwExceptions">Specifies whether exception during the initialization sequence should be thrown or not.</param>
|
||||
/// <returns></returns>
|
||||
bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false);
|
||||
/// <summary>
|
||||
/// Occurs when an exception is thrown in the device provider.
|
||||
/// </summary>
|
||||
event EventHandler<ExceptionEventArgs>? Exception;
|
||||
|
||||
/// <summary>
|
||||
/// Resets all handled <see cref="IRGBDevice"/> back top default.
|
||||
/// </summary>
|
||||
void ResetDevices();
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the device provider and loads available devices.
|
||||
/// </summary>
|
||||
/// <param name="loadFilter"><see cref="RGBDeviceType"/>-flags to filter the devices to load.</param>
|
||||
/// <param name="throwExceptions">Specifies if exceptions should be thrown or silently be ignored.</param>
|
||||
/// <returns><c>true</c> if the initialization was successful; <c>false</c> otherwise.</returns>
|
||||
bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic device provider loaded used to dynamically load devices into an application.
|
||||
/// This class should always provide an empty public constructor!
|
||||
/// </summary>
|
||||
public interface IRGBDeviceProviderLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the returned device-provider needs some specific initialization before use.
|
||||
/// </summary>
|
||||
bool RequiresInitialization { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device-provider.
|
||||
/// </summary>
|
||||
/// <returns>The device-provider.</returns>
|
||||
IRGBDeviceProvider GetDeviceProvider();
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a special part of a <see cref="T:RGB.NET.Core.IRGBDevice" />.
|
||||
/// </summary>
|
||||
public interface IRGBDeviceSpecialPart : IEnumerable<Led>
|
||||
{ }
|
||||
}
|
||||
17
RGB.NET.Core/Devices/KeyboardLayoutType.cs
Normal file
17
RGB.NET.Core/Devices/KeyboardLayoutType.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable 1591
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a list of available keyboard layout types.
|
||||
/// </summary>
|
||||
public enum KeyboardLayoutType
|
||||
{
|
||||
Unknown = 0,
|
||||
ANSI = 1,
|
||||
ISO = 2,
|
||||
JIS = 3,
|
||||
ABNT = 4,
|
||||
KS = 5
|
||||
}
|
||||
@ -1,151 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RGB.NET.Core.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the serializable layout of a <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[XmlRoot("Device")]
|
||||
public class DeviceLayout
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RGBDeviceType"/> of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Type")]
|
||||
public RGBDeviceType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RGBDeviceLighting"/> of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Lighting")]
|
||||
public RGBDeviceLighting Lighting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vendor of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Vendor")]
|
||||
public string Vendor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Model")]
|
||||
public string Model { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Core.Shape"/> of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Shape")]
|
||||
[DefaultValue(Shape.Rectangle)]
|
||||
public Shape Shape { get; set; } = Shape.Rectangle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Width")]
|
||||
public double Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlElement("Height")]
|
||||
public double Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of one 'unit' used for the calculation of led positions and sizes.
|
||||
/// </summary>
|
||||
[XmlElement("LedUnitWidth")]
|
||||
[DefaultValue(19.0)]
|
||||
public double LedUnitWidth { get; set; } = 19.0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height of one 'unit' used for the calculation of led positions and sizes.
|
||||
/// </summary>
|
||||
[XmlElement("LedUnitHeight")]
|
||||
[DefaultValue(19.0)]
|
||||
public double LedUnitHeight { get; set; } = 19.0;
|
||||
|
||||
/// <summary>
|
||||
/// The path images for this device are collected in.
|
||||
/// </summary>
|
||||
[XmlElement("ImageBasePath")]
|
||||
public string ImageBasePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The image file for this device.
|
||||
/// </summary>
|
||||
[XmlElement("DeviceImage")]
|
||||
public string DeviceImage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of <see cref="LedLayout"/> representing all the <see cref="Led"/> of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlArray("Leds")]
|
||||
public List<LedLayout> Leds { get; set; } = new List<LedLayout>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of <see cref="LedImageLayout"/> representing the layouts for the images of all the <see cref="Led"/> of the <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
[XmlArray("LedImageLayouts")]
|
||||
public List<LedImageLayout> LedImageLayouts { get; set; } = new List<LedImageLayout>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="DeviceLayout"/> from the given xml.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the xml file.</param>
|
||||
/// <returns>The deserialized <see cref="DeviceLayout"/>.</returns>
|
||||
public static DeviceLayout Load(string path)
|
||||
{
|
||||
if (!File.Exists(path)) return null;
|
||||
|
||||
try
|
||||
{
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(DeviceLayout));
|
||||
using (StreamReader reader = new StreamReader(path))
|
||||
{
|
||||
DeviceLayout layout = serializer.Deserialize(reader) as DeviceLayout;
|
||||
if (layout?.Leds != null)
|
||||
{
|
||||
LedLayout lastLed = null;
|
||||
foreach (LedLayout led in layout.Leds)
|
||||
{
|
||||
led.CalculateValues(layout, lastLed);
|
||||
lastLed = led;
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RGB.NET.Core.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the serializable image-data of a specific <see cref="Led"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[XmlRoot("LedImage")]
|
||||
public class LedImage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the <see cref="LedImage"/>.
|
||||
/// </summary>
|
||||
[XmlAttribute("Id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image of the <see cref="LedImage"/>.
|
||||
/// </summary>
|
||||
[XmlAttribute("Image")]
|
||||
public string Image { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RGB.NET.Core.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the serializable collection of <see cref="LedImage"/> for a specific layout.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[XmlRoot("LedImageLayout")]
|
||||
public class LedImageLayout
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the layout of the <see cref="LedImage"/>.
|
||||
/// </summary>
|
||||
[XmlAttribute("Layout")]
|
||||
[DefaultValue(null)]
|
||||
public string Layout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of <see cref="LedImage"/> representing the images of all the <see cref="Led"/> of the represented layout.
|
||||
/// </summary>
|
||||
[XmlArray("LedImages")]
|
||||
public List<LedImage> LedImages { get; set; } = new List<LedImage>();
|
||||
}
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RGB.NET.Core.Layout
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the serializable layout of a <see cref="Led"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[XmlType("Led")]
|
||||
public class LedLayout
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the <see cref="LedLayout"/>.
|
||||
/// </summary>
|
||||
[XmlAttribute("Id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the descriptive <see cref="RGB.NET.Core.Shape"/> of the <see cref="LedLayout"/>.
|
||||
/// This property is for XML-serialization only and should not be directly accessed.
|
||||
/// </summary>
|
||||
[XmlElement("Shape")]
|
||||
[DefaultValue("Rectangle")]
|
||||
public string DescriptiveShape { get; set; } = "Rectangle";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the descriptive x-position of the <see cref="LedLayout"/>.
|
||||
/// This property is for XML-serialization only and should not be directly accessed.
|
||||
/// </summary>
|
||||
[XmlElement("X")]
|
||||
[DefaultValue("+")]
|
||||
public string DescriptiveX { get; set; } = "+";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the descriptive y-position of the <see cref="LedLayout"/>.
|
||||
/// This property is for XML-serialization only and should not be directly accessed.
|
||||
/// </summary>
|
||||
[XmlElement("Y")]
|
||||
[DefaultValue("=")]
|
||||
public string DescriptiveY { get; set; } = "=";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the descriptive width of the <see cref="LedLayout"/>.
|
||||
/// This property is for XML-serialization only and should not be directly accessed.
|
||||
/// </summary>
|
||||
[XmlElement("Width")]
|
||||
[DefaultValue("1.0")]
|
||||
public string DescriptiveWidth { get; set; } = "1.0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the descriptive height of the <see cref="LedLayout"/>.
|
||||
/// This property is for XML-serialization only and should not be directly accessed.
|
||||
/// </summary>
|
||||
[XmlElement("Height")]
|
||||
[DefaultValue("1.0")]
|
||||
public string DescriptiveHeight { get; set; } = "1.0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RGB.NET.Core.Shape"/> of the <see cref="LedLayout"/>.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public Shape Shape { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vecor-data representing a custom-shape of the <see cref="LedLayout"/>.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public string ShapeData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the x-position of the <see cref="LedLayout"/>.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public double X { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the y-position of the <see cref="LedLayout"/>.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public double Y { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the <see cref="LedLayout"/>.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public double Width { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the <see cref="LedLayout"/>.
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public double Height { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the position- and size-data from the respective descriptive values.
|
||||
/// </summary>
|
||||
/// <param name="device">The <see cref="DeviceLayout"/> this <see cref="LedLayout"/> belongs to.</param>
|
||||
/// <param name="lastLed">The <see cref="LedLayout"/> previously calculated.</param>
|
||||
public void CalculateValues(DeviceLayout device, LedLayout lastLed)
|
||||
{
|
||||
if (!Enum.TryParse(DescriptiveShape, true, out Shape shape))
|
||||
{
|
||||
shape = Shape.Custom;
|
||||
ShapeData = DescriptiveShape;
|
||||
}
|
||||
Shape = shape;
|
||||
|
||||
Width = GetSizeValue(DescriptiveWidth, device.LedUnitWidth);
|
||||
Height = GetSizeValue(DescriptiveHeight, device.LedUnitHeight);
|
||||
|
||||
X = GetLocationValue(DescriptiveX, lastLed?.X ?? 0, Width, lastLed?.Width ?? 0);
|
||||
Y = GetLocationValue(DescriptiveY, lastLed?.Y ?? 0, Height, lastLed?.Height ?? 0);
|
||||
}
|
||||
|
||||
private double GetLocationValue(string value, double lastValue, double currentSize, double lastSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return 0;
|
||||
|
||||
value = value.Replace(" ", string.Empty);
|
||||
|
||||
if (string.Equals(value, "=", StringComparison.Ordinal))
|
||||
return lastValue;
|
||||
|
||||
if (string.Equals(value, "+", StringComparison.Ordinal))
|
||||
return lastValue + lastSize;
|
||||
|
||||
if (value.StartsWith("+", StringComparison.Ordinal))
|
||||
return lastValue + lastSize + double.Parse(value.Substring(1), CultureInfo.InvariantCulture);
|
||||
|
||||
if (string.Equals(value, "-", StringComparison.Ordinal))
|
||||
return lastValue - currentSize;
|
||||
|
||||
if (value.StartsWith("-", StringComparison.Ordinal))
|
||||
return lastValue - currentSize - double.Parse(value.Substring(1), CultureInfo.InvariantCulture);
|
||||
|
||||
if (string.Equals(value, "~", StringComparison.Ordinal))
|
||||
return (lastValue + lastSize) - currentSize;
|
||||
|
||||
if (value.StartsWith("~", StringComparison.Ordinal))
|
||||
return (lastValue + lastSize) - currentSize - double.Parse(value.Substring(1), CultureInfo.InvariantCulture);
|
||||
|
||||
return double.Parse(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private double GetSizeValue(string value, double unitSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return 0;
|
||||
|
||||
value = value.Replace(" ", string.Empty);
|
||||
|
||||
if (value.EndsWith("mm", StringComparison.OrdinalIgnoreCase))
|
||||
return double.Parse(value.Substring(0, value.Length - 2), CultureInfo.InvariantCulture);
|
||||
|
||||
return unitSize * double.Parse(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
using RGB.NET.Core.Layout;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a list of different lightning-modes used by <see cref="DeviceLayout"/>.
|
||||
/// </summary>
|
||||
public enum RGBDeviceLighting
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IRGBDevice"/> doesn't support lighting,
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IRGBDevice"/> supports per-key-lightning.
|
||||
/// </summary>
|
||||
Key = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IRGBDevice"/> supports per-device-lightning.
|
||||
/// </summary>
|
||||
Device = 2,
|
||||
}
|
||||
}
|
||||
@ -1,96 +1,105 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a list of different types of device.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum RGBDeviceType
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a list of different types of device.
|
||||
/// Represents nothing.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum RGBDeviceType
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents nothing.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a keyboard.
|
||||
/// </summary>
|
||||
Keyboard = 1 << 0,
|
||||
/// <summary>
|
||||
/// Represents a keyboard.
|
||||
/// </summary>
|
||||
Keyboard = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mouse.
|
||||
/// </summary>
|
||||
Mouse = 1 << 1,
|
||||
/// <summary>
|
||||
/// Represents a mouse.
|
||||
/// </summary>
|
||||
Mouse = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a headset.
|
||||
/// </summary>
|
||||
Headset = 1 << 2,
|
||||
/// <summary>
|
||||
/// Represents a headset.
|
||||
/// </summary>
|
||||
Headset = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mousepad.
|
||||
/// </summary>
|
||||
Mousepad = 1 << 3,
|
||||
/// <summary>
|
||||
/// Represents a mousepad.
|
||||
/// </summary>
|
||||
Mousepad = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a LED-stipe.
|
||||
/// </summary>
|
||||
LedStripe = 1 << 4,
|
||||
/// <summary>
|
||||
/// Represents a LED-stipe.
|
||||
/// </summary>
|
||||
LedStripe = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a LED-matrix.
|
||||
/// </summary>
|
||||
LedMatrix = 1 << 5,
|
||||
/// <summary>
|
||||
/// Represents a LED-matrix.
|
||||
/// </summary>
|
||||
LedMatrix = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Mainboard.
|
||||
/// </summary>
|
||||
Mainboard = 1 << 6,
|
||||
/// <summary>
|
||||
/// Represents a Mainboard.
|
||||
/// </summary>
|
||||
Mainboard = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Graphics card.
|
||||
/// </summary>
|
||||
GraphicsCard = 1 << 7,
|
||||
/// <summary>
|
||||
/// Represents a Graphics card.
|
||||
/// </summary>
|
||||
GraphicsCard = 1 << 7,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a DRAM-bank.
|
||||
/// </summary>
|
||||
DRAM = 1 << 8,
|
||||
/// <summary>
|
||||
/// Represents a DRAM-bank.
|
||||
/// </summary>
|
||||
DRAM = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a headset stand.
|
||||
/// </summary>
|
||||
HeadsetStand = 1 << 9,
|
||||
/// <summary>
|
||||
/// Represents a headset stand.
|
||||
/// </summary>
|
||||
HeadsetStand = 1 << 9,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a keypad.
|
||||
/// </summary>
|
||||
Keypad = 1 << 10,
|
||||
/// <summary>
|
||||
/// Represents a keypad.
|
||||
/// </summary>
|
||||
Keypad = 1 << 10,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a fan.
|
||||
/// </summary>
|
||||
Fan = 1 << 11,
|
||||
/// <summary>
|
||||
/// Represents a fan.
|
||||
/// </summary>
|
||||
Fan = 1 << 11,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a speaker
|
||||
/// </summary>
|
||||
Speaker = 1 << 12,
|
||||
/// <summary>
|
||||
/// Represents a speaker
|
||||
/// </summary>
|
||||
Speaker = 1 << 12,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a cooler.
|
||||
/// </summary>
|
||||
Cooler = 1 << 13,
|
||||
/// <summary>
|
||||
/// Represents a cooler.
|
||||
/// </summary>
|
||||
Cooler = 1 << 13,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device where the type is not known or not present in the list.
|
||||
/// </summary>
|
||||
Unknown = 1 << 31,
|
||||
/// <summary>
|
||||
/// Represents a monitor.
|
||||
/// </summary>
|
||||
Monitor = 1 << 14,
|
||||
|
||||
/// <summary>
|
||||
/// Represents all devices.
|
||||
/// </summary>
|
||||
All = ~None
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents a generic led-controller.
|
||||
/// </summary>
|
||||
LedController = 1 << 15,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device where the type is not known or not present in the list.
|
||||
/// </summary>
|
||||
Unknown = 1 << 31,
|
||||
|
||||
/// <summary>
|
||||
/// Represents all devices.
|
||||
/// </summary>
|
||||
All = ~None
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cooler-device
|
||||
/// </summary>
|
||||
public interface ICooler : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a cooler-device
|
||||
/// </summary>
|
||||
public interface ICooler : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a DRAM-device
|
||||
/// </summary>
|
||||
public interface IDRAM : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a DRAM-device
|
||||
/// </summary>
|
||||
public interface IDRAM : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// represents a fan-device
|
||||
/// </summary>
|
||||
public interface IFan : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// represents a fan-device
|
||||
/// </summary>
|
||||
public interface IFan : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a graphics-card-device
|
||||
/// </summary>
|
||||
public interface IGraphicsCard : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a graphics-card-device
|
||||
/// </summary>
|
||||
public interface IGraphicsCard : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a headset-device
|
||||
/// </summary>
|
||||
public interface IHeadset : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a headset-device
|
||||
/// </summary>
|
||||
public interface IHeadset : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a headset-stand-device
|
||||
/// </summary>
|
||||
public interface IHeadsetStand : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a headset-stand-device
|
||||
/// </summary>
|
||||
public interface IHeadsetStand : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,23 @@
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic keyboard-device.
|
||||
/// </summary>
|
||||
public interface IKeyboard : IRGBDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a keyboard-device
|
||||
/// Gets the device information assiciated with this device.
|
||||
/// </summary>
|
||||
public interface IKeyboard : IRGBDevice
|
||||
{ }
|
||||
new IKeyboardDeviceInfo DeviceInfo { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic keyboard device information.
|
||||
/// </summary>
|
||||
public interface IKeyboardDeviceInfo : IRGBDeviceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="KeyboardLayoutType"/> of the keyboard.
|
||||
/// </summary>
|
||||
KeyboardLayoutType Layout { get; }
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a keypad-device
|
||||
/// </summary>
|
||||
public interface IKeypad : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a keypad-device
|
||||
/// </summary>
|
||||
public interface IKeypad : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a led-matrix-device
|
||||
/// </summary>
|
||||
public interface ILedMatrix : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a led-matrix-device
|
||||
/// </summary>
|
||||
public interface ILedMatrix : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a led-stripe-device
|
||||
/// </summary>
|
||||
public interface ILedStripe : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a led-stripe-device
|
||||
/// </summary>
|
||||
public interface ILedStripe : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mainboard-device
|
||||
/// </summary>
|
||||
public interface IMainboard : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mainboard-device
|
||||
/// </summary>
|
||||
public interface IMainboard : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mouse-device
|
||||
/// </summary>
|
||||
public interface IMouse : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mouse-device
|
||||
/// </summary>
|
||||
public interface IMouse : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mousepad-device
|
||||
/// </summary>
|
||||
public interface IMousepad : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mousepad-device
|
||||
/// </summary>
|
||||
public interface IMousepad : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a speaker-device
|
||||
/// </summary>
|
||||
public interface ISpeaker : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a speaker-device
|
||||
/// </summary>
|
||||
public interface ISpeaker : IRGBDevice
|
||||
{ }
|
||||
@ -1,8 +1,7 @@
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a device with unkown or not specified type.
|
||||
/// </summary>
|
||||
public interface IUnknownDevice : IRGBDevice
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a device with unkown or not specified type.
|
||||
/// </summary>
|
||||
public interface IUnknownDevice : IRGBDevice
|
||||
{ }
|
||||
@ -3,35 +3,48 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Exception" />-event.
|
||||
/// </summary>
|
||||
public class ExceptionEventArgs : EventArgs
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="System.Exception"/> which is responsible for the event-call.
|
||||
/// </summary>
|
||||
public Exception Exception { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bool indicating if the exception is critical for the thrower.
|
||||
/// </summary>
|
||||
public bool IsCritical { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the exception should be thrown after the event is handled.
|
||||
/// </summary>
|
||||
public bool Throw { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Exception" />-event.
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.ExceptionEventArgs" /> class.
|
||||
/// </summary>
|
||||
public class ExceptionEventArgs : EventArgs
|
||||
/// <param name="exception">The <see cref="T:System.Exception" /> which is responsible for the event-call.</param>
|
||||
/// <param name="isCritical">Indicates if the exception is critical for the thrower.</param>
|
||||
/// <param name="throw">Indicates if the exception should be thrown after the event is handled.</param>
|
||||
public ExceptionEventArgs(Exception exception, bool isCritical = false, bool @throw = false)
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="System.Exception"/> which is responsible for the event-call.
|
||||
/// </summary>
|
||||
public Exception Exception { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.ExceptionEventArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="exception">The <see cref="T:System.Exception" /> which is responsible for the event-call.</param>
|
||||
public ExceptionEventArgs(Exception exception)
|
||||
{
|
||||
this.Exception = exception;
|
||||
}
|
||||
|
||||
#endregion
|
||||
this.Exception = exception;
|
||||
this.IsCritical = isCritical;
|
||||
this.Throw = @throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
public class ResolvePathEventArgs : EventArgs
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filename used to resolve the path.
|
||||
/// This has to be checked for null since it'S possible that only <see cref="FileName"/> is used.
|
||||
/// Also check <see cref="RelativePath "/> before use.
|
||||
/// </summary>
|
||||
public string RelativePart { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filename used to resolve the path.
|
||||
/// This has to be checked for null since it'S possible that only <see cref="RelativePart"/> is used.
|
||||
/// Also check <see cref="RelativePath "/> before use.
|
||||
/// </summary>
|
||||
public string FileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative path used to resolve the path.
|
||||
/// If this is set <see cref="RelativePart" /> and <see cref="FileName" /> are unused.
|
||||
/// </summary>
|
||||
public string RelativePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the resolved path.
|
||||
/// </summary>
|
||||
public string FinalPath { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Corer.ResolvePathEventArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="relativePart">The filename used to resolve the path.</param>
|
||||
/// <param name="fileName">The filename used to resolve the path.</param>
|
||||
/// <param name="finalPath">The relative part used to resolve the path.</param>
|
||||
public ResolvePathEventArgs(string relativePart, string fileName, string finalPath)
|
||||
{
|
||||
this.RelativePart = relativePart;
|
||||
this.FileName = fileName;
|
||||
this.FinalPath = finalPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Corer.ResolvePathEventArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The relative path used to resolve the path.</param>
|
||||
/// <param name="finalPath">The relative part used to resolve the path.</param>
|
||||
public ResolvePathEventArgs(string relativePath, string finalPath)
|
||||
{
|
||||
this.RelativePath = relativePath;
|
||||
this.FinalPath = finalPath;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -2,51 +2,65 @@
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.SurfaceLayoutChanged" />-event.
|
||||
/// </summary>
|
||||
public class SurfaceLayoutChangedEventArgs : EventArgs
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IRGBDevice"/> that caused the change. Returns null if the change isn't caused by a <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
public IRGBDevice? Devices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if the event is caused by the addition of a new <see cref="IRGBDevice"/> to the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
public bool DeviceAdded { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if the event is caused by the removal of a <see cref="IRGBDevice"/> to the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
public bool DeviceRemoved { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if the event is caused by a changed location or size of one of the <see cref="IRGBDevice"/> on the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
public bool DeviceChanged { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.SurfaceLayoutChanged" />-event.
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.SurfaceLayoutChangedEventArgs" /> class.
|
||||
/// </summary>
|
||||
public class SurfaceLayoutChangedEventArgs : EventArgs
|
||||
/// <param name="devices">The <see cref="T:RGB.NET.Core.IRGBDevice" /> that caused the change.</param>
|
||||
/// <param name="deviceAdded">A value indicating if the event is caused by the addition of a new <see cref="T:RGB.NET.Core.IRGBDevice" /> to the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
|
||||
/// <param name="deviceRemoved">A value indicating if the event is caused by the removal of a <see cref="T:RGB.NET.Core.IRGBDevice" /> from the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
|
||||
/// <param name="deviceChanged">A value indicating if the event is caused by a change to a <see cref="T:RGB.NET.Core.IRGBDevice" /> on the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
|
||||
private SurfaceLayoutChangedEventArgs(IRGBDevice? devices, bool deviceAdded, bool deviceRemoved, bool deviceChanged)
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IRGBDevice"/> that caused the change. Returns null if the change isn't caused by a <see cref="IRGBDevice"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<IRGBDevice> Devices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if the event is caused by the addition of a new <see cref="IRGBDevice"/> to the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
public bool DeviceAdded { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if the event is caused by a changed location of one of the devices on the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
public bool DeviceLocationChanged { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.SurfaceLayoutChangedEventArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="devices">The <see cref="T:RGB.NET.Core.IRGBDevice" /> that caused the change.</param>
|
||||
/// <param name="deviceAdded">A value indicating if the event is caused by the addition of a new <see cref="T:RGB.NET.Core.IRGBDevice" /> to the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
|
||||
/// <param name="deviceLocationChanged">A value indicating if the event is caused by a changed location of one of the devices on the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
|
||||
public SurfaceLayoutChangedEventArgs(IEnumerable<IRGBDevice> devices, bool deviceAdded, bool deviceLocationChanged)
|
||||
{
|
||||
this.Devices = devices;
|
||||
this.DeviceAdded = deviceAdded;
|
||||
this.DeviceLocationChanged = deviceLocationChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
this.Devices = devices;
|
||||
this.DeviceAdded = deviceAdded;
|
||||
this.DeviceRemoved = deviceRemoved;
|
||||
this.DeviceChanged = deviceChanged;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory
|
||||
|
||||
internal static SurfaceLayoutChangedEventArgs FromAddedDevice(IRGBDevice device) => new(device, true, false, false);
|
||||
internal static SurfaceLayoutChangedEventArgs FromRemovedDevice(IRGBDevice device) => new(device, false, true, false);
|
||||
internal static SurfaceLayoutChangedEventArgs FromChangedDevice(IRGBDevice device) => new(device, false, false, true);
|
||||
internal static SurfaceLayoutChangedEventArgs Misc() => new(null, false, false, false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,11 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updated" />-event.
|
||||
/// </summary>
|
||||
public class UpdatedEventArgs : EventArgs
|
||||
{ }
|
||||
}
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updated" />-event.
|
||||
/// </summary>
|
||||
public class UpdatedEventArgs : EventArgs
|
||||
{ }
|
||||
@ -3,49 +3,48 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updating" />-event.
|
||||
/// </summary>
|
||||
public class UpdatingEventArgs : EventArgs
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the elapsed time (in seconds) since the last update.
|
||||
/// </summary>
|
||||
public double DeltaTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trigger causing this update.
|
||||
/// </summary>
|
||||
public IUpdateTrigger? Trigger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom-data provided by the trigger for this update.
|
||||
/// </summary>
|
||||
public CustomUpdateData CustomData { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updating" />-event.
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.UpdatingEventArgs" /> class.
|
||||
/// </summary>
|
||||
public class UpdatingEventArgs : EventArgs
|
||||
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
|
||||
/// <param name="trigger">The trigger causing this update.</param>
|
||||
/// <param name="customData">The custom-data provided by the trigger for this update.</param>
|
||||
public UpdatingEventArgs(double deltaTime, IUpdateTrigger? trigger, CustomUpdateData customData)
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the elapsed time (in seconds) since the last update.
|
||||
/// </summary>
|
||||
public double DeltaTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trigger causing this update.
|
||||
/// </summary>
|
||||
public IUpdateTrigger Trigger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom-data provided by the trigger for this update.
|
||||
/// </summary>
|
||||
public CustomUpdateData CustomData { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.UpdatingEventArgs" /> class.
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
|
||||
/// <param name="trigger">The trigger causing this update.</param>
|
||||
/// <param name="customData">The custom-data provided by the trigger for this update.</param>
|
||||
public UpdatingEventArgs(double deltaTime, IUpdateTrigger trigger, CustomUpdateData customData)
|
||||
{
|
||||
this.DeltaTime = deltaTime;
|
||||
this.Trigger = trigger;
|
||||
this.CustomData = customData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
this.DeltaTime = deltaTime;
|
||||
this.Trigger = trigger;
|
||||
this.CustomData = customData;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
34
RGB.NET.Core/Exceptions/DeviceProviderException.cs
Normal file
34
RGB.NET.Core/Exceptions/DeviceProviderException.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents an exception thrown by a <see cref="IRGBDeviceProvider" />.
|
||||
/// </summary>
|
||||
public class DeviceProviderException : ApplicationException
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bool indicating if the exception is critical and shouldn't be ingored.
|
||||
/// </summary>
|
||||
public bool IsCritical { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeviceProviderException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="innerException">The exception that is the casue of the current exception or null if this exception was thrown on purpose.</param>
|
||||
/// <param name="isCritical">A value indicating if the exception is critical and shouldn't be ignored.</param>
|
||||
public DeviceProviderException(Exception? innerException, bool isCritical)
|
||||
: base(innerException?.Message, innerException)
|
||||
{
|
||||
this.IsCritical = isCritical;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,25 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents an exception thrown by an <see cref="T:RGB.NET.Core.IRGBDevice" />.
|
||||
/// </summary>
|
||||
public class RGBDeviceException : ApplicationException
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents an exception thrown by an <see cref="T:RGB.NET.Core.IRGBDevice" />.
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.RGBDeviceException" /> class.
|
||||
/// </summary>
|
||||
public class RGBDeviceException : ApplicationException
|
||||
{
|
||||
#region Constructors
|
||||
/// <param name="message">The message which describes the reason of throwing this exception.</param>
|
||||
/// <param name="innerException">Optional inner exception, which lead to this exception.</param>
|
||||
public RGBDeviceException(string message, Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.RGBDeviceException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message which describes the reason of throwing this exception.</param>
|
||||
/// <param name="innerException">Optional inner exception, which lead to this exception.</param>
|
||||
public RGBDeviceException(string message, Exception innerException = null)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
24
RGB.NET.Core/Exceptions/RGBSurfaceException.cs
Normal file
24
RGB.NET.Core/Exceptions/RGBSurfaceException.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents an exception thrown by an <see cref="T:RGB.NET.Core.RGBSurface" />.
|
||||
/// </summary>
|
||||
public class RGBSurfaceException : ApplicationException
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.RGBSurfaceException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message which describes the reason of throwing this exception.</param>
|
||||
/// <param name="innerException">Optional inner exception, which lead to this exception.</param>
|
||||
public RGBSurfaceException(string message, Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,30 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for <see cref="Color"/> related things.
|
||||
/// </summary>
|
||||
public static class ColorExtensions
|
||||
{
|
||||
public static class ColorExtensions
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between the two specified colors using the redmean algorithm.
|
||||
/// For more infos check https://www.compuphase.com/cmetric.htm
|
||||
/// </summary>
|
||||
/// <param name="color1">The start color of the distance calculation.</param>
|
||||
/// <param name="color2">The end color fot the distance calculation.</param>
|
||||
/// <returns>The redmean distance between the two specified colors.</returns>
|
||||
public static double DistanceTo(this in Color color1, in Color color2)
|
||||
{
|
||||
#region Methods
|
||||
(_, byte r1, byte g1, byte b1) = color1.GetRGBBytes();
|
||||
(_, byte r2, byte g2, byte b2) = color2.GetRGBBytes();
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance between the two given colors using the redmean algorithm.
|
||||
/// For more infos check https://www.compuphase.com/cmetric.htm
|
||||
/// </summary>
|
||||
/// <param name="color1">The start color of the distance calculation.</param>
|
||||
/// <param name="color2">The end color fot the distance calculation.</param>
|
||||
/// <returns></returns>
|
||||
public static double DistanceTo(this Color color1, Color color2)
|
||||
{
|
||||
(_, byte r1, byte g1, byte b1) = color1.GetRGBBytes();
|
||||
(_, byte r2, byte g2, byte b2) = color2.GetRGBBytes();
|
||||
|
||||
long rmean = (r1 + r2) / 2;
|
||||
long r = r1 - r2;
|
||||
long g = g1 - g2;
|
||||
long b = b1 - b2;
|
||||
return Math.Sqrt((((512 + rmean) * r * r) >> 8) + (4 * g * g) + (((767 - rmean) * b * b) >> 8));
|
||||
}
|
||||
|
||||
#endregion
|
||||
long rmean = (r1 + r2) / 2;
|
||||
long r = r1 - r2;
|
||||
long g = g1 - g2;
|
||||
long b = b1 - b2;
|
||||
return Math.Sqrt((((512 + rmean) * r * r) >> 8) + (4 * g * g) + (((767 - rmean) * b * b) >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
55
RGB.NET.Core/Extensions/CustomUpdateDataExtension.cs
Normal file
55
RGB.NET.Core/Extensions/CustomUpdateDataExtension.cs
Normal file
@ -0,0 +1,55 @@
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions for easier use of <see cref="CustomUpdateData"/>.
|
||||
/// </summary>
|
||||
public static class CustomUpdateDataExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the <see cref="CustomUpdateDataIndex.FLUSH_LEDS"/>-Parameter to the given value.
|
||||
/// </summary>
|
||||
/// <param name="customUpdateData">The update-data to modify.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <returns>The modified update-data.</returns>
|
||||
public static CustomUpdateData FlushLeds(this CustomUpdateData customUpdateData, bool value = true)
|
||||
{
|
||||
customUpdateData[CustomUpdateDataIndex.FLUSH_LEDS] = value;
|
||||
return customUpdateData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="CustomUpdateDataIndex.RENDER"/>-Parameter to the given value.
|
||||
/// </summary>
|
||||
/// <param name="customUpdateData">The update-data to modify.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <returns>The modified update-data.</returns>
|
||||
public static CustomUpdateData Render(this CustomUpdateData customUpdateData, bool value = true)
|
||||
{
|
||||
customUpdateData[CustomUpdateDataIndex.RENDER] = value;
|
||||
return customUpdateData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="CustomUpdateDataIndex.UPDATE_DEVICES"/>-Parameter to the given value.
|
||||
/// </summary>
|
||||
/// <param name="customUpdateData">The update-data to modify.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <returns>The modified update-data.</returns>
|
||||
public static CustomUpdateData UpdateDevices(this CustomUpdateData customUpdateData, bool value = true)
|
||||
{
|
||||
customUpdateData[CustomUpdateDataIndex.UPDATE_DEVICES] = value;
|
||||
return customUpdateData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="CustomUpdateDataIndex.HEARTBEAT"/>-Parameter to the given value.
|
||||
/// </summary>
|
||||
/// <param name="customUpdateData">The update-data to modify.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <returns>The modified update-data.</returns>
|
||||
public static CustomUpdateData Heartbeat(this CustomUpdateData customUpdateData, bool value = true)
|
||||
{
|
||||
customUpdateData[CustomUpdateDataIndex.HEARTBEAT] = value;
|
||||
return customUpdateData;
|
||||
}
|
||||
}
|
||||
@ -1,101 +1,110 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for the work with floats.
|
||||
/// </summary>
|
||||
public static class FloatExtensions
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for the work with doubles
|
||||
/// Defines the precision RGB.NET processes floating point comparisons in.
|
||||
/// </summary>
|
||||
public static class DoubleExtensions
|
||||
public const float TOLERANCE = 1E-7f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two values are equal respecting the <see cref="TOLERANCE"/>.
|
||||
/// </summary>
|
||||
/// <param name="value1">The first value to compare.</param>
|
||||
/// <param name="value2">The first value to compare.</param>
|
||||
/// <returns><c>true</c> if the difference is smaller than the <see cref="TOLERANCE"/>; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsInTolerance(this float value1, float value2) => Math.Abs(value1 - value2) < TOLERANCE;
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The lower value of the range the value is clamped to.</param>
|
||||
/// <param name="max">The higher value of the range the value is clamped to.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Clamp(this float value, float min, float max)
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Defines the precision RGB.NET processes floating point comparisons in.
|
||||
/// </summary>
|
||||
public const double TOLERANCE = 1E-10;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two values are equal respecting the <see cref="TOLERANCE"/>.
|
||||
/// </summary>
|
||||
/// <param name="value1">The first value to compare.</param>
|
||||
/// <param name="value2">The first value to compare.</param>
|
||||
/// <returns><c>true</c> if the difference is smaller than the <see cref="TOLERANCE"/>; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsInTolerance(this double value1, double value2) => Math.Abs(value1 - value2) < TOLERANCE;
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The lower value of the range the value is clamped to.</param>
|
||||
/// <param name="max">The higher value of the range the value is clamped to.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Clamp(this double value, double min, double max)
|
||||
{
|
||||
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
// ReSharper restore ConvertIfStatementToReturnStatement
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The lower value of the range the value is clamped to.</param>
|
||||
/// <param name="max">The higher value of the range the value is clamped to.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Clamp(this int value, int min, int max)
|
||||
{
|
||||
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
// ReSharper restore ConvertIfStatementToReturnStatement
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enforces the provided value to be in the specified range by wrapping it around the edges if it exceeds them.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to wrap.</param>
|
||||
/// <param name="min">The lower value of the range the value is wrapped into.</param>
|
||||
/// <param name="max">The higher value of the range the value is wrapped into.</param>
|
||||
/// <returns>The wrapped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Wrap(this double value, double min, double max)
|
||||
{
|
||||
double range = max - min;
|
||||
|
||||
while (value >= max)
|
||||
value -= range;
|
||||
|
||||
while (value < min)
|
||||
value += range;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static byte GetByteValueFromPercentage(this double percentage)
|
||||
{
|
||||
if (double.IsNaN(percentage)) return 0;
|
||||
|
||||
percentage = percentage.Clamp(0, 1.0);
|
||||
return (byte)(percentage >= 1.0 ? 255 : percentage * 256.0);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double GetPercentageFromByteValue(this byte value)
|
||||
=> ((double)value) / byte.MaxValue;
|
||||
|
||||
#endregion
|
||||
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
// ReSharper restore ConvertIfStatementToReturnStatement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The lower value of the range the value is clamped to.</param>
|
||||
/// <param name="max">The higher value of the range the value is clamped to.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Clamp(this int value, int min, int max)
|
||||
{
|
||||
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
// ReSharper restore ConvertIfStatementToReturnStatement
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enforces the provided value to be in the specified range by wrapping it around the edges if it exceeds them.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to wrap.</param>
|
||||
/// <param name="min">The lower value of the range the value is wrapped into.</param>
|
||||
/// <param name="max">The higher value of the range the value is wrapped into.</param>
|
||||
/// <returns>The wrapped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Wrap(this float value, float min, float max)
|
||||
{
|
||||
float range = max - min;
|
||||
|
||||
while (value >= max)
|
||||
value -= range;
|
||||
|
||||
while (value < min)
|
||||
value += range;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a normalized float value in the range [0..1] to a byte [0..255].
|
||||
/// </summary>
|
||||
/// <param name="percentage">The normalized float value to convert.</param>
|
||||
/// <returns>The byte value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static byte GetByteValueFromPercentage(this float percentage)
|
||||
{
|
||||
if (float.IsNaN(percentage)) return 0;
|
||||
|
||||
percentage = percentage.Clamp(0, 1.0f);
|
||||
return (byte)(percentage >= 1.0f ? 255 : percentage * 256.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte value [0..255] to a normalized float value in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="value">The byte value to convert.</param>
|
||||
/// <returns>The normalized float value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float GetPercentageFromByteValue(this byte value)
|
||||
=> value == 255 ? 1.0f : (value / 256.0f);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,37 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for <see cref="Point"/> related things.
|
||||
/// </summary>
|
||||
public static class PointExtensions
|
||||
{
|
||||
public static class PointExtensions
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Moves the specified <see cref="Point"/> by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> to move.</param>
|
||||
/// <param name="x">The x-ammount to move.</param>
|
||||
/// <param name="y">The y-ammount to move.</param>
|
||||
/// <returns>The new location of the point.</returns>
|
||||
public static Point Translate(this in Point point, float x = 0, float y = 0) => new(point.X + x, point.Y + y);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the specified <see cref="Point"/> by the specified amuont around the specified origin.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> to rotate.</param>
|
||||
/// <param name="rotation">The rotation.</param>
|
||||
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
|
||||
/// <returns>The new location of the point.</returns>
|
||||
public static Point Rotate(this in Point point, in Rotation rotation, in Point origin = new())
|
||||
{
|
||||
#region Methods
|
||||
float sin = MathF.Sin(rotation.Radians);
|
||||
float cos = MathF.Cos(rotation.Radians);
|
||||
|
||||
/// <summary>
|
||||
/// Moves the specified <see cref="Point"/> by the given amount.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> to move.</param>
|
||||
/// <param name="x">The x-ammount to move.</param>
|
||||
/// <param name="y">The y-ammount to move.</param>
|
||||
/// <returns>The new location of the point.</returns>
|
||||
public static Point Translate(this Point point, double x = 0, double y = 0) => new Point(point.X + x, point.Y + y);
|
||||
float x = point.X - origin.X;
|
||||
float y = point.Y - origin.Y;
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the specified <see cref="Point"/> by the given amuont around the given origin.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> to rotate.</param>
|
||||
/// <param name="rotation">The rotation.</param>
|
||||
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
|
||||
/// <returns>The new location of the point.</returns>
|
||||
public static Point Rotate(this Point point, Rotation rotation, Point origin = new Point())
|
||||
{
|
||||
double sin = Math.Sin(rotation.Radians);
|
||||
double cos = Math.Cos(rotation.Radians);
|
||||
x = (x * cos) - (y * sin);
|
||||
y = (x * sin) + (y * cos);
|
||||
|
||||
point = new Point(point.X - origin.X, point.Y - origin.Y);
|
||||
point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos));
|
||||
return new Point(point.X + origin.X, point.Y + origin.Y); ;
|
||||
}
|
||||
|
||||
#endregion
|
||||
return new Point(x + origin.X, y + origin.Y);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,169 +1,176 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for the work with rectangles.
|
||||
/// </summary>
|
||||
public static class RectangleExtensions
|
||||
{
|
||||
public static class RectangleExtensions
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Rectangle.Location"/> of the specified rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="location">The new location of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetLocation(this in Rectangle rect, in Point location) => new(location, rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Point.X"/> of the <see cref="Rectangle.Location"/> of the specified rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="x">The new x-location of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetX(this in Rectangle rect, float x) => new(new Point(x, rect.Location.Y), rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Point.Y"/> of the <see cref="Rectangle.Location"/> of the specified rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="y">The new y-location of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetY(this in Rectangle rect, float y) => new(new Point(rect.Location.X, y), rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Rectangle.Size"/> of the specified rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="size">The new size of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetSize(this in Rectangle rect, in Size size) => new(rect.Location, size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Size.Width"/> of the <see cref="Rectangle.Size"/> of the specified rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="width">The new width of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetWidth(this in Rectangle rect, float width) => new(rect.Location, new Size(width, rect.Size.Height));
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Size.Height"/> of the <see cref="Rectangle.Size"/> of the specified rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="height">The new height of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetHeight(this in Rectangle rect, float height) => new(rect.Location, new Size(rect.Size.Width, height));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the percentage of intersection of a rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to calculate the intersection for.</param>
|
||||
/// <param name="intersectingRect">The intersecting rectangle.</param>
|
||||
/// <returns>The percentage of intersection.</returns>
|
||||
public static float CalculateIntersectPercentage(this in Rectangle rect, in Rectangle intersectingRect)
|
||||
{
|
||||
#region Methods
|
||||
if (rect.IsEmpty || intersectingRect.IsEmpty) return 0;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Rectangle.Location"/> of the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="location">The new location of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetLocation(this Rectangle rect, Point location) => new Rectangle(location, rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Point.X"/> of the <see cref="Rectangle.Location"/> of the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="x">The new x-location of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetX(this Rectangle rect, double x) => new Rectangle(new Point(x, rect.Location.Y), rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Point.Y"/> of the <see cref="Rectangle.Location"/> of the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="y">The new y-location of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetY(this Rectangle rect, double y) => new Rectangle(new Point(rect.Location.X, y), rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Rectangle.Size"/> of the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="size">The new size of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetSize(this Rectangle rect, Size size) => new Rectangle(rect.Location, size);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Size.Width"/> of the <see cref="Rectangle.Size"/> of the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="width">The new width of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetWidth(this Rectangle rect, double width) => new Rectangle(rect.Location, new Size(width, rect.Size.Height));
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Size.Height"/> of the <see cref="Rectangle.Size"/> of the given rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to modify.</param>
|
||||
/// <param name="height">The new height of the rectangle.</param>
|
||||
/// <returns>The modified <see cref="Rectangle"/>.</returns>
|
||||
public static Rectangle SetHeight(this Rectangle rect, double height) => new Rectangle(rect.Location, new Size(rect.Size.Width, height));
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the percentage of intersection of a rectangle.
|
||||
/// </summary>
|
||||
/// <param name="intersectingRect">The intersecting rectangle.</param>
|
||||
/// <returns>The percentage of intersection.</returns>
|
||||
public static double CalculateIntersectPercentage(this Rectangle rect, Rectangle intersectingRect)
|
||||
{
|
||||
if (rect.IsEmpty || intersectingRect.IsEmpty) return 0;
|
||||
|
||||
Rectangle intersection = rect.CalculateIntersection(intersectingRect);
|
||||
return (intersection.Size.Width * intersection.Size.Height) / (rect.Size.Width * rect.Size.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="Rectangle"/> representing the intersection of this <see cref="Rectangle"/> and the one provided as parameter.
|
||||
/// </summary>
|
||||
/// <param name="intersectingRectangle">The intersecting <see cref="Rectangle"/></param>
|
||||
/// <returns>A new <see cref="Rectangle"/> representing the intersection this <see cref="Rectangle"/> and the one provided as parameter.</returns>
|
||||
public static Rectangle CalculateIntersection(this Rectangle rect, Rectangle intersectingRectangle)
|
||||
{
|
||||
double x1 = Math.Max(rect.Location.X, intersectingRectangle.Location.X);
|
||||
double x2 = Math.Min(rect.Location.X + rect.Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width);
|
||||
|
||||
double y1 = Math.Max(rect.Location.Y, intersectingRectangle.Location.Y);
|
||||
double y2 = Math.Min(rect.Location.Y + rect.Size.Height, intersectingRectangle.Location.Y + intersectingRectangle.Size.Height);
|
||||
|
||||
if ((x2 >= x1) && (y2 >= y1))
|
||||
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
|
||||
|
||||
return new Rectangle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <see cref="Point"/> is contained within this <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> to test.</param>
|
||||
/// <returns><c>true</c> if the rectangle contains the given point; otherwise <c>false</c>.</returns>
|
||||
public static bool Contains(this Rectangle rect, Point point) => rect.Contains(point.X, point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified location is contained within this <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The X-location to test.</param>
|
||||
/// <param name="y">The Y-location to test.</param>
|
||||
/// <returns><c>true</c> if the rectangle contains the given coordinates; otherwise <c>false</c>.</returns>
|
||||
public static bool Contains(this Rectangle rect, double x, double y) => (rect.Location.X <= x) && (x < (rect.Location.X + rect.Size.Width))
|
||||
&& (rect.Location.Y <= y) && (y < (rect.Location.Y + rect.Size.Height));
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <see cref="Rectangle"/> is contained within this <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="rect">The <see cref="Rectangle"/> to test.</param>
|
||||
/// <returns><c>true</c> if the rectangle contains the given rect; otherwise <c>false</c>.</returns>
|
||||
public static bool Contains(this Rectangle rect, Rectangle rect2) => (rect.Location.X <= rect2.Location.X) && ((rect2.Location.X + rect2.Size.Width) <= (rect.Location.X + rect.Size.Width))
|
||||
&& (rect.Location.Y <= rect2.Location.Y) && ((rect2.Location.Y + rect2.Size.Height) <= (rect.Location.Y + rect.Size.Height));
|
||||
|
||||
/// <summary>
|
||||
/// Moves the specified <see cref="Rectangle"/> by the given amount.
|
||||
/// </summary>
|
||||
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
|
||||
/// <param name="point">The amount to move.</param>
|
||||
/// <returns>The moved rectangle.</returns>
|
||||
public static Rectangle Translate(this Rectangle rect, Point point) => rect.Translate(point.X, point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Moves the specified <see cref="Rectangle"/> by the given amount.
|
||||
/// </summary>
|
||||
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
|
||||
/// <param name="x">The x-ammount to move.</param>
|
||||
/// <param name="y">The y-ammount to move.</param>
|
||||
/// <returns>The moved rectangle.</returns>
|
||||
public static Rectangle Translate(this Rectangle rect, double x = 0, double y = 0) => new Rectangle(rect.Location.Translate(x, y), rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the specified <see cref="Rectangle"/> by the given amuont around the given origin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned array of <see cref="Point"/> is filled with the new locations of the rectangle clockwise starting from the top left:
|
||||
/// [0] = top left
|
||||
/// [1] = top right
|
||||
/// [2] = bottom right
|
||||
/// [3] = bottom left
|
||||
/// </remarks>
|
||||
/// <param name="rect">The <see cref="Rectangle"/> to rotate.</param>
|
||||
/// <param name="rotation">The rotation.</param>
|
||||
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
|
||||
/// <returns>A array of <see cref="Point"/> containing the new locations of the corners of the original rectangle.</returns>
|
||||
public static Point[] Rotate(this Rectangle rect, Rotation rotation, Point origin = new Point())
|
||||
{
|
||||
Point[] points = {
|
||||
rect.Location, // top left
|
||||
new Point(rect.Location.X + rect.Size.Width, rect.Location.Y), // top right
|
||||
new Point(rect.Location.X + rect.Size.Width, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
new Point(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
};
|
||||
|
||||
double sin = Math.Sin(rotation.Radians);
|
||||
double cos = Math.Cos(rotation.Radians);
|
||||
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
Point point = points[i];
|
||||
point = new Point(point.X - origin.X, point.Y - origin.Y);
|
||||
point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos));
|
||||
points[i] = new Point(point.X + origin.X, point.Y + origin.Y);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
#endregion
|
||||
Rectangle intersection = rect.CalculateIntersection(intersectingRect);
|
||||
return (intersection.Size.Width * intersection.Size.Height) / (rect.Size.Width * rect.Size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the <see cref="Rectangle"/> representing the intersection of this <see cref="Rectangle"/> and the one provided as parameter.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle to calculate the intersection for.</param>
|
||||
/// <param name="intersectingRectangle">The intersecting <see cref="Rectangle"/>.</param>
|
||||
/// <returns>A new <see cref="Rectangle"/> representing the intersection this <see cref="Rectangle"/> and the one provided as parameter.</returns>
|
||||
public static Rectangle CalculateIntersection(this in Rectangle rect, in Rectangle intersectingRectangle)
|
||||
{
|
||||
float x1 = Math.Max(rect.Location.X, intersectingRectangle.Location.X);
|
||||
float x2 = Math.Min(rect.Location.X + rect.Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width);
|
||||
|
||||
float y1 = Math.Max(rect.Location.Y, intersectingRectangle.Location.Y);
|
||||
float y2 = Math.Min(rect.Location.Y + rect.Size.Height, intersectingRectangle.Location.Y + intersectingRectangle.Size.Height);
|
||||
|
||||
if ((x2 >= x1) && (y2 >= y1))
|
||||
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
|
||||
|
||||
return new Rectangle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <see cref="Point"/> is contained within this <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="rect">The containing rectangle.</param>
|
||||
/// <param name="point">The <see cref="Point"/> to test.</param>
|
||||
/// <returns><c>true</c> if the rectangle contains the specified point; otherwise <c>false</c>.</returns>
|
||||
public static bool Contains(this in Rectangle rect, in Point point) => rect.Contains(point.X, point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified location is contained within this <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="rect">The containing rectangle.</param>
|
||||
/// <param name="x">The X-location to test.</param>
|
||||
/// <param name="y">The Y-location to test.</param>
|
||||
/// <returns><c>true</c> if the rectangle contains the specified coordinates; otherwise <c>false</c>.</returns>
|
||||
public static bool Contains(this in Rectangle rect, float x, float y) => (rect.Location.X <= x) && (x < (rect.Location.X + rect.Size.Width))
|
||||
&& (rect.Location.Y <= y) && (y < (rect.Location.Y + rect.Size.Height));
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <see cref="Rectangle"/> is contained within this <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
/// <param name="rect">The containing rectangle.</param>
|
||||
/// <param name="rect2">The <see cref="Rectangle"/> to test.</param>
|
||||
/// <returns><c>true</c> if the rectangle contains the specified rect; otherwise <c>false</c>.</returns>
|
||||
public static bool Contains(this in Rectangle rect, in Rectangle rect2) => (rect.Location.X <= rect2.Location.X) && ((rect2.Location.X + rect2.Size.Width) <= (rect.Location.X + rect.Size.Width))
|
||||
&& (rect.Location.Y <= rect2.Location.Y) && ((rect2.Location.Y + rect2.Size.Height) <= (rect.Location.Y + rect.Size.Height));
|
||||
|
||||
/// <summary>
|
||||
/// Moves the specified <see cref="Rectangle"/> by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
|
||||
/// <param name="point">The amount to move.</param>
|
||||
/// <returns>The moved rectangle.</returns>
|
||||
public static Rectangle Translate(this in Rectangle rect, in Point point) => rect.Translate(point.X, point.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Moves the specified <see cref="Rectangle"/> by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
|
||||
/// <param name="x">The x-ammount to move.</param>
|
||||
/// <param name="y">The y-ammount to move.</param>
|
||||
/// <returns>The moved rectangle.</returns>
|
||||
public static Rectangle Translate(this in Rectangle rect, float x = 0, float y = 0) => new(rect.Location.Translate(x, y), rect.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the specified <see cref="Rectangle"/> by the specified amuont around the specified origin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned array of <see cref="Point"/> is filled with the new locations of the rectangle clockwise starting from the top left:
|
||||
/// [0] = top left
|
||||
/// [1] = top right
|
||||
/// [2] = bottom right
|
||||
/// [3] = bottom left
|
||||
/// </remarks>
|
||||
/// <param name="rect">The <see cref="Rectangle"/> to rotate.</param>
|
||||
/// <param name="rotation">The rotation.</param>
|
||||
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
|
||||
/// <returns>A array of <see cref="Point"/> containing the new locations of the corners of the original rectangle.</returns>
|
||||
public static Point[] Rotate(this in Rectangle rect, in Rotation rotation, in Point origin = new())
|
||||
{
|
||||
Point[] points = {
|
||||
rect.Location, // top left
|
||||
new(rect.Location.X + rect.Size.Width, rect.Location.Y), // top right
|
||||
new(rect.Location.X + rect.Size.Width, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
new(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right
|
||||
};
|
||||
|
||||
float sin = MathF.Sin(rotation.Radians);
|
||||
float cos = MathF.Cos(rotation.Radians);
|
||||
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
Point point = points[i];
|
||||
point = new Point(point.X - origin.X, point.Y - origin.Y);
|
||||
point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos));
|
||||
points[i] = new Point(point.X + origin.X, point.Y + origin.Y);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
84
RGB.NET.Core/Extensions/SurfaceExtensions.cs
Normal file
84
RGB.NET.Core/Extensions/SurfaceExtensions.cs
Normal file
@ -0,0 +1,84 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for the work with the surface.
|
||||
/// </summary>
|
||||
public static class SurfaceExtensions
|
||||
{
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the specifiec device provider and attaches all devices.
|
||||
/// </summary>
|
||||
/// <param name="surface">The surface to attach the devices to.</param>
|
||||
/// <param name="deviceProvider">The device provider to load.</param>
|
||||
/// <param name="loadFilter"><see cref="RGBDeviceType"/>-flags to filter the devices to load.</param>
|
||||
/// <param name="throwExceptions">Specifies if exceptions should be thrown or silently be ignored.</param>
|
||||
public static void Load(this RGBSurface surface, IRGBDeviceProvider deviceProvider, RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
|
||||
{
|
||||
if (!deviceProvider.IsInitialized)
|
||||
deviceProvider.Initialize(loadFilter, throwExceptions);
|
||||
|
||||
surface.Attach(deviceProvider.Devices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches the specified devices to the surface.
|
||||
/// </summary>
|
||||
/// <param name="surface">The surface the devices are attached to.</param>
|
||||
/// <param name="devices">The devices to attach.</param>
|
||||
public static void Attach(this RGBSurface surface, IEnumerable<IRGBDevice> devices)
|
||||
{
|
||||
foreach (IRGBDevice device in devices)
|
||||
surface.Attach(device);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches the specified devices from the surface.
|
||||
/// </summary>
|
||||
/// <param name="surface">The surface the devices are detached from.</param>
|
||||
/// <param name="devices">The devices to detach.</param>
|
||||
public static void Detach(this RGBSurface surface, IEnumerable<IRGBDevice> devices)
|
||||
{
|
||||
foreach (IRGBDevice device in devices)
|
||||
surface.Detach(device);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all devices of a specific type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of devices to get.</typeparam>
|
||||
/// <returns>A collection of devices with the specified type.</returns>
|
||||
public static IEnumerable<T> GetDevices<T>(this RGBSurface surface)
|
||||
where T : class
|
||||
=> surface.Devices.Where(x => x is T).Cast<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all devices of the specified <see cref="RGBDeviceType"/>.
|
||||
/// </summary>
|
||||
/// <param name="surface">The surface to get the devices from.</param>
|
||||
/// <param name="deviceType">The <see cref="RGBDeviceType"/> of the devices to get.</param>
|
||||
/// <returns>A collection of devices matching the specified <see cref="RGBDeviceType"/>.</returns>
|
||||
public static IEnumerable<IRGBDevice> GetDevices(this RGBSurface surface, RGBDeviceType deviceType)
|
||||
=> surface.Devices.Where(d => deviceType.HasFlag(d.DeviceInfo.DeviceType));
|
||||
|
||||
/// <summary>
|
||||
/// Automatically aligns all devices to prevent overlaps.
|
||||
/// </summary>
|
||||
public static void AlignDevices(this RGBSurface surface)
|
||||
{
|
||||
float posX = 0;
|
||||
foreach (IRGBDevice device in surface.Devices)
|
||||
{
|
||||
device.Location += new Point(posX, 0);
|
||||
posX += device.ActualSize.Width + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,51 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc cref="AbstractDecoratable{T}" />
|
||||
/// <inheritdoc cref="ILedGroup" />
|
||||
/// <summary>
|
||||
/// Represents a generic <see cref="T:RGB.NET.Core.AbstractLedGroup" />.
|
||||
/// </summary>
|
||||
public abstract class AbstractLedGroup : AbstractDecoratable<ILedGroupDecorator>, ILedGroup
|
||||
{
|
||||
/// <inheritdoc cref="AbstractDecoratable{T}" />
|
||||
/// <inheritdoc cref="ILedGroup" />
|
||||
#region Properties & Fields
|
||||
|
||||
RGBSurface? ILedGroup.Surface { get; set; }
|
||||
|
||||
/// <inheritdoc cref="ILedGroup.Surface" />
|
||||
public RGBSurface? Surface => ((ILedGroup)this).Surface;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBrush? Brush { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ZIndex { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic <see cref="T:RGB.NET.Core.AbstractLedGroup" />.
|
||||
/// Initializes a new instance of the <see cref="AbstractLedGroup"/> class.
|
||||
/// </summary>
|
||||
public abstract class AbstractLedGroup : AbstractDecoratable<ILedGroupDecorator>, ILedGroup
|
||||
protected AbstractLedGroup(RGBSurface? attachTo)
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBrush Brush { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ZIndex { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractLedGroup"/> class.
|
||||
/// </summary>
|
||||
/// <param name="autoAttach">Specifies whether this <see cref="AbstractLedGroup"/> should be automatically attached or not.</param>
|
||||
protected AbstractLedGroup(bool autoAttach)
|
||||
{
|
||||
if (autoAttach)
|
||||
RGBSurface.Instance.AttachLedGroup(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IList<Led> GetLeds();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnAttach()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnDetach()
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
attachTo?.Attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets a enumerable containing all leds in this group.
|
||||
/// </summary>
|
||||
/// <returns>A enumerable containing all leds of this group.</returns>
|
||||
protected abstract IEnumerable<Led> GetLeds();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnAttach() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void OnDetach() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<Led> GetEnumerator() => GetLeds().GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -3,38 +3,40 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic ledgroup.
|
||||
/// </summary>
|
||||
public interface ILedGroup : IDecoratable<ILedGroupDecorator>, IEnumerable<Led>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a generic ledgroup.
|
||||
/// Gets the surface this group is attached to or <c>null</c> if it is not attached to any surface.
|
||||
/// </summary>
|
||||
public interface ILedGroup : IDecoratable<ILedGroupDecorator>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
|
||||
/// </summary>
|
||||
IBrush Brush { get; set; }
|
||||
RGBSurface? Surface { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the z-index of this <see cref="ILedGroup"/> to allow ordering them before drawing. (lowest first) (default: 0)
|
||||
/// </summary>
|
||||
int ZIndex { get; set; }
|
||||
/// <summary>
|
||||
/// Gets a bool indicating if the group is attached to a surface.
|
||||
/// </summary>
|
||||
bool IsAttached => Surface != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.
|
||||
/// </summary>
|
||||
/// <returns>The list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.</returns>
|
||||
IList<Led> GetLeds();
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
|
||||
/// </summary>
|
||||
IBrush? Brush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ILedGroup"/> is attached to the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
void OnAttach();
|
||||
/// <summary>
|
||||
/// Gets or sets the z-index of this <see cref="ILedGroup"/> to allow ordering them before drawing. (lowest first) (default: 0)
|
||||
/// </summary>
|
||||
int ZIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ILedGroup"/> is detached from the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
void OnDetach();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ILedGroup"/> is attached to the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
void OnAttach();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ILedGroup"/> is detached from the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
void OnDetach();
|
||||
}
|
||||
57
RGB.NET.Core/Groups/LedGroupExtension.cs
Normal file
57
RGB.NET.Core/Groups/LedGroupExtension.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for <see cref="ILedGroup"/> related things.
|
||||
/// </summary>
|
||||
public static class LedGroupExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified <see cref="ILedGroup" /> to a <see cref="ListLedGroup" />.
|
||||
/// </summary>
|
||||
/// <param name="ledGroup">The <see cref="ILedGroup" /> to convert.</param>
|
||||
/// <returns>The converted <see cref="ListLedGroup" />.</returns>
|
||||
public static ListLedGroup ToListLedGroup(this ILedGroup ledGroup)
|
||||
{
|
||||
// ReSharper disable once InvertIf
|
||||
if (ledGroup is not ListLedGroup listLedGroup)
|
||||
{
|
||||
if (ledGroup.IsAttached)
|
||||
ledGroup.Detach();
|
||||
listLedGroup = new ListLedGroup(ledGroup.Surface, ledGroup) { Brush = ledGroup.Brush, ZIndex = ledGroup.ZIndex };
|
||||
}
|
||||
return listLedGroup;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="ListLedGroup" /> which contains all <see cref="Led"/> from the specified <see cref="ILedGroup"/> excluding the specified ones.
|
||||
/// </summary>
|
||||
/// <param name="ledGroup">The base <see cref="ILedGroup"/>.</param>
|
||||
/// <param name="ledIds">The <see cref="Led"/> to exclude.</param>
|
||||
/// <returns>The new <see cref="ListLedGroup" />.</returns>
|
||||
public static ListLedGroup Exclude(this ILedGroup ledGroup, params Led[] ledIds)
|
||||
{
|
||||
ListLedGroup listLedGroup = ledGroup.ToListLedGroup();
|
||||
foreach (Led led in ledIds)
|
||||
listLedGroup.RemoveLed(led);
|
||||
return listLedGroup;
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||
/// <summary>
|
||||
/// Attaches the specified <see cref="ILedGroup"/> to the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
|
||||
/// <param name="surface">The <see cref="RGBSurface"/> to attach this group to.</param>
|
||||
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be attached; otherwise, <c>false</c>.</returns>
|
||||
public static bool Attach(this ILedGroup ledGroup, RGBSurface surface) => surface.Attach(ledGroup);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches the specified <see cref="ILedGroup"/> from the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
|
||||
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be detached; otherwise, <c>false</c>.</returns>
|
||||
public static bool Detach(this ILedGroup ledGroup) => ledGroup.Surface?.Detach(ledGroup) ?? false;
|
||||
}
|
||||
132
RGB.NET.Core/Groups/ListLedGroup.cs
Normal file
132
RGB.NET.Core/Groups/ListLedGroup.cs
Normal file
@ -0,0 +1,132 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a ledgroup containing arbitrary <see cref="T:RGB.NET.Core.Led" />.
|
||||
/// </summary>
|
||||
public class ListLedGroup : AbstractLedGroup
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list containing the <see cref="Led"/> of this <see cref="ListLedGroup"/>.
|
||||
/// </summary>
|
||||
protected IList<Led> GroupLeds { get; } = new List<Led>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.ListLedGroup" /> class.
|
||||
/// </summary>
|
||||
/// <param name="surface">Specifies the surface to attach this group to or <c>null</c> if the group should not be attached on creation.</param>
|
||||
public ListLedGroup(RGBSurface? surface)
|
||||
: base(surface)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.ListLedGroup" /> class.
|
||||
/// </summary>
|
||||
/// <param name="surface">Specifies the surface to attach this group to or <c>null</c> if the group should not be attached on creation.</param>
|
||||
/// <param name="leds">The initial <see cref="T:RGB.NET.Core.Led" /> of this <see cref="T:RGB.NET.Groups.ListLedGroup" />.</param>
|
||||
public ListLedGroup(RGBSurface? surface, IEnumerable<Led> leds)
|
||||
: base(surface)
|
||||
{
|
||||
AddLeds(leds);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.ListLedGroup" /> class.
|
||||
/// </summary>
|
||||
/// <param name="surface">Specifies the surface to attach this group to or <c>null</c> if the group should not be attached on creation.</param>
|
||||
/// <param name="leds">The initial <see cref="T:RGB.NET.Core.Led" /> of this <see cref="T:RGB.NET.Groups.ListLedGroup" />.</param>
|
||||
public ListLedGroup(RGBSurface? surface, params Led[] leds)
|
||||
: base(surface)
|
||||
{
|
||||
AddLeds(leds);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified LED(s) to this <see cref="ListLedGroup"/>.
|
||||
/// </summary>
|
||||
/// <param name="leds">The LED(s) to add.</param>
|
||||
public void AddLed(params Led[] leds) => AddLeds(leds);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified <see cref="Led"/> to this <see cref="ListLedGroup"/>.
|
||||
/// </summary>
|
||||
/// <param name="leds">The <see cref="Led"/> to add.</param>
|
||||
public void AddLeds(IEnumerable<Led> leds)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
foreach (Led led in leds)
|
||||
if (!ContainsLed(led))
|
||||
GroupLeds.Add(led);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified LED(s) from this <see cref="ListLedGroup"/>.
|
||||
/// </summary>
|
||||
/// <param name="leds">The LED(s) to remove.</param>
|
||||
public void RemoveLed(params Led[] leds) => RemoveLeds(leds);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified <see cref="Led"/> from this <see cref="ListLedGroup"/>.
|
||||
/// </summary>
|
||||
/// <param name="leds">The <see cref="Led"/> to remove.</param>
|
||||
public void RemoveLeds(IEnumerable<Led> leds)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
foreach (Led led in leds)
|
||||
GroupLeds.Remove(led);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a specified LED is contained by this ledgroup.
|
||||
/// </summary>
|
||||
/// <param name="led">The LED which should be checked.</param>
|
||||
/// <returns><c>true</c> if the LED is contained by this ledgroup; otherwise, <c>false</c>.</returns>
|
||||
public bool ContainsLed(Led led)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
return GroupLeds.Contains(led);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the <see cref="Led"/> from the specified ledgroup in this ledgroup.
|
||||
/// </summary>
|
||||
/// <param name="groupToMerge">The ledgroup to merge.</param>
|
||||
public void MergeLeds(ILedGroup groupToMerge)
|
||||
{
|
||||
lock (GroupLeds)
|
||||
foreach (Led led in groupToMerge)
|
||||
if (!GroupLeds.Contains(led))
|
||||
GroupLeds.Add(led);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Gets a list containing the <see cref="T:RGB.NET.Core.Led" /> from this group.
|
||||
/// </summary>
|
||||
/// <returns>The list containing the <see cref="T:RGB.NET.Core.Led" />.</returns>
|
||||
protected override IEnumerable<Led> GetLeds()
|
||||
{
|
||||
lock (GroupLeds)
|
||||
return new List<Led>(GroupLeds);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,60 +1,61 @@
|
||||
namespace RGB.NET.Core
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper methods for converting things.
|
||||
/// </summary>
|
||||
public static class ConversionHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
|
||||
/// <summary>
|
||||
/// Contains helper methods for converting things.
|
||||
/// Converts an array of bytes to a HEX-representation.
|
||||
/// </summary>
|
||||
public static class ConversionHelper
|
||||
/// <param name="bytes">The array of bytes.</param>
|
||||
/// <returns>The HEX-representation of the provided bytes.</returns>
|
||||
public static string ToHex(params byte[] bytes)
|
||||
{
|
||||
#region Methods
|
||||
char[] c = new char[bytes.Length * 2];
|
||||
|
||||
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
|
||||
/// <summary>
|
||||
/// Converts an array of bytes to a HEX-representation.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The array of bytes.</param>
|
||||
/// <returns>The HEX-representation of the provided bytes.</returns>
|
||||
public static string ToHex(params byte[] bytes)
|
||||
for (int bx = 0, cx = 0; bx < bytes.Length; ++bx, ++cx)
|
||||
{
|
||||
char[] c = new char[bytes.Length * 2];
|
||||
byte b = ((byte)(bytes[bx] >> 4));
|
||||
c[cx] = (char)(b > 9 ? b + 0x37 : b + 0x30);
|
||||
|
||||
for (int bx = 0, cx = 0; bx < bytes.Length; ++bx, ++cx)
|
||||
{
|
||||
byte b = ((byte)(bytes[bx] >> 4));
|
||||
c[cx] = (char)(b > 9 ? b + 0x37: b + 0x30);
|
||||
|
||||
b = ((byte)(bytes[bx] & 0x0F));
|
||||
c[++cx] = (char)(b > 9 ? b + 0x37: b + 0x30);
|
||||
}
|
||||
|
||||
return new string(c);
|
||||
b = ((byte)(bytes[bx] & 0x0F));
|
||||
c[++cx] = (char)(b > 9 ? b + 0x37 : b + 0x30);
|
||||
}
|
||||
|
||||
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
|
||||
/// <summary>
|
||||
/// Converts the HEX-representation of a byte array to that array.
|
||||
/// </summary>
|
||||
/// <param name="hexString">The HEX-string to convert.</param>
|
||||
/// <returns>The correspondending byte array.</returns>
|
||||
public static byte[] HexToBytes(string hexString)
|
||||
{
|
||||
if ((hexString.Length == 0) || ((hexString.Length % 2) != 0))
|
||||
return new byte[0];
|
||||
|
||||
byte[] buffer = new byte[hexString.Length / 2];
|
||||
for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
|
||||
{
|
||||
// Convert first half of byte
|
||||
char c = hexString[sx];
|
||||
buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0')) << 4);
|
||||
|
||||
// Convert second half of byte
|
||||
c = hexString[++sx];
|
||||
buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0'));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
return new string(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
|
||||
/// <summary>
|
||||
/// Converts the HEX-representation of a byte array to that array.
|
||||
/// </summary>
|
||||
/// <param name="hexString">The HEX-string to convert.</param>
|
||||
/// <returns>The correspondending byte array.</returns>
|
||||
public static byte[] HexToBytes(ReadOnlySpan<char> hexString)
|
||||
{
|
||||
if ((hexString.Length == 0) || ((hexString.Length % 2) != 0))
|
||||
return Array.Empty<byte>();
|
||||
|
||||
byte[] buffer = new byte[hexString.Length / 2];
|
||||
for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
|
||||
{
|
||||
// Convert first half of byte
|
||||
char c = hexString[sx];
|
||||
buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0')) << 4);
|
||||
|
||||
// Convert second half of byte
|
||||
c = hexString[++sx];
|
||||
buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0'));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Offers some helper-methods for culture related things.
|
||||
/// </summary>
|
||||
public static class CultureHelper
|
||||
{
|
||||
#region DLLImports
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetKeyboardLayout(uint thread);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current keyboard-layout from the OS.
|
||||
/// </summary>
|
||||
/// <returns>The current keyboard-layout</returns>
|
||||
public static CultureInfo GetCurrentCulture()
|
||||
{
|
||||
try
|
||||
{
|
||||
int keyboardLayout = GetKeyboardLayout(0).ToInt32() & 0xFFFF;
|
||||
return new CultureInfo(keyboardLayout);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new CultureInfo(1033); // en-US on error.
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
26
RGB.NET.Core/Helper/DeviceHelper.cs
Normal file
26
RGB.NET.Core/Helper/DeviceHelper.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offsers some helper methods for device creation.
|
||||
/// </summary>
|
||||
public static class DeviceHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a unique device name from a manufacturer and model name.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The id is made unique based on the assembly calling this method.
|
||||
/// </remarks>
|
||||
/// <param name="manufacturer">The manufacturer of the device.</param>
|
||||
/// <param name="model">The model of the device.</param>
|
||||
/// <returns>The unique identifier for this device.</returns>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static string CreateDeviceName(string manufacturer, string model) => IdGenerator.MakeUnique(Assembly.GetCallingAssembly(), $"{manufacturer} {model}");
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Offers some helper-methods for file-path related things.
|
||||
/// </summary>
|
||||
public static class PathHelper
|
||||
{
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a path is resolving.
|
||||
/// </summary>
|
||||
public static event EventHandler<ResolvePathEventArgs> ResolvingAbsolutePath;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The relative part of the path to convert.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public static string GetAbsolutePath(string relativePath) => GetAbsolutePath((object)null, relativePath);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The relative part of the path to convert.</param>
|
||||
/// <param name="fileName">The file name of the path to convert.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public static string GetAbsolutePath(string relativePath, string fileName) => GetAbsolutePath(null, relativePath, fileName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
|
||||
/// </summary>
|
||||
/// <param name="sender">The requester of this path. (Used for better control when using the event to override this behavior.)</param>
|
||||
/// <param name="relativePath">The relative path to convert.</param>
|
||||
/// <param name="fileName">The file name of the path to convert.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public static string GetAbsolutePath(object sender, string relativePath, string fileName)
|
||||
{
|
||||
string relativePart = Path.Combine(relativePath, fileName);
|
||||
|
||||
string assemblyLocation = Assembly.GetEntryAssembly()?.Location;
|
||||
if (assemblyLocation == null) return relativePart;
|
||||
|
||||
string directoryName = Path.GetDirectoryName(assemblyLocation);
|
||||
string path = directoryName == null ? null : Path.Combine(directoryName, relativePart);
|
||||
|
||||
ResolvePathEventArgs args = new ResolvePathEventArgs(relativePath, fileName, path);
|
||||
ResolvingAbsolutePath?.Invoke(sender, args);
|
||||
|
||||
return args.FinalPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
|
||||
/// </summary>
|
||||
/// <param name="sender">The requester of this path. (Used for better control when using the event to override this behavior.)</param>
|
||||
/// <param name="relativePath">The relative path to convert.</param>
|
||||
/// <returns>The absolute path.</returns>
|
||||
public static string GetAbsolutePath(object sender, string relativePath)
|
||||
{
|
||||
string assemblyLocation = Assembly.GetEntryAssembly()?.Location;
|
||||
if (assemblyLocation == null) return relativePath;
|
||||
|
||||
string directoryName = Path.GetDirectoryName(assemblyLocation);
|
||||
string path = directoryName == null ? null : Path.Combine(directoryName, relativePath);
|
||||
|
||||
ResolvePathEventArgs args = new ResolvePathEventArgs(relativePath, path);
|
||||
ResolvingAbsolutePath?.Invoke(sender, args);
|
||||
|
||||
return args.FinalPath;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
178
RGB.NET.Core/Helper/TimerHelper.cs
Normal file
178
RGB.NET.Core/Helper/TimerHelper.cs
Normal file
@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some helper methods for timed operations.
|
||||
/// </summary>
|
||||
public static class TimerHelper
|
||||
{
|
||||
#region DLL-Imports
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
|
||||
private static extern void TimeBeginPeriod(int t);
|
||||
|
||||
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
|
||||
private static extern void TimeEndPeriod(int t);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
private static readonly object HIGH_RESOLUTION_TIMER_LOCK = new();
|
||||
|
||||
private static bool _areHighResolutionTimersEnabled = false;
|
||||
|
||||
private static bool _useHighResolutionTimers = true;
|
||||
/// <summary>
|
||||
/// Gets or sets if High Resolution Timers should be used.
|
||||
/// </summary>
|
||||
public static bool UseHighResolutionTimers
|
||||
{
|
||||
get => _useHighResolutionTimers;
|
||||
set
|
||||
{
|
||||
lock (HIGH_RESOLUTION_TIMER_LOCK)
|
||||
{
|
||||
_useHighResolutionTimers = value;
|
||||
CheckHighResolutionTimerUsage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static readonly HashSet<HighResolutionTimerDisposable> _timerLeases = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Executes the provided action and blocks if needed until the the <see param="targetExecuteTime"/> has passed.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to execute.</param>
|
||||
/// <param name="targetExecuteTime">The time in ms this method should block. default: 0</param>
|
||||
/// <returns>The time in ms spent executing the <see param="action"/>.</returns>
|
||||
public static double Execute(Action action, double targetExecuteTime = 0)
|
||||
{
|
||||
long preUpdateTicks = Stopwatch.GetTimestamp();
|
||||
|
||||
action();
|
||||
|
||||
double updateTime = GetElapsedTime(preUpdateTicks);
|
||||
|
||||
if (targetExecuteTime > 0)
|
||||
{
|
||||
int sleep = (int)(targetExecuteTime - updateTime);
|
||||
if (sleep > 0)
|
||||
Thread.Sleep(sleep);
|
||||
}
|
||||
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the elapsed time in ms from the provided timestamp until now.
|
||||
/// </summary>
|
||||
/// <param name="initialTimestamp">The initial timestamp to calculate the time from.</param>
|
||||
/// <returns>The elapsed time in ms.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double GetElapsedTime(long initialTimestamp) => ((Stopwatch.GetTimestamp() - initialTimestamp) / (Stopwatch.Frequency / 1000.0));
|
||||
|
||||
/// <summary>
|
||||
/// Requests to use to use High Resolution Timers if enabled.
|
||||
/// IMPORTANT: Always dispose the returned disposable if High Resolution Timers are no longer needed for the caller.
|
||||
/// </summary>
|
||||
/// <returns>A disposable to remove the request.</returns>
|
||||
public static IDisposable RequestHighResolutionTimer()
|
||||
{
|
||||
HighResolutionTimerDisposable timerLease = new();
|
||||
lock (HIGH_RESOLUTION_TIMER_LOCK)
|
||||
{
|
||||
_timerLeases.Add(timerLease);
|
||||
CheckHighResolutionTimerUsage();
|
||||
}
|
||||
|
||||
return timerLease;
|
||||
}
|
||||
|
||||
private static void CheckHighResolutionTimerUsage()
|
||||
{
|
||||
if (UseHighResolutionTimers && (_timerLeases.Count > 0))
|
||||
EnableHighResolutionTimers();
|
||||
else
|
||||
DisableHighResolutionTimers();
|
||||
}
|
||||
|
||||
private static void EnableHighResolutionTimers()
|
||||
{
|
||||
lock (HIGH_RESOLUTION_TIMER_LOCK)
|
||||
{
|
||||
if (_areHighResolutionTimersEnabled) return;
|
||||
|
||||
// DarthAffe 06.05.2022: Linux should use 1ms timers by default
|
||||
if (OperatingSystem.IsWindows())
|
||||
TimeBeginPeriod(1);
|
||||
|
||||
_areHighResolutionTimersEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisableHighResolutionTimers()
|
||||
{
|
||||
lock (HIGH_RESOLUTION_TIMER_LOCK)
|
||||
{
|
||||
if (!_areHighResolutionTimersEnabled) return;
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
TimeEndPeriod(1);
|
||||
|
||||
_areHighResolutionTimersEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all open High Resolution Timer Requests.
|
||||
/// This should be called once when exiting the application to make sure nothing remains open and the application correctly unregisters itself on OS level.
|
||||
/// Shouldn't be needed if everything is disposed, but better safe then sorry.
|
||||
/// </summary>
|
||||
public static void DisposeAllHighResolutionTimerRequests()
|
||||
{
|
||||
List<HighResolutionTimerDisposable> timerLeases = new(_timerLeases);
|
||||
foreach (HighResolutionTimerDisposable timer in timerLeases)
|
||||
timer.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private class HighResolutionTimerDisposable : IDisposable
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
lock (HIGH_RESOLUTION_TIMER_LOCK)
|
||||
{
|
||||
_timerLeases.Remove(this);
|
||||
CheckHighResolutionTimerUsage();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
74
RGB.NET.Core/Ids/IdGenerator.cs
Normal file
74
RGB.NET.Core/Ids/IdGenerator.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some methods to create and handle unique identifiers.
|
||||
/// </summary>
|
||||
public static class IdGenerator
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
private static readonly HashSet<string> _registeredIds = new();
|
||||
private static readonly Dictionary<Assembly, Dictionary<string, string>> _idMappings = new();
|
||||
private static readonly Dictionary<Assembly, Dictionary<string, int>> _counter = new();
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Makes the specified id unique based on the calling assembly by adding a counter if needed.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to make unique.</param>
|
||||
/// <returns>The unique id.</returns>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static string MakeUnique(string id) => MakeUnique(Assembly.GetCallingAssembly(), id);
|
||||
|
||||
internal static string MakeUnique(Assembly callingAssembly, string id)
|
||||
{
|
||||
if (!_idMappings.TryGetValue(callingAssembly, out Dictionary<string, string>? idMapping))
|
||||
{
|
||||
_idMappings.Add(callingAssembly, idMapping = new Dictionary<string, string>());
|
||||
_counter.Add(callingAssembly, new Dictionary<string, int>());
|
||||
}
|
||||
|
||||
Dictionary<string, int> counterMapping = _counter[callingAssembly];
|
||||
|
||||
if (!idMapping.TryGetValue(id, out string? mappedId))
|
||||
{
|
||||
mappedId = id;
|
||||
int mappingCounter = 1;
|
||||
while (_registeredIds.Contains(mappedId))
|
||||
mappedId = $"{id} ({++mappingCounter})";
|
||||
|
||||
_registeredIds.Add(mappedId);
|
||||
idMapping.Add(id, mappedId);
|
||||
}
|
||||
|
||||
if (!counterMapping.ContainsKey(mappedId))
|
||||
counterMapping.Add(mappedId, 0);
|
||||
|
||||
int counter = ++counterMapping[mappedId];
|
||||
return counter <= 1 ? mappedId : $"{mappedId} ({counter})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the counter used to create unique ids.
|
||||
/// All previous generated ids are not garantueed to stay unique if this is called!
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ResetCounter() => ResetCounter(Assembly.GetCallingAssembly());
|
||||
|
||||
internal static void ResetCounter(Assembly callingAssembly)
|
||||
{
|
||||
if (_counter.TryGetValue(callingAssembly, out Dictionary<string, int>? counter))
|
||||
counter.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,310 +1,175 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a single LED of a RGB-device.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Id} {Color}")]
|
||||
public class Led : Placeable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single LED of a RGB-device.
|
||||
/// Gets the <see cref="IRGBDevice"/> this <see cref="Led"/> is associated with.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Id} {Color}")]
|
||||
public class Led : AbstractBindable
|
||||
public IRGBDevice Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="LedId"/> of the <see cref="Led" />.
|
||||
/// </summary>
|
||||
public LedId Id { get; }
|
||||
|
||||
private Shape _shape = Shape.Rectangle;
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Core.Shape"/> of the <see cref="Led"/>.
|
||||
/// </summary>
|
||||
public Shape Shape
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IRGBDevice"/> this <see cref="Led"/> is associated with.
|
||||
/// </summary>
|
||||
public IRGBDevice Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="LedId"/> of the <see cref="Led" />.
|
||||
/// </summary>
|
||||
public LedId Id { get; }
|
||||
|
||||
private Shape _shape = Shape.Rectangle;
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Core.Shape"/> of the <see cref="Led"/>.
|
||||
/// </summary>
|
||||
public Shape Shape
|
||||
{
|
||||
get => _shape;
|
||||
set => SetProperty(ref _shape, value);
|
||||
}
|
||||
|
||||
private string _shapeData;
|
||||
/// <summary>
|
||||
/// Gets or sets the data used for by the <see cref="Core.Shape.Custom"/>-<see cref="Core.Shape"/>.
|
||||
/// </summary>
|
||||
public string ShapeData
|
||||
{
|
||||
get => _shapeData;
|
||||
set => SetProperty(ref _shapeData, value);
|
||||
}
|
||||
|
||||
private Point _location;
|
||||
/// <summary>
|
||||
/// Gets or sets the relative location of the <see cref="Led"/>.
|
||||
/// </summary>
|
||||
public Point Location
|
||||
{
|
||||
get => _location;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _location, value))
|
||||
{
|
||||
UpdateActualData();
|
||||
UpdateAbsoluteData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Size _size;
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the <see cref="Led"/>.
|
||||
/// </summary>
|
||||
public Size Size
|
||||
{
|
||||
get => _size;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _size, value))
|
||||
{
|
||||
UpdateActualData();
|
||||
UpdateAbsoluteData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Point _actualLocation;
|
||||
/// <summary>
|
||||
/// Gets the actual location of the <see cref="Led"/>.
|
||||
/// This includes device-scaling and rotation.
|
||||
/// </summary>
|
||||
public Point ActualLocation
|
||||
{
|
||||
get => _actualLocation;
|
||||
private set => SetProperty(ref _actualLocation, value);
|
||||
}
|
||||
|
||||
private Size _actualSize;
|
||||
/// <summary>
|
||||
/// Gets the actual size of the <see cref="Led"/>.
|
||||
/// This includes device-scaling.
|
||||
/// </summary>
|
||||
public Size ActualSize
|
||||
{
|
||||
get => _actualSize;
|
||||
private set => SetProperty(ref _actualSize, value);
|
||||
}
|
||||
|
||||
private Rectangle _ledRectangle;
|
||||
/// <summary>
|
||||
/// Gets a rectangle representing the logical location of the <see cref="Led"/> relative to the <see cref="Device"/>.
|
||||
/// </summary>
|
||||
public Rectangle LedRectangle
|
||||
{
|
||||
get => _ledRectangle;
|
||||
private set => SetProperty(ref _ledRectangle, value);
|
||||
}
|
||||
|
||||
private Rectangle _absoluteLedRectangle;
|
||||
/// <summary>
|
||||
/// Gets a rectangle representing the logical location of the <see cref="Led"/> on the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
public Rectangle AbsoluteLedRectangle
|
||||
{
|
||||
get => _absoluteLedRectangle;
|
||||
private set => SetProperty(ref _absoluteLedRectangle, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the <see cref="Led" /> is about to change it's color.
|
||||
/// </summary>
|
||||
public bool IsDirty => RequestedColor.HasValue && (RequestedColor != InternalColor);
|
||||
|
||||
private Color? _requestedColor;
|
||||
/// <summary>
|
||||
/// Gets a copy of the <see cref="Core.Color"/> the LED should be set to on the next update.
|
||||
/// Null if there is no update-request for the next update.
|
||||
/// </summary>
|
||||
public Color? RequestedColor
|
||||
{
|
||||
get => _requestedColor;
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _requestedColor, value);
|
||||
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(nameof(IsDirty));
|
||||
}
|
||||
}
|
||||
|
||||
private Color _color = Color.Transparent;
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="Core.Color"/> of the <see cref="Led"/>. Sets the <see cref="RequestedColor" /> for the next update.
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get => _color;
|
||||
set
|
||||
{
|
||||
if (!IsLocked)
|
||||
{
|
||||
if (RequestedColor.HasValue)
|
||||
RequestedColor += value;
|
||||
else
|
||||
RequestedColor = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the <see cref="Color"/> ignoring all workflows regarding locks and update-requests. />
|
||||
/// </summary>
|
||||
internal Color InternalColor
|
||||
{
|
||||
get => _color;
|
||||
set => SetProperty(ref _color, value);
|
||||
}
|
||||
|
||||
private bool _isLocked;
|
||||
/// <summary>
|
||||
/// Gets or sets if the color of this LED can be changed.
|
||||
/// </summary>
|
||||
public bool IsLocked
|
||||
{
|
||||
get => _isLocked;
|
||||
set => SetProperty(ref _isLocked, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URI of an image of the <see cref="Led"/> or null if there is no image.
|
||||
/// </summary>
|
||||
public Uri Image { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider-specific data associated with this led.
|
||||
/// </summary>
|
||||
public object CustomData { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Led"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The <see cref="IRGBDevice"/> the <see cref="Led"/> is associated with.</param>
|
||||
/// <param name="id">The <see cref="LedId"/> of the <see cref="Led"/>.</param>
|
||||
/// <param name="location">The physical location of the <see cref="Led"/> relative to the <see cref="Device"/>.</param>
|
||||
/// <param name="size">The size of the <see cref="Led"/>.</param>
|
||||
/// <param name="customData">The provider-specific data associated with this led.</param>
|
||||
internal Led(IRGBDevice device, LedId id, Point location, Size size, object customData = null)
|
||||
{
|
||||
this.Device = device;
|
||||
this.Id = id;
|
||||
this.Location = location;
|
||||
this.Size = size;
|
||||
this.CustomData = customData;
|
||||
|
||||
device.PropertyChanged += DevicePropertyChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private void DevicePropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if ((e.PropertyName == nameof(IRGBDevice.Location)))
|
||||
UpdateAbsoluteData();
|
||||
else if (e.PropertyName == nameof(IRGBDevice.DeviceRectangle))
|
||||
{
|
||||
UpdateActualData();
|
||||
UpdateAbsoluteData();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateActualData()
|
||||
{
|
||||
ActualSize = Size * Device.Scale;
|
||||
|
||||
Point actualLocation = (Location * Device.Scale);
|
||||
Rectangle ledRectangle = new Rectangle(Location * Device.Scale, Size * Device.Scale);
|
||||
|
||||
if (Device.Rotation.IsRotated)
|
||||
{
|
||||
Point deviceCenter = new Rectangle(Device.ActualSize).Center;
|
||||
Point actualDeviceCenter = new Rectangle(Device.DeviceRectangle.Size).Center;
|
||||
Point centerOffset = new Point(actualDeviceCenter.X - deviceCenter.X, actualDeviceCenter.Y - deviceCenter.Y);
|
||||
|
||||
actualLocation = actualLocation.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center) + centerOffset;
|
||||
ledRectangle = new Rectangle(ledRectangle.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center)).Translate(centerOffset);
|
||||
}
|
||||
|
||||
ActualLocation = actualLocation;
|
||||
LedRectangle = ledRectangle;
|
||||
}
|
||||
|
||||
private void UpdateAbsoluteData()
|
||||
{
|
||||
AbsoluteLedRectangle = LedRectangle.Translate(Device.Location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/>. For example "Enter [A: 255, R: 255, G: 0, B: 0]".</returns>
|
||||
public override string ToString() => $"{Id} {Color}";
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Led"/> to the requested <see cref="Core.Color"/>.
|
||||
/// </summary>
|
||||
internal void Update()
|
||||
{
|
||||
if (!RequestedColor.HasValue) return;
|
||||
|
||||
_color = RequestedColor.Value;
|
||||
RequestedColor = null;
|
||||
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(nameof(Color));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the <see cref="Led"/> back to default.
|
||||
/// </summary>
|
||||
internal void Reset()
|
||||
{
|
||||
_color = Color.Transparent;
|
||||
RequestedColor = null;
|
||||
IsLocked = false;
|
||||
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(nameof(Color));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Led" /> to a <see cref="Core.Color" />.
|
||||
/// </summary>
|
||||
/// <param name="led">The <see cref="Led"/> to convert.</param>
|
||||
public static implicit operator Color(Led led) => led?.Color ?? Color.Transparent;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Led" /> to a <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="led">The <see cref="Led"/> to convert.</param>
|
||||
public static implicit operator Rectangle(Led led) => led?.LedRectangle ?? new Rectangle();
|
||||
|
||||
#endregion
|
||||
get => _shape;
|
||||
set => SetProperty(ref _shape, value);
|
||||
}
|
||||
}
|
||||
|
||||
private string? _shapeData;
|
||||
/// <summary>
|
||||
/// Gets or sets the data used for by the <see cref="Core.Shape.Custom"/>-<see cref="Core.Shape"/>.
|
||||
/// </summary>
|
||||
public string? ShapeData
|
||||
{
|
||||
get => _shapeData;
|
||||
set => SetProperty(ref _shapeData, value);
|
||||
}
|
||||
|
||||
private Rectangle _absoluteBoundary;
|
||||
/// <summary>
|
||||
/// Gets a rectangle representing the logical location of the <see cref="Led"/> on the <see cref="RGBSurface"/>.
|
||||
/// </summary>
|
||||
public Rectangle AbsoluteBoundary
|
||||
{
|
||||
get => _absoluteBoundary;
|
||||
private set => SetProperty(ref _absoluteBoundary, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the <see cref="Led" /> is about to change it's color.
|
||||
/// </summary>
|
||||
public bool IsDirty => RequestedColor.HasValue && (RequestedColor != Color);
|
||||
|
||||
private Color? _requestedColor;
|
||||
/// <summary>
|
||||
/// Gets a copy of the <see cref="Core.Color"/> the LED should be set to on the next update.
|
||||
/// Null if there is no update-request for the next update.
|
||||
/// </summary>
|
||||
public Color? RequestedColor
|
||||
{
|
||||
get => _requestedColor;
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _requestedColor, value);
|
||||
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(nameof(IsDirty));
|
||||
}
|
||||
}
|
||||
|
||||
private Color _color = Color.Transparent;
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="Core.Color"/> of the <see cref="Led"/>. Sets the <see cref="RequestedColor" /> for the next update.
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get => _color;
|
||||
set
|
||||
{
|
||||
if (RequestedColor.HasValue)
|
||||
RequestedColor = RequestedColor.Value + value;
|
||||
else
|
||||
RequestedColor = _color + value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider-specific data associated with this led.
|
||||
/// </summary>
|
||||
public object? CustomData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets some custom metadata of this led.
|
||||
/// </summary>
|
||||
public object? LayoutMetadata { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Led"/> class.
|
||||
/// </summary>
|
||||
/// <param name="device">The <see cref="IRGBDevice"/> the <see cref="Led"/> is associated with.</param>
|
||||
/// <param name="id">The <see cref="LedId"/> of the <see cref="Led"/>.</param>
|
||||
/// <param name="location">The physical location of the <see cref="Led"/> relative to the <see cref="Device"/>.</param>
|
||||
/// <param name="size">The size of the <see cref="Led"/>.</param>
|
||||
/// <param name="customData">The provider-specific data associated with this led.</param>
|
||||
internal Led(IRGBDevice device, LedId id, Point location, Size size, object? customData = null)
|
||||
: base(device)
|
||||
{
|
||||
this.Device = device;
|
||||
this.Id = id;
|
||||
this.Location = location;
|
||||
this.Size = size;
|
||||
this.CustomData = customData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateActualPlaceableData()
|
||||
{
|
||||
base.UpdateActualPlaceableData();
|
||||
|
||||
AbsoluteBoundary = Boundary.Translate(Device.Location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/>. For example "Enter [A: 255, R: 255, G: 0, B: 0]".</returns>
|
||||
public override string ToString() => $"{Id} {Color}";
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="Led"/> to the requested <see cref="Core.Color"/>.
|
||||
/// </summary>
|
||||
internal void Update()
|
||||
{
|
||||
if (!RequestedColor.HasValue) return;
|
||||
|
||||
_color = RequestedColor.Value;
|
||||
RequestedColor = null;
|
||||
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(nameof(Color));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Led" /> to a <see cref="Core.Color" />.
|
||||
/// </summary>
|
||||
/// <param name="led">The <see cref="Led"/> to convert.</param>
|
||||
public static implicit operator Color(Led led) => led.Color;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Led" /> to a <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="led">The <see cref="Led"/> to convert.</param>
|
||||
public static implicit operator Rectangle(Led led) => led.Boundary;
|
||||
|
||||
#endregion
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
148
RGB.NET.Core/Leds/LedMapping.cs
Normal file
148
RGB.NET.Core/Leds/LedMapping.cs
Normal file
@ -0,0 +1,148 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a mapping from <see cref="LedId"/> to a custom identifier.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The identifier the <see cref="LedId"/> is mapped to.</typeparam>
|
||||
public class LedMapping<T> : IEnumerable<(LedId ledId, T mapping)>
|
||||
where T : notnull
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly Dictionary<LedId, T> _mapping = new();
|
||||
private readonly Dictionary<T, LedId> _reverseMapping = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of entries in this mapping.
|
||||
/// </summary>
|
||||
public int Count => _mapping.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of all mapped ledids.
|
||||
/// </summary>
|
||||
public ICollection<LedId> LedIds => _mapping.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of all mapped custom identifiers.
|
||||
/// </summary>
|
||||
public ICollection<T> Mappings => _reverseMapping.Keys;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom identifier mapped to the specified <see cref="LedId"/>.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The led id to get the mapped identifier.</param>
|
||||
/// <returns>The mapped ifentifier.</returns>
|
||||
public T this[LedId ledId]
|
||||
{
|
||||
get => _mapping[ledId];
|
||||
set
|
||||
{
|
||||
_mapping[ledId] = value;
|
||||
_reverseMapping[value] = ledId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="LedId"/> mapped to the specified custom identifier.
|
||||
/// </summary>
|
||||
/// <param name="mapping">The custom identifier to get the mapped led id.</param>
|
||||
/// <returns>The led id.</returns>
|
||||
public LedId this[T mapping]
|
||||
{
|
||||
get => _reverseMapping[mapping];
|
||||
set => this[value] = mapping;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new entry to the mapping.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The <see cref="LedId"/> to map.</param>
|
||||
/// <param name="mapping">The custom identifier to map.</param>
|
||||
public void Add(LedId ledId, T mapping)
|
||||
{
|
||||
_mapping.Add(ledId, mapping);
|
||||
_reverseMapping.Add(mapping, ledId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified <see cref="LedId"/> is mapped.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The led id to check.</param>
|
||||
/// <returns><c>true</c> if the led id is mapped; otherwise <c>false</c>.</returns>
|
||||
public bool Contains(LedId ledId) => _mapping.ContainsKey(ledId);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified custom identifier is mapped.
|
||||
/// </summary>
|
||||
/// <param name="mapping">The custom identifier to check.</param>
|
||||
/// <returns><c>true</c> if the led id is mapped; otherwise <c>false</c>.</returns>
|
||||
public bool Contains(T mapping) => _reverseMapping.ContainsKey(mapping);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom identifier mapped to the specified led id.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The led id to get the custom identifier for.</param>
|
||||
/// <param name="mapping">Contains the mapped custom identifier or null if there is no mapping for the specified led id.</param>
|
||||
/// <returns><c>true</c> if there was a custom identifier for the specified led id; otherwise <c>false</c>.</returns>
|
||||
public bool TryGetValue(LedId ledId, out T? mapping) => _mapping.TryGetValue(ledId, out mapping);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the led id mapped to the specified custom identifier.
|
||||
/// </summary>
|
||||
/// <param name="mapping">The custom identifier to get the led id for.</param>
|
||||
/// <param name="ledId">Contains the mapped led id or null if there is no mapping for the specified led id.</param>
|
||||
/// <returns><c>true</c> if there was a led id for the specified custom identifier; otherwise <c>false</c>.</returns>
|
||||
public bool TryGetValue(T mapping, out LedId ledId) => _reverseMapping.TryGetValue(mapping, out ledId);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified led id and the mapped custom identifier.
|
||||
/// </summary>
|
||||
/// <param name="ledId">The led id to remove.</param>
|
||||
/// <returns><c>true</c> if there was a mapping for the led id to remove; otherwise <c>false</c>.</returns>
|
||||
public bool Remove(LedId ledId)
|
||||
{
|
||||
if (_mapping.TryGetValue(ledId, out T? mapping))
|
||||
_reverseMapping.Remove(mapping);
|
||||
return _mapping.Remove(ledId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified custom identifier and the mapped led id.
|
||||
/// </summary>
|
||||
/// <param name="mapping">The custom identifier to remove.</param>
|
||||
/// <returns><c>true</c> if there was a mapping for the custom identifier to remove; otherwise <c>false</c>.</returns>
|
||||
public bool Remove(T mapping)
|
||||
{
|
||||
if (_reverseMapping.TryGetValue(mapping, out LedId ledId))
|
||||
_mapping.Remove(ledId);
|
||||
return _reverseMapping.Remove(mapping);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all registered mappings.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_mapping.Clear();
|
||||
_reverseMapping.Clear();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<(LedId ledId, T mapping)> GetEnumerator() => _mapping.Select(x => (x.Key, x.Value)).GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,67 +1,61 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a basic bindable class which notifies when a property value changes.
|
||||
/// </summary>
|
||||
public abstract class AbstractBindable : IBindable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic bindable class which notifies when a property value changes.
|
||||
/// Occurs when a property value changes.
|
||||
/// </summary>
|
||||
public abstract class AbstractBindable : IBindable
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the property already matches the desired value or needs to be updated.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the property.</typeparam>
|
||||
/// <param name="storage">Reference to the backing-filed.</param>
|
||||
/// <param name="value">Value to apply.</param>
|
||||
/// <returns><c>true</c> if the value needs to be updated; otherweise <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected virtual bool RequiresUpdate<T>(ref T storage, T value) => !Equals(storage, value);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the property already matches the desired value and updates it if not.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the property.</typeparam>
|
||||
/// <param name="storage">Reference to the backing-filed.</param>
|
||||
/// <param name="value">Value to apply.</param>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
|
||||
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
|
||||
/// <returns><c>true</c> if the value was changed, <c>false</c> if the existing value matched the desired value.</returns>
|
||||
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
#region Events
|
||||
if (!RequiresUpdate(ref storage, value)) return false;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a property value changes.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the property already matches the desirec value or needs to be updated.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the property.</typeparam>
|
||||
/// <param name="storage">Reference to the backing-filed.</param>
|
||||
/// <param name="value">Value to apply.</param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected virtual bool RequiresUpdate<T>(ref T storage, T value)
|
||||
{
|
||||
return !Equals(storage, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the property already matches the desired value and updates it if not.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the property.</typeparam>
|
||||
/// <param name="storage">Reference to the backing-filed.</param>
|
||||
/// <param name="value">Value to apply.</param>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
|
||||
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
|
||||
/// <returns><c>true</c> if the value was changed, <c>false</c> if the existing value matched the desired value.</returns>
|
||||
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (!this.RequiresUpdate(ref storage, value)) return false;
|
||||
|
||||
storage = value;
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
this.OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the <see cref="PropertyChanged"/>-event when a a property value has changed.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
|
||||
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
storage = value;
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the <see cref="PropertyChanged"/>-event when a a property value has changed.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
|
||||
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,11 +1,10 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a basic bindable class which notifies when a property value changes.
|
||||
/// </summary>
|
||||
public interface IBindable : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a basic bindable class which notifies when a property value changes.
|
||||
/// </summary>
|
||||
public interface IBindable : INotifyPropertyChanged
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
92
RGB.NET.Core/Positioning/IPlaceable.cs
Normal file
92
RGB.NET.Core/Positioning/IPlaceable.cs
Normal file
@ -0,0 +1,92 @@
|
||||
// ReSharper disable EventNeverSubscribedTo.Global
|
||||
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic placeable element.
|
||||
/// </summary>
|
||||
public interface IPlaceable
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the location of the <see cref="IPlaceable"/>.
|
||||
/// </summary>
|
||||
Point Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the <see cref="IPlaceable"/>.
|
||||
/// </summary>
|
||||
Size Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scale of the <see cref="IPlaceable"/>.
|
||||
/// </summary>
|
||||
Scale Scale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation of the <see cref="IPlaceable"/>.
|
||||
/// </summary>
|
||||
Rotation Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual location of the <see cref="IPlaceable"/>.
|
||||
/// This includes the <see cref="Rotation"/>.
|
||||
/// </summary>
|
||||
Point ActualLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual <see cref="Size"/> of the <see cref="IPlaceable"/>.
|
||||
/// This includes the <see cref="Scale"/>.
|
||||
/// </summary>
|
||||
Size ActualSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a rectangle containing the whole <see cref="IPlaceable"/>.
|
||||
/// This includes <see cref="Location"/>, <see cref="Size"/>, <see cref="Scale"/> and <see cref="Rotation"/>.
|
||||
/// </summary>
|
||||
Rectangle Boundary { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="Location"/> property was changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> LocationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="Size"/> property was changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> SizeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="Scale"/> property was changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> ScaleChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="Rotation"/> property was changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> RotationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="ActualLocation"/> property was changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> ActualLocationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="ActualSize"/> property was changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> ActualSizeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the <see cref="Boundary"/> property was changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> BoundaryChanged;
|
||||
|
||||
#endregion
|
||||
}
|
||||
261
RGB.NET.Core/Positioning/Placeable.cs
Normal file
261
RGB.NET.Core/Positioning/Placeable.cs
Normal file
@ -0,0 +1,261 @@
|
||||
using System;
|
||||
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a placeable element.
|
||||
/// </summary>
|
||||
public class Placeable : AbstractBindable, IPlaceable
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent this placeable is placed in.
|
||||
/// </summary>
|
||||
protected IPlaceable? Parent { get; }
|
||||
|
||||
private Point _location = Point.Invalid;
|
||||
/// <inheritdoc />
|
||||
public Point Location
|
||||
{
|
||||
get => _location;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _location, value))
|
||||
OnLocationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Size _size = Size.Invalid;
|
||||
/// <inheritdoc />
|
||||
public Size Size
|
||||
{
|
||||
get => _size;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _size, value))
|
||||
OnSizeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Scale _scale = new(1);
|
||||
/// <inheritdoc />
|
||||
public Scale Scale
|
||||
{
|
||||
get => _scale;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _scale, value))
|
||||
OnScaleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Rotation _rotation = new(0);
|
||||
/// <inheritdoc />
|
||||
public Rotation Rotation
|
||||
{
|
||||
get => _rotation;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _rotation, value))
|
||||
OnRotationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Point _actualLocation = Point.Invalid;
|
||||
/// <inheritdoc />
|
||||
public Point ActualLocation
|
||||
{
|
||||
get => _actualLocation;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _actualLocation, value))
|
||||
OnActualLocationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Size _actualSize = Size.Invalid;
|
||||
/// <inheritdoc />
|
||||
public Size ActualSize
|
||||
{
|
||||
get => _actualSize;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _actualSize, value))
|
||||
OnActualSizeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle _boundary = new(Point.Invalid, Point.Invalid);
|
||||
/// <inheritdoc />
|
||||
public Rectangle Boundary
|
||||
{
|
||||
get => _boundary;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _boundary, value))
|
||||
OnBoundaryChanged();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs>? LocationChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs>? SizeChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs>? ScaleChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs>? RotationChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs>? ActualLocationChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs>? ActualSizeChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs>? BoundaryChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Placeable" /> class.
|
||||
/// </summary>
|
||||
public Placeable() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Placeable" /> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent this placeable is placed in.</param>
|
||||
public Placeable(IPlaceable parent)
|
||||
{
|
||||
this.Parent = parent;
|
||||
|
||||
Parent.BoundaryChanged += (_, _) => UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Placeable" /> class.
|
||||
/// </summary>
|
||||
/// <param name="location">The location of this placeable.</param>
|
||||
/// <param name="size">The size of this placeable.</param>
|
||||
public Placeable(Point location, Size size)
|
||||
{
|
||||
this.Location = location;
|
||||
this.Size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Placeable" /> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent placeable this placeable is placed in.</param>
|
||||
/// <param name="location">The location of this placeable.</param>
|
||||
/// <param name="size">The size of this placeable.</param>
|
||||
public Placeable(IPlaceable parent, Point location, Size size)
|
||||
{
|
||||
this.Parent = parent;
|
||||
this.Location = location;
|
||||
this.Size = size;
|
||||
|
||||
Parent.BoundaryChanged += (_, _) => UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the <see cref="ActualSize"/>, <see cref="ActualLocation"/> and <see cref="Boundary"/> based on the <see cref="Size"/>, <see cref="Scale"/> and <see cref="Rotation"/>.
|
||||
/// </summary>
|
||||
protected virtual void UpdateActualPlaceableData()
|
||||
{
|
||||
if (Parent != null)
|
||||
{
|
||||
Size actualSize = Size * Parent.Scale;
|
||||
Point actualLocation = (Location * Parent.Scale);
|
||||
Rectangle boundary = new(actualLocation, actualSize);
|
||||
|
||||
if (Parent.Rotation.IsRotated)
|
||||
{
|
||||
Point parentCenter = new Rectangle(Parent.ActualSize).Center;
|
||||
Point actualParentCenter = new Rectangle(Parent.Boundary.Size).Center;
|
||||
Point centerOffset = new(actualParentCenter.X - parentCenter.X, actualParentCenter.Y - parentCenter.Y);
|
||||
|
||||
actualLocation = actualLocation.Rotate(Parent.Rotation, new Rectangle(Parent.ActualSize).Center) + centerOffset;
|
||||
boundary = new Rectangle(boundary.Rotate(Parent.Rotation, new Rectangle(Parent.ActualSize).Center)).Translate(centerOffset);
|
||||
}
|
||||
|
||||
ActualLocation = actualLocation;
|
||||
ActualSize = actualSize;
|
||||
Boundary = boundary;
|
||||
}
|
||||
else
|
||||
{
|
||||
ActualLocation = Location;
|
||||
ActualSize = Size * Scale;
|
||||
Boundary = new Rectangle(Location, new Rectangle(new Rectangle(Location, ActualSize).Rotate(Rotation)).Size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="Location"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnLocationChanged()
|
||||
{
|
||||
LocationChanged?.Invoke(this, new EventArgs());
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="Size"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnSizeChanged()
|
||||
{
|
||||
SizeChanged?.Invoke(this, new EventArgs());
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="Scale"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnScaleChanged()
|
||||
{
|
||||
ScaleChanged?.Invoke(this, new EventArgs());
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="Rotation"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnRotationChanged()
|
||||
{
|
||||
RotationChanged?.Invoke(this, new EventArgs());
|
||||
UpdateActualPlaceableData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ActualLocation"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnActualLocationChanged() => ActualLocationChanged?.Invoke(this, new EventArgs());
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="ActualLocation"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnActualSizeChanged() => ActualSizeChanged?.Invoke(this, new EventArgs());
|
||||
|
||||
/// <summary>
|
||||
/// Called when the <see cref="Boundary"/> property was changed.
|
||||
/// </summary>
|
||||
protected virtual void OnBoundaryChanged() => BoundaryChanged?.Invoke(this, new EventArgs());
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,162 +1,154 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a point consisting of a X- and a Y-position.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[X: {X}, Y: {Y}]")]
|
||||
public readonly struct Point
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static readonly Point INVALID = new(float.NaN, float.NaN);
|
||||
/// <summary>
|
||||
/// Represents a point consisting of a X- and a Y-position.
|
||||
/// Gets a [NaN,NaN]-Point.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[X: {X}, Y: {Y}]")]
|
||||
public struct Point
|
||||
public static ref readonly Point Invalid => ref INVALID;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X-position of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public float X { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y-position of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public float Y { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Point"/> class using the provided values.
|
||||
/// </summary>
|
||||
/// <param name="x">The value used for the X-position.</param>
|
||||
/// <param name="y">The value used for the Y-position.</param>
|
||||
public Point(float x, float y)
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Gets a [NaN,NaN]-Point.
|
||||
/// </summary>
|
||||
public static Point Invalid => new Point(double.NaN, double.NaN);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X-position of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public double X { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y-position of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public double Y { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Point"/> class using the provided values.
|
||||
/// </summary>
|
||||
/// <param name="x">The value used for the X-position.</param>
|
||||
/// <param name="y">The value used for the Y-position.</param>
|
||||
public Point(double x, double y)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="X"/>- and <see cref="Y"/>-position of this <see cref="Point"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the <see cref="X"/> and <see cref="Y"/> of this <see cref="Point"/>. For example "[X: 100, Y: 20]".</returns>
|
||||
public override string ToString() => $"[X: {X}, Y: {Y}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Point" /> and is equivalent to this <see cref="Point" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Point" /> equivalent to this <see cref="Point" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is Point)) return false;
|
||||
|
||||
Point comparePoint = (Point)obj;
|
||||
return ((double.IsNaN(X) && double.IsNaN(comparePoint.X)) || X.EqualsInTolerance(comparePoint.X))
|
||||
&& ((double.IsNaN(Y) && double.IsNaN(comparePoint.Y)) || Y.EqualsInTolerance(comparePoint.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Point" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Point" />.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = X.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Y.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Point" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point" /> to compare.</param>
|
||||
/// <param name="point2">The second <see cref="Point" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="point1" /> and <paramref name="point2" /> are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(Point point1, Point point2) => point1.Equals(point2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Point" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point" /> to compare.</param>
|
||||
/// <param name="point2">The second <see cref="Point" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="point1" /> and <paramref name="point2" /> are not equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(Point point1, Point point2) => !(point1 == point2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the addition of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the addition of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator +(Point point1, Point point2) => new Point(point1.X + point2.X, point1.Y + point2.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Rectangle"/> created from the provided <see cref="Point"/> and <see cref="Size"/>.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> of the rectangle.</param>
|
||||
/// <param name="size">The <see cref="Size"/> of the rectangle.</param>
|
||||
/// <returns>The rectangle created from the provided <see cref="Point"/> and <see cref="Size"/>.</returns>
|
||||
public static Rectangle operator +(Point point, Size size) => new Rectangle(point, size);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the subtraction of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the subtraction of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator -(Point point1, Point point2) => new Point(point1.X - point2.X, point1.Y - point2.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the multiplication of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the multiplication of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator *(Point point1, Point point2) => new Point(point1.X * point2.X, point1.Y * point2.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the division of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the division of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator /(Point point1, Point point2)
|
||||
{
|
||||
if (point2.X.EqualsInTolerance(0) || point2.Y.EqualsInTolerance(0)) return Invalid;
|
||||
return new Point(point1.X / point2.X, point1.Y / point2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the multiplication of the <see cref="Point"/> and the provided <see cref="Scale"/>.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/>.</param>
|
||||
/// <param name="scale">The <see cref="Scale"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the multiplication of the <see cref="Point"/> and the provided <see cref="Scale"/>.</returns>
|
||||
public static Point operator *(Point point, Scale scale) => new Point(point.X * scale.Horizontal, point.Y * scale.Vertical);
|
||||
|
||||
#endregion
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="X"/>- and <see cref="Y"/>-position of this <see cref="Point"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the <see cref="X"/> and <see cref="Y"/> of this <see cref="Point"/>. For example "[X: 100, Y: 20]".</returns>
|
||||
public override string ToString() => $"[X: {X}, Y: {Y}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Point" /> and is equivalent to this <see cref="Point" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Point" /> equivalent to this <see cref="Point" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not Point comparePoint) return false;
|
||||
|
||||
return ((float.IsNaN(X) && float.IsNaN(comparePoint.X)) || X.EqualsInTolerance(comparePoint.X))
|
||||
&& ((float.IsNaN(Y) && float.IsNaN(comparePoint.Y)) || Y.EqualsInTolerance(comparePoint.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Point" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Point" />.</returns>
|
||||
public override int GetHashCode() => HashCode.Combine(X, Y);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Point" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point" /> to compare.</param>
|
||||
/// <param name="point2">The second <see cref="Point" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="point1" /> and <paramref name="point2" /> are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(in Point point1, in Point point2) => point1.Equals(point2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Point" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point" /> to compare.</param>
|
||||
/// <param name="point2">The second <see cref="Point" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="point1" /> and <paramref name="point2" /> are not equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(in Point point1, in Point point2) => !(point1 == point2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the addition of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the addition of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator +(in Point point1, in Point point2) => new(point1.X + point2.X, point1.Y + point2.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Rectangle"/> created from the provided <see cref="Point"/> and <see cref="Size"/>.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/> of the rectangle.</param>
|
||||
/// <param name="size">The <see cref="Size"/> of the rectangle.</param>
|
||||
/// <returns>The rectangle created from the provided <see cref="Point"/> and <see cref="Size"/>.</returns>
|
||||
public static Rectangle operator +(in Point point, in Size size) => new(point, size);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the subtraction of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the subtraction of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator -(in Point point1, in Point point2) => new(point1.X - point2.X, point1.Y - point2.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the multiplication of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the multiplication of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator *(in Point point1, in Point point2) => new(point1.X * point2.X, point1.Y * point2.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the division of the two provided <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/>.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the division of the two provided <see cref="Point"/>.</returns>
|
||||
public static Point operator /(in Point point1, in Point point2)
|
||||
{
|
||||
if (point2.X.EqualsInTolerance(0) || point2.Y.EqualsInTolerance(0)) return Invalid;
|
||||
return new Point(point1.X / point2.X, point1.Y / point2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Point"/> representing the multiplication of the <see cref="Point"/> and the provided <see cref="Scale"/>.
|
||||
/// </summary>
|
||||
/// <param name="point">The <see cref="Point"/>.</param>
|
||||
/// <param name="scale">The <see cref="Scale"/>.</param>
|
||||
/// <returns>A new <see cref="Point"/> representing the multiplication of the <see cref="Point"/> and the provided <see cref="Scale"/>.</returns>
|
||||
public static Point operator *(in Point point, in Scale scale) => new(point.X * scale.Horizontal, point.Y * scale.Vertical);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -6,223 +6,236 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace RGB.NET.Core
|
||||
namespace RGB.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rectangle defined by it's position and it's size.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[Location: {Location}, Size: {Size}]")]
|
||||
public readonly struct Rectangle
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rectangle defined by it's position and it's size.
|
||||
/// Gets the <see cref="Point"/> representing the top-left corner of the <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("[Location: {Location}, Size: {Size}]")]
|
||||
public struct Rectangle
|
||||
public Point Location { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Size"/> of the <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
public Size Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new <see cref="Point"/> representing the center-point of the <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
public Point Center { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bool indicating if both, the width and the height of the rectangle is greater than zero.
|
||||
/// <c>True</c> if the rectangle has a width or a height of zero; otherwise, <c>false</c>.
|
||||
/// </summary>
|
||||
public bool IsEmpty => (Size.Width <= FloatExtensions.TOLERANCE) || (Size.Height <= FloatExtensions.TOLERANCE);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the provided values for <see cref="P:RGB.NET.Core.Rectangle.Location" /> ans <see cref="P:RGB.NET.Core.Rectangle.Size" />.
|
||||
/// </summary>
|
||||
/// <param name="x">The x-value of the <see cref="T:RGB.NET.Core.Location" /> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="y">The y-value of the <see cref="T:RGB.NET.Core.Location" />-position of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="width">The width of the <see cref="T:RGB.NET.Core.Size"/> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="height">The height of the <see cref="T:RGB.NET.Core.Size"/> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
public Rectangle(float x, float y, float width, float height)
|
||||
: this(new Point(x, y), new Size(width, height))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class using the <see cref="Location"/>(0,0) and the specified <see cref="Core.Size"/>.
|
||||
/// </summary>
|
||||
/// <param name="size">The size of of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
public Rectangle(Size size) : this(new Point(), size)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class using the specified <see cref="Point"/> and <see cref="Core.Size"/>.
|
||||
/// </summary>
|
||||
/// <param name="location">The location of this of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="size">The size of of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
public Rectangle(Point location, Size size)
|
||||
{
|
||||
#region Properties & Fields
|
||||
this.Location = location;
|
||||
this.Size = size;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Point"/> representing the top-left corner of the <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
public Point Location { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Size"/> of the <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
public Size Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new <see cref="Point"/> representing the center-point of the <see cref="Rectangle"/>.
|
||||
/// </summary>
|
||||
public Point Center { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bool indicating if both, the width and the height of the rectangle is greater than zero.
|
||||
/// <c>True</c> if the rectangle has a width or a height of zero; otherwise, <c>false</c>.
|
||||
/// </summary>
|
||||
public bool IsEmpty => (Size.Width <= DoubleExtensions.TOLERANCE) || (Size.Height <= DoubleExtensions.TOLERANCE);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the provided values for <see cref="P:RGB.NET.Core.Rectangle.Location" /> ans <see cref="P:RGB.NET.Core.Rectangle.Size" />.
|
||||
/// </summary>
|
||||
/// <param name="x">The x-value of the <see cref="T:RGB.NET.Core.Location" /> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="y">The y-value of the <see cref="T:RGB.NET.Core.Location" />-position of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="width">The width of the <see cref="T:RGB.NET.Core.Size"/> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="height">The height of the <see cref="T:RGB.NET.Core.Size"/> of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
public Rectangle(double x, double y, double width, double height)
|
||||
: this(new Point(x, y), new Size(width, height))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class using the <see cref="Location"/>(0,0) and the given <see cref="Core.Size"/>.
|
||||
/// </summary>
|
||||
/// <param name="size">The size of of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
public Rectangle(Size size) : this(new Point(), size)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class using the given <see cref="Point"/> and <see cref="Core.Size"/>.
|
||||
/// </summary>
|
||||
/// <param name="location">The location of this of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
/// <param name="size">The size of of this <see cref="T:RGB.NET.Core.Rectangle" />.</param>
|
||||
public Rectangle(Point location, Size size)
|
||||
{
|
||||
this.Location = location;
|
||||
this.Size = size;
|
||||
Center = new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the given array of <see cref="T:RGB.NET.Core.Rectangle" />.
|
||||
/// The <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /> is calculated to completely contain all rectangles provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="rectangles">The array of <see cref="T:RGB.NET.Core.Rectangle" /> used to calculate the <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /></param>
|
||||
public Rectangle(params Rectangle[] rectangles)
|
||||
: this(rectangles.AsEnumerable())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class using the given list of <see cref="Rectangle"/>.
|
||||
/// The <see cref="Location"/> and <see cref="Size"/> is calculated to completely contain all rectangles provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="rectangles">The list of <see cref="Rectangle"/> used to calculate the <see cref="Location"/> and <see cref="Size"/></param>
|
||||
public Rectangle(IEnumerable<Rectangle> rectangles)
|
||||
{
|
||||
bool hasPoint = false;
|
||||
double posX = double.MaxValue;
|
||||
double posY = double.MaxValue;
|
||||
double posX2 = double.MinValue;
|
||||
double posY2 = double.MinValue;
|
||||
|
||||
if (rectangles != null)
|
||||
foreach (Rectangle rectangle in rectangles)
|
||||
{
|
||||
hasPoint = true;
|
||||
posX = Math.Min(posX, rectangle.Location.X);
|
||||
posY = Math.Min(posY, rectangle.Location.Y);
|
||||
posX2 = Math.Max(posX2, rectangle.Location.X + rectangle.Size.Width);
|
||||
posY2 = Math.Max(posY2, rectangle.Location.Y + rectangle.Size.Height);
|
||||
}
|
||||
|
||||
(Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0));
|
||||
Location = location;
|
||||
Size = size;
|
||||
Center = new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the given array of <see cref="T:RGB.NET.Core.Point" />.
|
||||
/// The <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /> is calculated to contain all points provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="points">The array of <see cref="T:RGB.NET.Core.Point" /> used to calculate the <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /></param>
|
||||
public Rectangle(params Point[] points)
|
||||
: this(points.AsEnumerable())
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the given list of <see cref="T:RGB.NET.Core.Point" />.
|
||||
/// The <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /> is calculated to contain all points provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="points">The list of <see cref="T:RGB.NET.Core.Point" /> used to calculate the <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /></param>
|
||||
public Rectangle(IEnumerable<Point> points)
|
||||
: this()
|
||||
{
|
||||
bool hasPoint = false;
|
||||
double posX = double.MaxValue;
|
||||
double posY = double.MaxValue;
|
||||
double posX2 = double.MinValue;
|
||||
double posY2 = double.MinValue;
|
||||
|
||||
if (points != null)
|
||||
foreach (Point point in points)
|
||||
{
|
||||
hasPoint = true;
|
||||
posX = Math.Min(posX, point.X);
|
||||
posY = Math.Min(posY, point.Y);
|
||||
posX2 = Math.Max(posX2, point.X);
|
||||
posY2 = Math.Max(posY2, point.Y);
|
||||
}
|
||||
|
||||
(Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0));
|
||||
|
||||
Location = location;
|
||||
Size = size;
|
||||
Center = new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private static (Point location, Size size) InitializeFromPoints(Point point1, Point point2)
|
||||
{
|
||||
double posX = Math.Min(point1.X, point2.X);
|
||||
double posY = Math.Min(point1.Y, point2.Y);
|
||||
double width = Math.Max(point1.X, point2.X) - posX;
|
||||
double height = Math.Max(point1.Y, point2.Y) - posY;
|
||||
|
||||
return (new Point(posX, posY), new Size(width, height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Location"/>- and <see cref="Size"/>-position of this <see cref="Rectangle"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the <see cref="Location"/> and <see cref="Size"/> of this <see cref="Rectangle"/>. For example "[Location: [X: 100, Y: 10], Size: [Width: 20, Height: [40]]".</returns>
|
||||
public override string ToString() => $"[Location: {Location}, Size: {Size}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Rectangle" /> and is equivalent to this <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Rectangle" /> equivalent to this <see cref="Rectangle" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is Rectangle compareRect))
|
||||
return false;
|
||||
|
||||
if (GetType() != compareRect.GetType())
|
||||
return false;
|
||||
|
||||
return (Location == compareRect.Location) && (Size == compareRect.Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Rectangle" />.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = Location.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Size.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Rectangle" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="rectangle1">The first <see cref="Rectangle" /> to compare.</param>
|
||||
/// <param name="rectangle2">The second <see cref="Rectangle" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="rectangle1" /> and <paramref name="rectangle2" /> are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(Rectangle rectangle1, Rectangle rectangle2) => rectangle1.Equals(rectangle2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Rectangle" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="rectangle1">The first <see cref="Rectangle" /> to compare.</param>
|
||||
/// <param name="rectangle2">The second <see cref="Rectangle" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="rectangle1" /> and <paramref name="rectangle2" /> are not equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(Rectangle rectangle1, Rectangle rectangle2) => !(rectangle1 == rectangle2);
|
||||
|
||||
#endregion
|
||||
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the specified array of <see cref="T:RGB.NET.Core.Rectangle" />.
|
||||
/// The <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /> is calculated to completely contain all rectangles provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="rectangles">The array of <see cref="T:RGB.NET.Core.Rectangle" /> used to calculate the <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" />.</param>
|
||||
public Rectangle(params Rectangle[] rectangles)
|
||||
: this(rectangles.AsEnumerable())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Rectangle"/> class using the specified list of <see cref="Rectangle"/>.
|
||||
/// The <see cref="Location"/> and <see cref="Size"/> is calculated to completely contain all rectangles provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="rectangles">The list of <see cref="Rectangle"/> used to calculate the <see cref="Location"/> and <see cref="Size"/>.</param>
|
||||
public Rectangle(IEnumerable<Rectangle> rectangles)
|
||||
{
|
||||
bool hasPoint = false;
|
||||
float posX = float.MaxValue;
|
||||
float posY = float.MaxValue;
|
||||
float posX2 = float.MinValue;
|
||||
float posY2 = float.MinValue;
|
||||
|
||||
foreach (Rectangle rectangle in rectangles)
|
||||
{
|
||||
hasPoint = true;
|
||||
posX = Math.Min(posX, rectangle.Location.X);
|
||||
posY = Math.Min(posY, rectangle.Location.Y);
|
||||
posX2 = Math.Max(posX2, rectangle.Location.X + rectangle.Size.Width);
|
||||
posY2 = Math.Max(posY2, rectangle.Location.Y + rectangle.Size.Height);
|
||||
}
|
||||
|
||||
(Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0));
|
||||
Location = location;
|
||||
Size = size;
|
||||
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the specified array of <see cref="T:RGB.NET.Core.Point" />.
|
||||
/// The <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /> is calculated to contain all points provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="points">The array of <see cref="T:RGB.NET.Core.Point" /> used to calculate the <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" />.</param>
|
||||
public Rectangle(params Point[] points)
|
||||
: this(points.AsEnumerable())
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Rectangle" /> class using the specified list of <see cref="T:RGB.NET.Core.Point" />.
|
||||
/// The <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" /> is calculated to contain all points provided as parameters.
|
||||
/// </summary>
|
||||
/// <param name="points">The list of <see cref="T:RGB.NET.Core.Point" /> used to calculate the <see cref="P:RGB.NET.Core.Rectangle.Location" /> and <see cref="P:RGB.NET.Core.Rectangle.Size" />.</param>
|
||||
public Rectangle(IEnumerable<Point> points)
|
||||
: this()
|
||||
{
|
||||
bool hasPoint = false;
|
||||
float posX = float.MaxValue;
|
||||
float posY = float.MaxValue;
|
||||
float posX2 = float.MinValue;
|
||||
float posY2 = float.MinValue;
|
||||
|
||||
foreach (Point point in points)
|
||||
{
|
||||
hasPoint = true;
|
||||
posX = Math.Min(posX, point.X);
|
||||
posY = Math.Min(posY, point.Y);
|
||||
posX2 = Math.Max(posX2, point.X);
|
||||
posY2 = Math.Max(posY2, point.Y);
|
||||
}
|
||||
|
||||
(Point location, Size size) = hasPoint ? InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)) : InitializeFromPoints(new Point(0, 0), new Point(0, 0));
|
||||
|
||||
Location = location;
|
||||
Size = size;
|
||||
Center = new Point(Location.X + (Size.Width / 2.0f), Location.Y + (Size.Height / 2.0f));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private static (Point location, Size size) InitializeFromPoints(in Point point1, in Point point2)
|
||||
{
|
||||
float posX = Math.Min(point1.X, point2.X);
|
||||
float posY = Math.Min(point1.Y, point2.Y);
|
||||
float width = Math.Max(point1.X, point2.X) - posX;
|
||||
float height = Math.Max(point1.Y, point2.Y) - posY;
|
||||
|
||||
return (new Point(posX, posY), new Size(width, height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="Location"/>- and <see cref="Size"/>-position of this <see cref="Rectangle"/> to a human-readable string.
|
||||
/// </summary>
|
||||
/// <returns>A string that contains the <see cref="Location"/> and <see cref="Size"/> of this <see cref="Rectangle"/>. For example "[Location: [X: 100, Y: 10], Size: [Width: 20, Height: [40]]".</returns>
|
||||
public override string ToString() => $"[Location: {Location}, Size: {Size}]";
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the specified object is a <see cref="Rectangle" /> and is equivalent to this <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to test.</param>
|
||||
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Rectangle" /> equivalent to this <see cref="Rectangle" />; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not Rectangle compareRect)
|
||||
return false;
|
||||
|
||||
if (GetType() != compareRect.GetType())
|
||||
return false;
|
||||
|
||||
return (Location == compareRect.Location) && (Size == compareRect.Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <returns>An integer value that specifies the hash code for this <see cref="Rectangle" />.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = Location.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Size.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Rectangle" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="rectangle1">The first <see cref="Rectangle" /> to compare.</param>
|
||||
/// <param name="rectangle2">The second <see cref="Rectangle" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="rectangle1" /> and <paramref name="rectangle2" /> are equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(in Rectangle rectangle1, in Rectangle rectangle2) => rectangle1.Equals(rectangle2);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether two specified <see cref="Rectangle" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="rectangle1">The first <see cref="Rectangle" /> to compare.</param>
|
||||
/// <param name="rectangle2">The second <see cref="Rectangle" /> to compare.</param>
|
||||
/// <returns><c>true</c> if <paramref name="rectangle1" /> and <paramref name="rectangle2" /> are not equal; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(in Rectangle rectangle1, in Rectangle rectangle2) => !(rectangle1 == rectangle2);
|
||||
|
||||
// DarthAffe 20.02.2021: Used for normalization
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Rectangle"/> normalized to the specified reference.
|
||||
/// </summary>
|
||||
/// <param name="rectangle1">The rectangle to nromalize.</param>
|
||||
/// <param name="rectangle2">The reference used for normalization.</param>
|
||||
/// <returns>A normalized rectangle.</returns>
|
||||
public static Rectangle operator /(in Rectangle rectangle1, in Rectangle rectangle2)
|
||||
{
|
||||
float x = rectangle1.Location.X / (rectangle2.Size.Width - rectangle2.Location.X);
|
||||
float y = rectangle1.Location.Y / (rectangle2.Size.Height - rectangle2.Location.Y);
|
||||
Size size = rectangle1.Size / rectangle2.Size;
|
||||
return new Rectangle(new Point(x, y), size);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user