diff --git a/RGB.NET.Core/Color.cs b/RGB.NET.Core/Color.cs new file mode 100644 index 0000000..e782750 --- /dev/null +++ b/RGB.NET.Core/Color.cs @@ -0,0 +1,496 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Diagnostics; +using RGB.NET.Core.Extensions; +using RGB.NET.Core.MVVM; + +namespace RGB.NET.Core +{ + /// + /// Represents an ARGB (alpha, red, green, blue) color. + /// + [DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}, H: {Hue}, S: {Saturation}, V: {Value}]")] + public class Color : AbstractBindable + { + #region Constants + + /// + /// Gets an transparent color [A: 0, R: 0, G: 0, B: 0] + /// + public static Color Transparent => new Color(); + + #endregion + + #region Properties & Fields + + #region RGB + + private byte _a; + /// + /// Gets or sets the alpha component value of this as byte in the range [0..255]. + /// + public byte A + { + get { return _a; } + set + { + if (SetProperty(ref _a, value)) + // ReSharper disable once ExplicitCallerInfoArgument + OnPropertyChanged(nameof(APercent)); + } + } + + /// + /// Gets or sets the alpha component value of this as percentage in the range [0..1]. + /// + public double APercent + { + get { return A / (double)byte.MaxValue; } + set { A = GetByteValueFromPercentage(value); } + } + + private byte _r; + /// + /// Gets or sets the red component value of this as byte in the range [0..255]. + /// + public byte R + { + get { return _r; } + set + { + if (SetProperty(ref _r, value)) + { + InvalidateHSV(); + // ReSharper disable once ExplicitCallerInfoArgument + OnPropertyChanged(nameof(RPercent)); + } + } + } + + /// + /// Gets or sets the red component value of this as percentage in the range [0..1]. + /// + public double RPercent + { + get { return R / (double)byte.MaxValue; } + set { R = GetByteValueFromPercentage(value); } + } + + private byte _g; + /// + /// Gets or sets the green component value of this as byte in the range [0..255]. + /// + public byte G + { + get { return _g; } + set + { + if (SetProperty(ref _g, value)) + { + InvalidateHSV(); + // ReSharper disable ExplicitCallerInfoArgument + OnPropertyChanged(nameof(GPercent)); + // ReSharper restore ExplicitCallerInfoArgument + } + } + } + + /// + /// Gets or sets the green component value of this as percentage in the range [0..1]. + /// + public double GPercent + { + get { return G / (double)byte.MaxValue; } + set { G = GetByteValueFromPercentage(value); } + } + + private byte _b; + /// + /// Gets or sets the blue component value of this as byte in the range [0..255]. + /// + public byte B + { + get { return _b; } + set + { + if (SetProperty(ref _b, value)) + { + InvalidateHSV(); + // ReSharper disable ExplicitCallerInfoArgument + OnPropertyChanged(nameof(BPercent)); + // ReSharper restore ExplicitCallerInfoArgument + } + } + } + + /// + /// Gets or sets the blue component value of this as percentage in the range [0..1]. + /// + public double BPercent + { + get { return B / (double)byte.MaxValue; } + set { B = GetByteValueFromPercentage(value); } + } + + #endregion + + #region HSV + + private double? _hue; + /// + /// Gets or sets the hue component value (HSV-color space) of this as degree in the range [0..360]. + /// + public double Hue + { + get { return _hue ?? (_hue = CaclulateHueFromRGB()).Value; } + set + { + if (SetProperty(ref _hue, value)) + UpdateRGBFromHSV(); + } + } + + private double? _saturation; + /// + /// Gets or sets the saturation component value (HSV-color space) of this as degree in the range [0..1]. + /// + public double Saturation + { + get { return _saturation ?? (_saturation = CaclulateSaturationFromRGB()).Value; } + set + { + if (SetProperty(ref _saturation, value)) + UpdateRGBFromHSV(); + } + } + + private double? _value; + /// + /// Gets or sets the value component value (HSV-color space) of this as degree in the range [0..1]. + /// + public double Value + { + get { return _value ?? (_value = CaclulateValueFromRGB()).Value; } + set + { + if (SetProperty(ref _value, value)) + UpdateRGBFromHSV(); + } + } + + #endregion + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// The class created by this constructor equals . + /// + public Color() + : this(0, 0, 0) + { } + + /// + /// Initializes a new instance of the class using only RGB-Values. + /// Alpha defaults to 255. + /// + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(byte r, byte g, byte b) + : this(255, r, g, b) + { } + + /// + /// Initializes a new instance of the class using ARGB-values. + /// + /// The alpha component value of this . + /// The red component value of this . + /// The green component value of this . + /// The blue component value of this . + public Color(byte a, byte r, byte g, byte b) + { + this.A = a; + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Initializes a new instance of the class using only RGB-Values. + /// Alpha defaults to 255. + /// + /// The hue component value of this . + /// The saturation component value of this . + /// The value component value of this . + public Color(double hue, double saturation, double value) + : this(255, hue, saturation, value) + { } + + /// + /// Initializes a new instance of the class using ARGB-values. + /// + /// The alpha component value of this . + /// The hue component value of this . + /// The saturation component value of this . + /// The value component value of this . + public Color(byte a, double hue, double saturation, double value) + { + this.A = a; + this._hue = hue; + this._saturation = saturation; + this._value = value; + + // ReSharper disable ExplicitCallerInfoArgument + // ReSharper disable VirtualMemberCallInConstructor + OnPropertyChanged(nameof(Hue)); + OnPropertyChanged(nameof(Saturation)); + OnPropertyChanged(nameof(Value)); + // ReSharper restore VirtualMemberCallInConstructor + // ReSharper restore ExplicitCallerInfoArgument + + UpdateRGBFromHSV(); + } + + /// + /// Initializes a new instance of the class by cloning a existing . + /// + /// The the values are copied from. + public Color(Color color) + : this(color.A, color.R, color.G, color.B) + { } + + #endregion + + #region Methods + + /// + /// Blends a over this color. + /// + /// The to blend. + public void Blend(Color color) + { + if (color.A == 0) return; + + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (color.A == 255) + { + A = color.A; + R = color.A; + G = color.A; + B = color.A; + } + else + { + double resultA = (1.0 - ((1.0 - color.APercent) * (1.0 - APercent))); + double resultR = (((color.RPercent * color.APercent) / resultA) + ((RPercent * APercent * (1.0 - color.APercent)) / resultA)); + double resultG = (((color.GPercent * color.APercent) / resultA) + ((GPercent * APercent * (1.0 - color.APercent)) / resultA)); + double resultB = (((color.BPercent * color.APercent) / resultA) + ((BPercent * APercent * (1.0 - color.APercent)) / resultA)); + + APercent = resultA; + RPercent = resultR; + GPercent = resultG; + BPercent = resultB; + } + } + + private void InvalidateHSV() + { + _hue = null; + _saturation = null; + _value = null; + + // ReSharper disable ExplicitCallerInfoArgument + OnPropertyChanged(nameof(Hue)); + OnPropertyChanged(nameof(Saturation)); + OnPropertyChanged(nameof(Value)); + // ReSharper restore ExplicitCallerInfoArgument + } + + private double CaclulateHueFromRGB() + { + if ((R == G) && (G == B)) return 0.0; + + double min = Math.Min(Math.Min(R, G), B); + double max = Math.Max(Math.Max(R, G), B); + + double hue; + 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; + if (hue < 0.0) + hue += 360.0; + + return hue; + } + + private double CaclulateSaturationFromRGB() + { + int max = Math.Max(R, Math.Max(G, B)); + int min = Math.Min(R, Math.Min(G, B)); + + return (max == 0) ? 0 : 1.0 - (min / (double)max); + } + + private double CaclulateValueFromRGB() + { + return Math.Max(R, Math.Max(G, B)) / 255.0; + } + + private void UpdateRGBFromHSV() + { + if (Saturation <= 0.0) + { + byte val = GetByteValueFromPercentage(Value); + UpdateRGBWithoutInvalidatingHSV(val, val, val); + } + + double hh = (Hue % 360.0) / 60.0; + int i = (int)hh; + double ff = hh - i; + double p = Value * (1.0 - Saturation); + double q = Value * (1.0 - (Saturation * ff)); + double t = Value * (1.0 - (Saturation * (1.0 - ff))); + + switch (i) + { + case 0: + UpdateRGBWithoutInvalidatingHSV(GetByteValueFromPercentage(Value), + GetByteValueFromPercentage(t), + GetByteValueFromPercentage(p)); + break; + case 1: + UpdateRGBWithoutInvalidatingHSV(GetByteValueFromPercentage(q), + GetByteValueFromPercentage(Value), + GetByteValueFromPercentage(p)); + break; + case 2: + UpdateRGBWithoutInvalidatingHSV(GetByteValueFromPercentage(p), + GetByteValueFromPercentage(Value), + GetByteValueFromPercentage(t)); + break; + case 3: + UpdateRGBWithoutInvalidatingHSV(GetByteValueFromPercentage(p), + GetByteValueFromPercentage(q), + GetByteValueFromPercentage(Value)); + break; + case 4: + UpdateRGBWithoutInvalidatingHSV(GetByteValueFromPercentage(t), + GetByteValueFromPercentage(p), + GetByteValueFromPercentage(Value)); + break; + default: + UpdateRGBWithoutInvalidatingHSV(GetByteValueFromPercentage(Value), + GetByteValueFromPercentage(p), + GetByteValueFromPercentage(q)); + break; + } + } + + private void UpdateRGBWithoutInvalidatingHSV(byte r, byte g, byte b) + { + _r = r; + _g = g; + _b = b; + + // ReSharper disable ExplicitCallerInfoArgument + OnPropertyChanged(nameof(R)); + OnPropertyChanged(nameof(RPercent)); + OnPropertyChanged(nameof(G)); + OnPropertyChanged(nameof(GPercent)); + OnPropertyChanged(nameof(B)); + OnPropertyChanged(nameof(BPercent)); + // ReSharper enable ExplicitCallerInfoArgument + } + + private static byte GetByteValueFromPercentage(double percentage) + { + if (double.IsNaN(percentage)) return 0; + + percentage = percentage.Clamp(0, 1.0); + return (byte)(percentage.Equals(1.0) ? 255 : percentage * 256.0); + } + + /// + /// Converts the individual byte-values of this to a human-readable string. + /// + /// A string that contains the individual byte-values of this . For example "[A: 255, R: 255, G: 0, B: 0]". + public override string ToString() + { + return $"[A: {A}, R: {R}, G: {G}, B: {B}, H: {Hue}, S: {Saturation}, V: {Value}]"; + } + + /// + /// Tests whether the specified object is a and is equivalent to this . + /// + /// The object to test. + /// true if is a equivalent to this ; otherwise, false. + public override bool Equals(object obj) + { + Color compareColor = obj as Color; + if (ReferenceEquals(compareColor, null)) + return false; + + if (ReferenceEquals(this, compareColor)) + return true; + + if (GetType() != compareColor.GetType()) + return false; + + return (compareColor.A == A) && (compareColor.R == R) && (compareColor.G == G) && (compareColor.B == B); + } + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() + { + unchecked + { + int hashCode = A.GetHashCode(); + hashCode = (hashCode * 397) ^ R.GetHashCode(); + hashCode = (hashCode * 397) ^ G.GetHashCode(); + hashCode = (hashCode * 397) ^ B.GetHashCode(); + return hashCode; + } + } + + #endregion + + #region Operators + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are equal; otherwise, false. + public static bool operator ==(Color color1, Color color2) + { + return ReferenceEquals(color1, null) ? ReferenceEquals(color2, null) : color1.Equals(color2); + } + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are not equal; otherwise, false. + public static bool operator !=(Color color1, Color color2) + { + return !(color1 == color2); + } + + #endregion + } +} diff --git a/RGB.NET.Core/Devices/IRGBDevice.cs b/RGB.NET.Core/Devices/IRGBDevice.cs new file mode 100644 index 0000000..cb6d363 --- /dev/null +++ b/RGB.NET.Core/Devices/IRGBDevice.cs @@ -0,0 +1,8 @@ +namespace RGB.NET.Core.Devices +{ + /// + /// Represents a generic RGB-device + /// + public interface IRGBDevice + { } +} diff --git a/RGB.NET.Core/Extensions/DoubleExtensions.cs b/RGB.NET.Core/Extensions/DoubleExtensions.cs new file mode 100644 index 0000000..87e8876 --- /dev/null +++ b/RGB.NET.Core/Extensions/DoubleExtensions.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.CompilerServices; + +namespace RGB.NET.Core.Extensions +{ + /// + /// Offers some extensions and helper-methods for the work with doubles + /// + public static class DoubleExtensions + { + #region Constants + + /// + /// Defines the precision RGB.NET processes floating point comparisons in. + /// + public const double TOLERANCE = 1E-10; + + #endregion + + #region Methods + + /// + /// Checks if two values are equal respecting the . + /// + /// The first value to compare. + /// The first value to compare. + /// true if the difference is smaller than the ; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool EqualsInTolerance(this double value1, double value2) + { + return Math.Abs(value1 - value2) < TOLERANCE; + } + + /// + /// Camps the provided value to be bigger or equal min and smaller or equal max. + /// + /// The value to clamp. + /// The lower value of the range the value is clamped to. + /// The higher value of the range the value is clamped to. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(this double value, double min, double max) + { + return Math.Max(min, Math.Min(max, value)); + } + + #endregion + } +} diff --git a/RGB.NET.Core/ILedId.cs b/RGB.NET.Core/ILedId.cs new file mode 100644 index 0000000..373ccc5 --- /dev/null +++ b/RGB.NET.Core/ILedId.cs @@ -0,0 +1,13 @@ +namespace RGB.NET.Core +{ + /// + /// Represents a generic Id of a . + /// + public interface ILedId + { + /// + /// Gets a value indicating if this is valid. + /// + bool IsValid { get; } + } +} diff --git a/RGB.NET.Core/Led.cs b/RGB.NET.Core/Led.cs new file mode 100644 index 0000000..2840054 --- /dev/null +++ b/RGB.NET.Core/Led.cs @@ -0,0 +1,154 @@ +using System.Diagnostics; +using RGB.NET.Core.Devices; +using RGB.NET.Core.MVVM; + +namespace RGB.NET.Core +{ + /// + /// Represents a single LED of a RGB-device. + /// + [DebuggerDisplay("{Id} {Color}")] + public class Led : AbstractBindable + { + #region Properties & Fields + + /// + /// Gets the this is associated with. + /// + public IRGBDevice Device { get; } + + /// + /// Gets the of the . + /// + public ILedId Id { get; } + + /// + /// Gets a rectangle representing the physical location of the relative to the . + /// + public Rectangle LedRectangle { get; } + + /// + /// Indicates whether the is about to change it's color. + /// + public bool IsDirty => RequestedColor != _color; + + private Color _requestedColor = Color.Transparent; + /// + /// Gets a copy of the the LED should be set to on the next update. + /// + public Color RequestedColor + { + get { return new Color(_requestedColor); } + private set + { + SetProperty(ref _requestedColor, value); + + // ReSharper disable once ExplicitCallerInfoArgument + OnPropertyChanged(nameof(IsDirty)); + } + } + + private Color _color = Color.Transparent; + /// + /// Gets the current of the . Sets the for the next update. + /// + public Color Color + { + get { return _color; } + set + { + if (!IsLocked) + { + RequestedColor.Blend(value); + + // ReSharper disable ExplicitCallerInfoArgument + OnPropertyChanged(nameof(RequestedColor)); + OnPropertyChanged(nameof(IsDirty)); + // ReSharper restore ExplicitCallerInfoArgument + } + } + } + + private bool _isLocked; + /// + /// Gets or sets if the color of this LED can be changed. + /// + public bool IsLocked + { + get { return _isLocked; } + set { SetProperty(ref _isLocked, value); } + } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The the is associated with. + /// The of the . + /// The representing the physical location of the relative to the . + internal Led(IRGBDevice device, ILedId id, Rectangle ledRectangle) + { + this.Device = device; + this.Id = id; + this.LedRectangle = ledRectangle; + } + + #endregion + + #region Methods + + /// + /// Converts the and the of this to a human-readable string. + /// + /// A string that contains the and the of this . For example "Enter [A: 255, R: 255, G: 0, B: 0]". + public override string ToString() + { + return $"{Id} {Color}"; + } + + /// + /// Updates the to the requested . + /// + internal void Update() + { + _color = RequestedColor; + } + + /// + /// Resets the back to default. + /// + internal void Reset() + { + _color = Color.Transparent; + RequestedColor = Color.Transparent; + IsLocked = false; + } + + #endregion + + #region Operators + + /// + /// Converts a to a . + /// + /// The to convert. + public static implicit operator Color(Led led) + { + return led?.Color; + } + + /// + /// Converts a to a . + /// + /// The to convert. + public static implicit operator Rectangle(Led led) + { + return led?.LedRectangle; + } + + #endregion + } +} diff --git a/RGB.NET.Core/MVVM/AbstractBindable.cs b/RGB.NET.Core/MVVM/AbstractBindable.cs new file mode 100644 index 0000000..29291dd --- /dev/null +++ b/RGB.NET.Core/MVVM/AbstractBindable.cs @@ -0,0 +1,66 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace RGB.NET.Core.MVVM +{ + /// + /// Represents a basic bindable class which notifies when a property value changes. + /// + public abstract class AbstractBindable : INotifyPropertyChanged + { + #region Events + + /// + /// Occurs when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Methods + + /// + /// Checks if the property already matches the desirec value or needs to be updated. + /// + /// Type of the property. + /// Reference to the backing-filed. + /// Value to apply. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual bool RequiresUpdate(ref T storage, T value) + { + return !Equals(storage, value); + } + + /// + /// Checks if the property already matches the desired value and updates it if not. + /// + /// Type of the property. + /// Reference to the backing-filed. + /// Value to apply. + /// Name of the property used to notify listeners. This value is optional + /// and can be provided automatically when invoked from compilers that support . + /// true if the value was changed, false if the existing value matched the desired value. + protected virtual bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) + { + if (!RequiresUpdate(ref storage, value)) return false; + + storage = value; + // ReSharper disable once ExplicitCallerInfoArgument + OnPropertyChanged(propertyName); + return true; + } + + /// + /// Triggers the -event when a a property value has changed. + /// + /// Name of the property used to notify listeners. This value is optional + /// and can be provided automatically when invoked from compilers that support . + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + #endregion + } +} diff --git a/RGB.NET.Core/Positioning/Point.cs b/RGB.NET.Core/Positioning/Point.cs new file mode 100644 index 0000000..a099f52 --- /dev/null +++ b/RGB.NET.Core/Positioning/Point.cs @@ -0,0 +1,179 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System.Diagnostics; +using RGB.NET.Core.Extensions; +using RGB.NET.Core.MVVM; + +namespace RGB.NET.Core +{ + /// + /// Represents a point consisting of a X- and a Y-position. + /// + [DebuggerDisplay("[X: {X}, Y: {Y}]")] + public class Point : AbstractBindable + { + #region Properties & Fields + + private double _x; + /// + /// Gets or sets the X-position of this . + /// + public double X + { + get { return _x; } + set { SetProperty(ref _x, value); } + } + + private double _y; + /// + /// Gets or sets the Y-position of this . + /// + public double Y + { + get { return _y; } + set { SetProperty(ref _y, value); } + } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public Point() + { } + + /// + /// Initializes a new instance of the class using the provided values. + /// + /// The value used for the X-position. + /// The value used for the Y-position. + public Point(double x, double y) + { + this.X = x; + this.Y = y; + } + + #endregion + + #region Methods + + /// + /// Converts the - and -position of this to a human-readable string. + /// + /// A string that contains the and of this . For example "[X: 100, Y: 20]". + public override string ToString() + { + return $"[X: {X}, Y: {Y}]"; + } + + /// + /// Tests whether the specified object is a and is equivalent to this . + /// + /// The object to test. + /// true if is a equivalent to this ; otherwise, false. + public override bool Equals(object obj) + { + Point compareColor = obj as Point; + if (ReferenceEquals(compareColor, null)) + return false; + + if (ReferenceEquals(this, compareColor)) + return true; + + if (GetType() != compareColor.GetType()) + return false; + + return X.EqualsInTolerance(compareColor.X) && Y.EqualsInTolerance(compareColor.Y); + } + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() + { + unchecked + { + int hashCode = X.GetHashCode(); + hashCode = (hashCode * 397) ^ Y.GetHashCode(); + return hashCode; + } + } + + #endregion + + #region Operators + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are equal; otherwise, false. + public static bool operator ==(Point point1, Point point2) + { + return ReferenceEquals(point1, null) ? ReferenceEquals(point2, null) : point1.Equals(point2); + } + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are not equal; otherwise, false. + public static bool operator !=(Point point1, Point point2) + { + return !(point1 == point2); + } + + /// + /// Returns a new representing the addition of the two provided . + /// + /// The first . + /// The second . + /// A new representing the addition of the two provided . + public static Point operator +(Point point1, Point point2) + { + return new Point(point1.X + point2.X, point1.Y + point2.Y); + } + + /// + /// Returns a new representing the substraction of the two provided . + /// + /// The first . + /// The second . + /// A new representing the substraction of the two provided . + public static Point operator -(Point point1, Point point2) + { + return new Point(point1.X - point2.X, point1.Y - point2.Y); + } + + /// + /// Returns a new representing the multiplication of the two provided . + /// + /// The first . + /// The second . + /// A new representing the multiplication of the two provided . + public static Point operator *(Point point1, Point point2) + { + return new Point(point1.X * point2.X, point1.Y * point2.Y); + } + + /// + /// Returns a new representing the division of the two provided . + /// + /// The first . + /// The second . + /// A new representing the division of the two provided . + public static Point operator /(Point point1, Point point2) + { + if (point2.X.EqualsInTolerance(0) || point2.Y.EqualsInTolerance(0)) return new Point(); + return new Point(point1.X / point2.X, point1.Y / point2.Y); + } + + #endregion + } +} diff --git a/RGB.NET.Core/Positioning/Rectangle.cs b/RGB.NET.Core/Positioning/Rectangle.cs new file mode 100644 index 0000000..92bb85f --- /dev/null +++ b/RGB.NET.Core/Positioning/Rectangle.cs @@ -0,0 +1,208 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using RGB.NET.Core.Extensions; +using RGB.NET.Core.MVVM; + +namespace RGB.NET.Core +{ + /// + /// Represents a rectangle defined by it's position and it's size. + /// + [DebuggerDisplay("[Location: {Location}, Size: {Size}]")] + public class Rectangle : AbstractBindable + { + #region Properties & Fields + + private Point _location; + /// + /// Gets or sets the representing the top-left corner of the . + /// + public Point Location + { + get { return _location; } + set + { + if (SetProperty(ref _location, value)) + // ReSharper disable once ExplicitCallerInfoArgument + OnPropertyChanged(nameof(Center)); + } + } + + private Size _size; + /// + /// Gets or sets the of the . + /// + public Size Size + { + get { return _size; } + set + { + if (SetProperty(ref _size, value)) + // ReSharper disable once ExplicitCallerInfoArgument + OnPropertyChanged(nameof(Center)); + } + } + + /// + /// Gets a new representing the center-point of the . + /// + public Point Center => new Point(Location.X + (Size.Width / 2.0), Location.Y + (Size.Height / 2.0)); + + /// + /// Gets a bool indicating if both, the width and the height of the rectangle is greater than zero. + /// + public bool IsEmpty => (Size.Width > DoubleExtensions.TOLERANCE) && (Size.Height > DoubleExtensions.TOLERANCE); + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public Rectangle() + : this(new Point(), new Size()) + { } + + /// + /// Initializes a new instance of the class using the provided values for ans . + /// + /// The -position of this . + /// The -position of this . + /// The of this . + /// The of this . + public Rectangle(double x, double y, double width, double height) + : this(new Point(x, y), new Size(width, height)) + { } + + /// + /// Initializes a new instance of the class using the given and . + /// + /// + /// + public Rectangle(Point location, Size size) + { + this.Location = location; + this.Size = size; + } + + /// + /// Initializes a new instance of the class using the given array of . + /// The and is calculated to completely contain all rectangles provided as parameters. + /// + /// The array of used to calculate the and + public Rectangle(params Rectangle[] rectangles) + : this(rectangles.AsEnumerable()) + { } + + /// + /// Initializes a new instance of the class using the given list of . + /// The and is calculated to completely contain all rectangles provided as parameters. + /// + /// The list of used to calculate the and + public Rectangle(IEnumerable rectangles) + { + double posX = double.MaxValue; + double posY = double.MaxValue; + double posX2 = double.MinValue; + double posY2 = double.MinValue; + + foreach (Rectangle rectangle in rectangles) + { + 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); + } + + InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)); + } + + /// + /// Initializes a new instance of the class using the given array of . + /// The and is calculated to contain all points provided as parameters. + /// + /// The array of used to calculate the and + public Rectangle(params Point[] points) + : this(points.AsEnumerable()) + { } + + /// + /// Initializes a new instance of the class using the given list of . + /// The and is calculated to contain all points provided as parameters. + /// + /// The list of used to calculate the and + public Rectangle(IEnumerable points) + : this() + { + double posX = double.MaxValue; + double posY = double.MaxValue; + double posX2 = double.MinValue; + double posY2 = double.MinValue; + + foreach (Point point in points) + { + posX = Math.Min(posX, point.X); + posY = Math.Min(posY, point.Y); + posX2 = Math.Max(posX2, point.X); + posY2 = Math.Max(posY2, point.Y); + } + + InitializeFromPoints(new Point(posX, posY), new Point(posX2, posY2)); + } + + #endregion + + #region Methods + + private void 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; + + Location = new Point(posX, posY); + Size = new Size(width, height); + } + + /// + /// Calculates the percentage of intersection of a rectangle. + /// + /// The intersecting rectangle. + /// The percentage of intersection. + public double CalculateIntersectPercentage(Rectangle intersectingRect) + { + if (IsEmpty || intersectingRect.IsEmpty) return 0; + + Rectangle intersection = CalculateIntersection(intersectingRect); + return intersection.IsEmpty ? 0 : (intersection.Size.Width * intersection.Size.Height) / (Size.Width * Size.Height); + } + + /// + /// Calculates the representing the intersection of this and the one provided as parameter. + /// + /// The intersecting + /// A new representing the intersection this and the one provided as parameter. + public Rectangle CalculateIntersection(Rectangle intersectingRectangle) + { + double x1 = Math.Max(Location.X, intersectingRectangle.Location.X); + double x2 = Math.Min(Location.X + Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width); + + double y1 = Math.Max(Location.Y, intersectingRectangle.Location.Y); + double y2 = Math.Min(Location.Y + 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(); + } + + #endregion + } +} diff --git a/RGB.NET.Core/Positioning/Size.cs b/RGB.NET.Core/Positioning/Size.cs new file mode 100644 index 0000000..f3aa936 --- /dev/null +++ b/RGB.NET.Core/Positioning/Size.cs @@ -0,0 +1,187 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System.Diagnostics; +using RGB.NET.Core.Extensions; +using RGB.NET.Core.MVVM; + +namespace RGB.NET.Core +{ + /// + /// Represents a size consisting of a width and a height. + /// + [DebuggerDisplay("[Width: {Width}, Height: {Height}]")] + public class Size : AbstractBindable + { + #region Properties & Fields + + private double _width; + /// + /// Gets or sets the width component value of this . + /// + public double Width + { + get { return _width; } + set { SetProperty(ref _width, value); } + } + + private double _height; + /// + /// Gets or sets the height component value of this . + /// + public double Height + { + get { return _height; } + set { SetProperty(ref _height, value); } + } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public Size() + { } + + /// + /// Initializes a new instance of the using the provided size to define a square. + /// + /// The size used for the component value and the component value. + public Size(double size) + : this(size, size) + { } + + /// + /// Initializes a new instance of the class using the provided values. + /// + /// The size used for the component value. + /// The size used for the component value. + public Size(double width, double height) + { + this.Width = width; + this.Height = height; + } + + #endregion + + #region Methods + + /// + /// Converts the and of this to a human-readable string. + /// + /// A string that contains the and of this . For example "[Width: 100, Height: 20]". + public override string ToString() + { + return $"[Width: {Width}, Height: {Height}]"; + } + + /// + /// Tests whether the specified object is a and is equivalent to this . + /// + /// The object to test. + /// true if is a equivalent to this ; otherwise, false. + public override bool Equals(object obj) + { + Size compareColor = obj as Size; + if (ReferenceEquals(compareColor, null)) + return false; + + if (ReferenceEquals(this, compareColor)) + return true; + + if (GetType() != compareColor.GetType()) + return false; + + return Width.EqualsInTolerance(compareColor.Width) && Height.EqualsInTolerance(compareColor.Height); + } + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() + { + unchecked + { + int hashCode = Width.GetHashCode(); + hashCode = (hashCode * 397) ^ Height.GetHashCode(); + return hashCode; + } + } + + #endregion + + #region Operators + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are equal; otherwise, false. + public static bool operator ==(Size size1, Size size2) + { + return ReferenceEquals(size1, null) ? ReferenceEquals(size2, null) : size1.Equals(size2); + } + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are not equal; otherwise, false. + public static bool operator !=(Size size1, Size size2) + { + return !(size1 == size2); + } + + /// + /// Returns a new representing the addition of the two provided . + /// + /// The first . + /// The second . + /// A new representing the addition of the two provided . + public static Size operator +(Size size1, Size size2) + { + return new Size(size1.Width + size2.Width, size1.Height + size2.Height); + } + + /// + /// Returns a new representing the substraction of the two provided . + /// + /// The first . + /// The second . + /// A new representing the substraction of the two provided . + public static Size operator -(Size size1, Size size2) + { + return new Size(size1.Width - size2.Width, size1.Height - size2.Height); + } + + /// + /// Returns a new representing the multiplication of the two provided . + /// + /// The first . + /// The second . + /// A new representing the multiplication of the two provided . + public static Size operator *(Size size1, Size size2) + { + return new Size(size1.Width * size2.Width, size1.Height * size2.Height); + } + + /// + /// Returns a new representing the division of the two provided . + /// + /// The first . + /// The second . + /// A new representing the division of the two provided . + public static Size operator /(Size size1, Size size2) + { + if (size2.Width.EqualsInTolerance(0) || size2.Height.EqualsInTolerance(0)) return new Size(); + return new Size(size1.Width / size2.Width, size1.Height / size2.Height); + } + + #endregion + } +} diff --git a/RGB.NET.Core/Properties/Annotations.cs b/RGB.NET.Core/Properties/Annotations.cs new file mode 100644 index 0000000..9ebddb9 --- /dev/null +++ b/RGB.NET.Core/Properties/Annotations.cs @@ -0,0 +1,1048 @@ +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace RGB.NET.Core.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as format-string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; private set; } + } + + /// + /// For a parameter that is expected to be one of the limited set of values. + /// Specify fields of which type should be used as values for this parameter. + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; private set; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, it's name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for method output + /// means that the methos doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by R# analysis.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // regular assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; private set; } + + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that marked element should be localized or not. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; private set; } + + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes + /// as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; private set; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Waring: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of method invocation must be used. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; private set; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + public sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; private set; } + } + + /// + /// An extension method marked with this attribute is processed by ReSharper code completion + /// as a 'Source Template'. When extension method is completed over some expression, it's source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// > + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation or property access + /// over collection type affects content of the collection. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; private set; } + } + + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; private set; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable, passed as parameter, is not enumerated. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that parameter is regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + public sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; private set; } + + [NotNull] public Type ControlType { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; private set; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; private set; } + + [NotNull] public string FieldName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; private set; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } +} \ No newline at end of file diff --git a/RGB.NET.Core/RGB.NET.Core.csproj b/RGB.NET.Core/RGB.NET.Core.csproj index 6b9eafb..7511fa3 100644 --- a/RGB.NET.Core/RGB.NET.Core.csproj +++ b/RGB.NET.Core/RGB.NET.Core.csproj @@ -42,8 +42,18 @@ + + + + + + + + + +