Reworked rendering

This commit is contained in:
DarthAffe committed 2021-02-21 15:49:05 +01:00
1 parent d5c9ec2e26
commit 13afc29987
47 files changed
+732 -642

No files matched your search

@@ -0,0 +1,172 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using System;
using RGB.NET.Core;
namespace RGB.NET.Presets.Decorators
{
/// <inheritdoc cref="AbstractUpdateAwareDecorator" />
/// <inheritdoc cref="IBrushDecorator" />
/// <summary>
/// Represents a decorator which allows to flash a brush by modifying his opacity.
/// </summary>
public class FlashDecorator : AbstractUpdateAwareDecorator, IBrushDecorator
{
#region Properties & Fields
/// <summary>
/// Gets or sets the attack-time (in seconds) of the decorator. (default: 0.2)<br />
/// This is close to a synthesizer envelope. (See <see href="http://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope" /> as reference)
/// </summary>
public double Attack { get; set; } = 0.2;
/// <summary>
/// Gets or sets the decay-time (in seconds) of the decorator. (default: 0)<br />
/// This is close to a synthesizer envelope. (See <see href="http://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope" /> as reference)
/// </summary>
public double Decay { get; set; } = 0;
/// <summary>
/// Gets or sets the sustain-time (in seconds) of the decorator. (default: 0.3)<br />
/// This is close to a synthesizer envelope. (See <see href="http://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope" /> as reference)<br />
/// Note that this value for naming reasons represents the time NOT the level.
/// </summary>
public double Sustain { get; set; } = 0.3;
/// <summary>
/// Gets or sets the release-time (in seconds) of the decorator. (default: 0.2)<br />
/// This is close to a synthesizer envelope. (See <see href="http://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope" /> as reference)
/// </summary>
public double Release { get; set; } = 0.2;
/// <summary>
/// Gets or sets the level to which the oppacity (percentage) should raise in the attack-cycle. (default: 1);
/// </summary>
public double AttackValue { get; set; } = 1;
/// <summary>
/// Gets or sets the level at which the oppacity (percentage) should stay in the sustain-cycle. (default: 1);
/// </summary>
public double SustainValue { get; set; } = 1;
/// <summary>
/// Gets or sets the level at which the oppacity (percentage) should stay in the pause-cycle. (default: 0);
/// </summary>
public double PauseValue { get; set; } = 0;
/// <summary>
/// Gets or sets the interval (in seconds) in which the decorator should repeat (if repetition is enabled). (default: 1)
/// </summary>
public double Interval { get; set; } = 1;
/// <summary>
/// Gets or sets the amount of repetitions the decorator should do until it's finished. Zero means infinite. (default: 0)
/// </summary>
public int Repetitions { get; set; } = 0;
private ADSRPhase _currentPhase;
private double _currentPhaseValue;
private int _repetitionCount;
private double _currentValue;
#endregion
#region Constructors
public FlashDecorator(RGBSurface surface, bool updateIfDisabled = false)
: base(surface, updateIfDisabled)
{ }
#endregion
#region Methods
/// <inheritdoc />
public void ManipulateColor(in Rectangle rectangle, in RenderTarget renderTarget, ref Color color) => color = color.SetA(_currentValue);
/// <inheritdoc />
protected override void Update(double deltaTime)
{
_currentPhaseValue -= deltaTime;
// Using ifs instead of a switch allows to skip phases with time 0.
// ReSharper disable InvertIf
if (_currentPhase == ADSRPhase.Attack)
if (_currentPhaseValue > 0)
_currentValue = PauseValue + (Math.Min(1, (Attack - _currentPhaseValue) / Attack) * (AttackValue - PauseValue));
else
{
_currentPhaseValue = Decay;
_currentPhase = ADSRPhase.Decay;
}
if (_currentPhase == ADSRPhase.Decay)
if (_currentPhaseValue > 0)
_currentValue = SustainValue + (Math.Min(1, _currentPhaseValue / Decay) * (AttackValue - SustainValue));
else
{
_currentPhaseValue = Sustain;
_currentPhase = ADSRPhase.Sustain;
}
if (_currentPhase == ADSRPhase.Sustain)
if (_currentPhaseValue > 0)
_currentValue = SustainValue;
else
{
_currentPhaseValue = Release;
_currentPhase = ADSRPhase.Release;
}
if (_currentPhase == ADSRPhase.Release)
if (_currentPhaseValue > 0)
_currentValue = PauseValue + (Math.Min(1, _currentPhaseValue / Release) * (SustainValue - PauseValue));
else
{
_currentPhaseValue = Interval;
_currentPhase = ADSRPhase.Pause;
}
if (_currentPhase == ADSRPhase.Pause)
if (_currentPhaseValue > 0)
_currentValue = PauseValue;
else
{
if ((++_repetitionCount >= Repetitions) && (Repetitions > 0))
Detach();
_currentPhaseValue = Attack;
_currentPhase = ADSRPhase.Attack;
}
// ReSharper restore InvertIf
}
/// <inheritdoc cref="AbstractUpdateAwareDecorator.OnAttached" />
/// <inheritdoc cref="IDecorator.OnAttached" />
public override void OnAttached(IDecoratable decoratable)
{
base.OnAttached(decoratable);
_currentPhase = ADSRPhase.Attack;
_currentPhaseValue = Attack;
_repetitionCount = 0;
_currentValue = 0;
}
#endregion
// ReSharper disable once InconsistentNaming
private enum ADSRPhase
{
Attack,
Decay,
Sustain,
Release,
Pause
}
}
}
@@ -0,0 +1,11 @@
using RGB.NET.Core;
namespace RGB.NET.Presets.Decorators
{
/// <inheritdoc />
/// <summary>
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Presets.Gradients.IGradient" />.
/// </summary>
public interface IGradientDecorator : IDecorator
{ }
}
@@ -0,0 +1,73 @@
using RGB.NET.Core;
using RGB.NET.Presets.Textures.Gradients;
namespace RGB.NET.Presets.Decorators
{
/// <inheritdoc cref="AbstractUpdateAwareDecorator" />
/// <inheritdoc cref="IGradientDecorator" />
/// <summary>
/// Represents a decorator which allows to move an <see cref="T:RGB.NET.Presets.Gradients.IGradient" /> by modifying his offset.
/// </summary>
public class MoveGradientDecorator : AbstractUpdateAwareDecorator, IGradientDecorator
{
#region Properties & Fields
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable MemberCanBePrivate.Global
/// <summary>
/// Gets or sets the direction the <see cref="IGradient"/> is moved.
/// True leads to an offset-increment (normaly moving to the right), false to an offset-decrement (normaly moving to the left).
/// </summary>
public bool Direction { get; set; }
/// <summary>
/// Gets or sets the speed of the movement in units per second.
/// The meaning of units differs for the different <see cref="IGradient"/>, but 360 units will always be one complete cycle:
/// <see cref="LinearGradient"/>: 360 unit = 1 offset.
/// <see cref="RainbowGradient"/>: 1 unit = 1 degree.
/// </summary>
public double Speed { get; set; }
// ReSharper restore MemberCanBePrivate.Global
// ReSharper restore AutoPropertyCanBeMadeGetOnly.Global
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Decorators.MoveGradientDecorator" /> class.
/// </summary>
/// <param name="speed">The speed of the movement in units per second.
/// The meaning of units differs for the different <see cref="T:RGB.NET.Presets.Gradients.IGradient" /> but 360 units will always be one complete cycle:
/// <see cref="T:RGB.NET.Presets.Gradients.LinearGradient" />: 360 unit = 1 offset.
/// <see cref="T:RGB.NET.Presets.Gradients.RainbowGradient" />: 1 unit = 1 degree.</param>
/// <param name="direction">The direction the <see cref="T:RGB.NET.Presets.Gradients.IGradient" /> is moved.
/// True leads to an offset-increment (normaly moving to the right), false to an offset-decrement (normaly moving to the left).</param>
public MoveGradientDecorator(RGBSurface surface, double speed = 180.0, bool direction = true)
: base(surface)
{
this.Speed = speed;
this.Direction = direction;
}
#endregion
#region Methods
/// <inheritdoc />
protected override void Update(double deltaTime)
{
double movement = Speed * deltaTime;
if (!Direction)
movement = -movement;
foreach (IDecoratable decoratedObject in DecoratedObjects)
if (decoratedObject is IGradient gradient)
gradient.Move(movement);
}
#endregion
}
}
@@ -0,0 +1,59 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using RGB.NET.Core;
namespace RGB.NET.Presets.Groups
{
/// <summary>
/// Offers some extensions and helper-methods for <see cref="ILedGroup"/> related things.
/// </summary>
public static class LedGroupExtension
{
/// <summary>
/// Converts the given <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 ListLedGroup listLedGroup))
{
if (ledGroup.Surface != null)
ledGroup.Detach(ledGroup.Surface);
listLedGroup = new ListLedGroup(ledGroup.Surface, ledGroup.GetLeds()) { Brush = ledGroup.Brush };
}
return listLedGroup;
}
/// <summary>
/// Returns a new <see cref="ListLedGroup" /> which contains all <see cref="Led"/> from the given <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 given <see cref="ILedGroup"/> to 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 attached; otherwise, <c>false</c>.</returns>
public static bool Attach(this ILedGroup ledGroup, RGBSurface surface) => surface.AttachLedGroup(ledGroup);
/// <summary>
/// Detaches the given <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, RGBSurface surface) => surface.DetachLedGroup(ledGroup);
}
}
+126
View File
@@ -0,0 +1,126 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
namespace RGB.NET.Presets.Groups
{
/// <inheritdoc />
/// <summary>
/// Represents a <see cref="T:RGB.NET.Presets.Groups.RectangleLedGroup" /> containing <see cref="T:RGB.NET.Core.Led" /> which physically lay inside a <see cref="T:RGB.NET.Core.Rectangle" />.
/// </summary>
public class RectangleLedGroup : AbstractLedGroup
{
#region Properties & Fields
private IList<Led>? _ledCache;
private Rectangle _rectangle;
/// <summary>
/// Gets or sets the <see cref="Core.Rectangle"/> the <see cref="Led"/> should be taken from.
/// </summary>
public Rectangle Rectangle
{
get => _rectangle;
set
{
if (SetProperty(ref _rectangle, value))
InvalidateCache();
}
}
private double _minOverlayPercentage;
/// <summary>
/// Gets or sets the minimal percentage overlay a <see cref="Led"/> must have with the <see cref="Core.Rectangle" /> to be taken into the <see cref="RectangleLedGroup"/>.
/// </summary>
public double MinOverlayPercentage
{
get => _minOverlayPercentage;
set
{
if (SetProperty(ref _minOverlayPercentage, value))
InvalidateCache();
}
}
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> class.
/// </summary>
/// <param name="fromLed">They first <see cref="T:RGB.NET.Core.Led" /> to calculate the <see cref="T:RGB.NET.Core.Rectangle" /> of this <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> from.</param>
/// <param name="toLed">They second <see cref="T:RGB.NET.Core.Led" /> to calculate the <see cref="T:RGB.NET.Core.Rectangle" /> of this <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> from.</param>
/// <param name="minOverlayPercentage">(optional) The minimal percentage overlay a <see cref="T:RGB.NET.Core.Led" /> must have with the <see cref="P:RGB.NET.Groups.RectangleLedGroup.Rectangle" /> to be taken into the <see cref="T:RGB.NET.Groups.RectangleLedGroup" />. (default: 0.5)</param>
/// <param name="autoAttach">(optional) Specifies whether this <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> should be automatically attached or not. (default: true)</param>
public RectangleLedGroup(RGBSurface? surface, Led fromLed, Led toLed, double minOverlayPercentage = 0.5)
: this(surface, new Rectangle(fromLed.Boundary, toLed.Boundary), minOverlayPercentage)
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> class.
/// </summary>
/// <param name="fromPoint">They first point to calculate the <see cref="T:RGB.NET.Core.Rectangle" /> of this <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> from.</param>
/// <param name="toPoint">They second point to calculate the <see cref="T:RGB.NET.Core.Rectangle" /> of this <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> from.</param>
/// <param name="minOverlayPercentage">(optional) The minimal percentage overlay a <see cref="T:RGB.NET.Core.Led" /> must have with the <see cref="P:RGB.NET.Groups.RectangleLedGroup.Rectangle" /> to be taken into the <see cref="T:RGB.NET.Groups.RectangleLedGroup" />. (default: 0.5)</param>
/// <param name="autoAttach">(optional) Specifies whether this <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> should be automatically attached or not. (default: true)</param>
public RectangleLedGroup(RGBSurface? surface, Point fromPoint, Point toPoint, double minOverlayPercentage = 0.5)
: this(surface, new Rectangle(fromPoint, toPoint), minOverlayPercentage)
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> class.
/// </summary>
/// <param name="rectangle">The <see cref="T:RGB.NET.Core.Rectangle" /> of this <see cref="T:RGB.NET.Groups.RectangleLedGroup" />.</param>
/// <param name="minOverlayPercentage">(optional) The minimal percentage overlay a <see cref="T:RGB.NET.Core.Led" /> must have with the <see cref="P:RGB.NET.Groups.RectangleLedGroup.Rectangle" /> to be taken into the <see cref="T:RGB.NET.Groups.RectangleLedGroup" />. (default: 0.5)</param>
/// <param name="autoAttach">(optional) Specifies whether this <see cref="T:RGB.NET.Groups.RectangleLedGroup" /> should be automatically attached or not. (default: true)</param>
public RectangleLedGroup(RGBSurface? surface, Rectangle rectangle, double minOverlayPercentage = 0.5)
: base(surface)
{
this.Rectangle = rectangle;
this.MinOverlayPercentage = minOverlayPercentage;
}
#endregion
#region Methods
/// <inheritdoc />
public override void OnAttach(RGBSurface surface)
{
base.OnAttach(surface);
if (Surface != null)
Surface.SurfaceLayoutChanged += RGBSurfaceOnSurfaceLayoutChanged;
}
/// <inheritdoc />
public override void OnDetach(RGBSurface surface)
{
if (Surface != null)
Surface.SurfaceLayoutChanged -= RGBSurfaceOnSurfaceLayoutChanged;
base.OnDetach(surface);
}
private void RGBSurfaceOnSurfaceLayoutChanged(SurfaceLayoutChangedEventArgs args) => InvalidateCache();
/// <inheritdoc />
/// <summary>
/// Gets a list containing all <see cref="T:RGB.NET.Core.Led" /> of this <see cref="T:RGB.NET.Presets.Groups.RectangleLedGroup" />.
/// </summary>
/// <returns>The list containing all <see cref="T:RGB.NET.Core.Led" /> of this <see cref="T:RGB.NET.Presets.Groups.RectangleLedGroup" />.</returns>
public override IList<Led> GetLeds() => _ledCache ??= (Surface?.Leds.Where(led => led.AbsoluteBoundary.CalculateIntersectPercentage(Rectangle) >= MinOverlayPercentage).ToList() ?? new List<Led>());
private void InvalidateCache() => _ledCache = null;
#endregion
}
}
+86
View File
@@ -0,0 +1,86 @@
// ReSharper disable MemberCanBePrivate.Global
using System;
using RGB.NET.Core;
namespace RGB.NET.Presets.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(in Point startPoint, in Point endPoint, in Point point)
{
Point intersectingPoint;
if (startPoint.Y.EqualsInTolerance(endPoint.Y)) // Horizontal case
intersectingPoint = new Point(point.X, startPoint.Y);
else if (startPoint.X.EqualsInTolerance(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(in Point point, in Point origin, in 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.EqualsInTolerance(origin.Y)) && (point.X < origin.X) && (direction.X > origin.X))
|| ((point.Y.EqualsInTolerance(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(in Point point1, in Point point2)
{
double x = point1.X - point2.X;
double y = point1.Y - point2.Y;
return Math.Sqrt((y * y) + (x * x));
}
#endregion
}
}
+56
View File
@@ -0,0 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Authors>Darth Affe</Authors>
<Company>Wyrez</Company>
<Language>en-US</Language>
<NeutralLanguage>en-US</NeutralLanguage>
<Title>RGB.NET.Presets</Title>
<AssemblyName>RGB.NET.Presets</AssemblyName>
<AssemblyTitle>RGB.NET.Presets</AssemblyTitle>
<PackageId>RGB.NET.Presets</PackageId>
<RootNamespace>RGB.NET.Presets</RootNamespace>
<Description>Presets-Presets of RGB.NET</Description>
<Summary>Presets for RGB.NET, a C# (.NET) library for accessing various RGB-peripherals</Summary>
<Copyright>Copyright © Darth Affe 2020</Copyright>
<PackageCopyright>Copyright © Darth Affe 2020</PackageCopyright>
<PackageIconUrl>http://lib.arge.be/icon.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/DarthAffe/RGB.NET</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE</PackageLicenseUrl>
<RepositoryType>Github</RepositoryType>
<RepositoryUrl>https://github.com/DarthAffe/RGB.NET</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageReleaseNotes></PackageReleaseNotes>
<Version>0.0.1</Version>
<AssemblyVersion>0.0.1</AssemblyVersion>
<FileVersion>0.0.1</FileVersion>
<OutputPath>..\bin\</OutputPath>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RGB.NET.Core\RGB.NET.Core.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=brushes/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=decorators/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=groups_005Cextensions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -0,0 +1,42 @@
using RGB.NET.Core;
using RGB.NET.Presets.Textures.Gradients;
namespace RGB.NET.Presets.Textures
{
public abstract class AbstractGradientTexture : AbstractBindable, ITexture
{
#region Properties & Fields
/// <summary>
/// Gets the gradient drawn by the brush.
/// </summary>
public IGradient Gradient { get; }
/// <inheritdoc />
public Size Size { get; }
/// <inheritdoc />
public Color this[in Point point] => GetColor(point);
/// <inheritdoc />
public Color this[in Rectangle rectangle] => GetColor(rectangle.Center);
#endregion
#region Constructors
protected AbstractGradientTexture(Size size, IGradient gradient)
{
this.Size = size;
this.Gradient = gradient;
}
#endregion
#region Methods
protected abstract Color GetColor(in Point point);
#endregion
}
}
@@ -0,0 +1,98 @@
// 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.Core;
using RGB.NET.Presets.Textures.Gradients;
namespace RGB.NET.Presets.Textures
{
/// <inheritdoc />
/// <summary>
/// Represents a brush drawing a conical gradient.
/// </summary>
public sealed class ConicalGradientTexture : AbstractGradientTexture
{
#region Constants
private const double PI2 = Math.PI * 2;
#endregion
#region Properties & Fields
private double _origin = Math.Atan2(-1, 0);
/// <summary>
/// Gets or sets the origin (radian-angle) this <see cref="ConicalGradientTexture"/> is drawn to. (default: -π/2)
/// </summary>
public double Origin
{
get => _origin;
set => SetProperty(ref _origin, value);
}
private Point _center = new(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="ConicalGradientTexture"/>. (default: 0.5, 0.5)
/// </summary>
public Point Center
{
get => _center;
set => SetProperty(ref _center, value);
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.ConicalGradientTexture" /> class.
/// </summary>
/// <param name="gradient">The <see cref="T:RGB.NET.Presets.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Presets.Textures.ConicalGradientTexture" />.</param>
public ConicalGradientTexture(Size size, IGradient gradient)
: base(size, gradient)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.ConicalGradientTexture" /> 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.Presets.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Presets.Textures.ConicalGradientTexture" />.</param>
public ConicalGradientTexture(Size size, IGradient gradient, Point center)
: base(size, gradient)
{
this.Center = center;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.ConicalGradientTexture" /> 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.Presets.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Presets.Textures.ConicalGradientTexture" />.</param>
public ConicalGradientTexture(Size size, IGradient gradient, Point center, float origin)
: base(size, gradient)
{
this.Center = center;
this.Origin = origin;
}
#endregion
#region Methods
protected override Color GetColor(in Point point)
{
double angle = Math.Atan2(point.Y - Center.Y, point.X - Center.X) - Origin;
if (angle < 0) angle += PI2;
double offset = angle / PI2;
return Gradient.GetColor(offset);
}
#endregion
}
}
@@ -0,0 +1,151 @@
// 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;
using RGB.NET.Presets.Decorators;
namespace RGB.NET.Presets.Textures.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();
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 += (_, _) => 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 += (_, _) => 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 += (_, _) => 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(stop => stop.Offset);
if (offset > max)
return max;
double min = GradientStops.Min(stop => stop.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(stop => stop.Offset > 1))
foreach (GradientStop gradientStop in GradientStops)
gradientStop.Offset -= 1;
while (GradientStops.All(stop => stop.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, EventArgs.Empty);
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
}
}
@@ -0,0 +1,52 @@
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable MemberCanBePrivate.Global
using RGB.NET.Core;
namespace RGB.NET.Presets.Textures.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
}
}
@@ -0,0 +1,30 @@
using System;
using RGB.NET.Core;
using RGB.NET.Presets.Decorators;
namespace RGB.NET.Presets.Textures.Gradients
{
/// <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);
}
}
@@ -0,0 +1,152 @@
// ReSharper disable UnusedMember.Global
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using RGB.NET.Core;
namespace RGB.NET.Presets.Textures.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 = new();
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.Gradients.LinearGradient" /> class.
/// </summary>
public LinearGradient()
{
Initialize();
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.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.Presets.Textures.Gradients.AbstractGradient" /> class.
/// </summary>
/// <param name="wrapGradient">Specifies whether the gradient should wrapp or not (see <see cref="P:RGB.NET.Presets.Textures.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 += (_, 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(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
}
}
@@ -0,0 +1,109 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using System;
using RGB.NET.Core;
using RGB.NET.Presets.Decorators;
namespace RGB.NET.Presets.Textures.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 += (_, _) => 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, EventArgs.Empty);
#endregion
}
}
@@ -0,0 +1,79 @@
// 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.Core;
using RGB.NET.Presets.Helper;
using RGB.NET.Presets.Textures.Gradients;
namespace RGB.NET.Presets.Textures
{
/// <inheritdoc />
/// <summary>
/// Represents a brush drawing a linear gradient.
/// </summary>
public sealed class LinearGradientTexture : AbstractGradientTexture
{
#region Properties & Fields
private Point _startPoint = new(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="LinearGradientTexture"/>. (default: 0.0, 0.5)
/// </summary>
public Point StartPoint
{
get => _startPoint;
set => SetProperty(ref _startPoint, value);
}
private Point _endPoint = new(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="LinearGradientTexture"/>. (default: 1.0, 0.5)
/// </summary>
public Point EndPoint
{
get => _endPoint;
set => SetProperty(ref _endPoint, value);
}
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.LinearGradientTexture" /> class.
/// </summary>
/// <param name="gradient">The <see cref="T:RGB.NET.Presets.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Presets.Textures.LinearGradientTexture" />.</param>
public LinearGradientTexture(Size size, IGradient gradient)
: base(size, gradient)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.LinearGradientTexture" /> 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.Presets.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Presets.Textures.LinearGradientTexture" />.</param>
public LinearGradientTexture(Size size, IGradient gradient, Point startPoint, Point endPoint)
: base(size, gradient)
{
this.StartPoint = startPoint;
this.EndPoint = endPoint;
}
#endregion
#region Methods
protected override Color GetColor(in Point point)
{
double offset = GradientHelper.CalculateLinearGradientOffset(StartPoint, EndPoint, point);
return Gradient.GetColor(offset);
}
#endregion
}
}
@@ -0,0 +1,78 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using RGB.NET.Core;
using RGB.NET.Presets.Helper;
using RGB.NET.Presets.Textures.Gradients;
namespace RGB.NET.Presets.Textures
{
/// <inheritdoc />
/// <summary>
/// Represents a brush drawing a radial gradient around a center point.
/// </summary>
public sealed class RadialGradientTexture : AbstractGradientTexture
{
#region Properties & Fields
private double _referenceDistance = GradientHelper.CalculateDistance(new Point(0.5, 0.5), new Point(0, 0));
private Point _center = new(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="RadialGradientTexture"/> should be drawn. (default: 0.5, 0.5)
/// </summary>
public Point Center
{
get => _center;
set
{
if (SetProperty(ref _center, value))
CalculateReferenceDistance();
}
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.RadialGradientTexture" /> class.
/// </summary>
/// <param name="gradient">The gradient drawn by the brush.</param>
public RadialGradientTexture(Size size, IGradient gradient)
: base(size, gradient)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Presets.Textures.RadialGradientTexture" /> 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 RadialGradientTexture(Size size, IGradient gradient, Point center)
: base(size, gradient)
{
this.Center = center;
}
#endregion
#region Methods
private void CalculateReferenceDistance()
{
double referenceX = Center.X < 0.5 ? 1 : 0;
double referenceY = Center.Y < 0.5 ? 1 : 0;
_referenceDistance = GradientHelper.CalculateDistance(new Point(referenceX, referenceY), Center);
}
protected override Color GetColor(in Point point)
{
double distance = GradientHelper.CalculateDistance(point, Center);
double offset = distance / _referenceDistance;
return Gradient.GetColor(offset);
}
#endregion
}
}